Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
language: node_js
sudo: false
node_js:
- "5"
- "6"
- "8"
- "10"
- "node"
addons:
apt:
Expand Down
80 changes: 72 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# tiny-msgpack [![Build Status](https://travis-ci.org/JoshuaWise/tiny-msgpack.svg?branch=master)](https://travis-ci.org/JoshuaWise/tiny-msgpack)
# msgpack-typed-numbers [![Build Status](https://travis-ci.org/mattbishop/msgpack-typed-numbers.svg?branch=master)](https://travis-ci.org/mattbishop/msgpack-typed-numbers)

A minimalistic [MessagePack](http://msgpack.org/index.html) encoder and decoder for JavaScript.
A minimalistic [MessagePack](http://msgpack.org/index.html) encoder and decoder for JavaScript. Supports typed numbers for 64-bit longs and consistent floating point values. Based on Joshua Wise's excellent [tiny-msgpack](https://github.com/JoshuaWise/tiny-msgpack) project.

- Tiny Size (2.63 kB minified and gzipped)
- Fast performance (Slightly faster than [msgpack-lite](https://github.com/kawanet/msgpack-lite/))
- Supports true 64-bit longs and consistent floating-point values
- Extension support
- No other bells or whistles

Expand All @@ -12,22 +13,85 @@ By default, `msgpack` can encode numbers, strings, booleans, nulls, arrays, obje
## Installation

```bash
npm install --save tiny-msgpack
npm install --save msgpack-typed-numbers
```

## Usage

```js
var msgpack = require('tiny-msgpack');
var msgpack = require('msgpack-typed-numbers');

var uint8array = msgpack.encode({foo: 'bar', baz: 123});
var object = msgpack.decode(uint8array);
```

## Typed Numbers

Javascript numbers reqpresent both integer and floating point values. This poses two problems for msgpack:

1. integer values > 53 bits cannot be represented
2. floating point values with whole numbers (ie. 1.0) are indistinguishable from integers, so msgpack treats them as integers

When one is using msgpack to send and receive data with other systems that do not have this limitation, like Python or Java, these limitations can ruin the data being transmitted. `msgpack-with-numbers` provides a way to specify Float and Long values to encode and decode numeric data correctly.

### Floating-Point Numbers

To used typed Float values, wrap the value in a `msgpack.Float` class.

```js
const msgpack = require('msgpack-typed-numbers');

// NOTE: integer number input
const float1 = new msgpack.Float(100);
const float2 = new msgpack.Float(1.1);

const uint8array = msgpack.encode({
floatField1: float1,
floatField2: float2
});

// float1 is encoed as a 32-bit float
// float2 is encoded as a 64-bit float
const obj = msgpack.decode(uint8array);
console.log(obj);
/* =>
{ floatField1: 100, floatField2: 1.1 }
*/
```

### Long Numbers

To use Long values, create a Long value with the [Long](https://www.npmjs.com/package/long) library.

```js
const msgpack = require('msgpack-typed-numbers');
const Long = require('long');

// max 64-bit number
const long1 = Long.fromBits(-1, 0x7fffffff);
const long2 = Long.fromNumber(-100);

const uint8array = msgpack.encode({
longField1: long1,
longField2: long2,
});

// long1 is encoded as a 64-bit uint
// long2 is encoded as an 8-bit int
var obj = msgpack.decode(uint8array);
console.log(obj);
/* =>
{ longField1: Long { low: -1, high: 2147483647, unsigned: false },
longField2: -100 }
*/
```

The use of Long and Float are optional. One can still pass primitive JS numbers and they will be treated as variant-type numeric data.

## Extensions

```js
var msgpack = require('tiny-msgpack');
var msgpack = require('msgpack-typed-numbers');
var codec = new msgpack.Codec;

function encodeDate(date) {
Expand All @@ -45,7 +109,7 @@ console.log(object.timestamp instanceof Date); // => true

## Browser Support

In the browser, `tiny-msgpack` requires the [Encoding API](https://developer.mozilla.org/en-US/docs/Web/API/Encoding_API), which is currently only implemented by Chrome and Firefox. However, if you polyfill it, this package is supported by the following browsers:
In the browser, `msgpack-typed-numbers` requires the [Encoding API](https://developer.mozilla.org/en-US/docs/Web/API/Encoding_API), which is currently only implemented by Chrome and Firefox. However, if you polyfill it, this package is supported by the following browsers:

- Chrome 9+
- Firefox 15+
Expand All @@ -55,8 +119,8 @@ In the browser, `tiny-msgpack` requires the [Encoding API](https://developer.moz

## Zero copy

In the [MessagePack](http://msgpack.org/index.html) format, binary data is encoded as... binary data! To maximize performance, `tiny-msgpack` does not copy binary data when encoding or decoding it. So after decoding, the contents of a returned `Uint8Array` can be affected by modifying the input `Uint8Array` (the same can happen with encoding).
In the [MessagePack](http://msgpack.org/index.html) format, binary data is encoded as... binary data! To maximize performance, `msgpack-typed-numbers` does not copy binary data when encoding or decoding it. So after decoding, the contents of a returned `Uint8Array` can be affected by modifying the input `Uint8Array` (the same can happen with encoding).

## License

[MIT](https://github.com/JoshuaWise/tiny-msgpack/blob/master/LICENSE)
[MIT](https://github.com/mattbishop/tiny-msgpack/blob/master/LICENSE)
44 changes: 22 additions & 22 deletions lib/decode.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,98 +16,98 @@ var readToken = new Array(256);
// interpretation of the bytes at the given Paper's offset.
(function (read) {
var i;

// Creates a readToken function that returns a constant value.
var constant = function (value) {
return function () {
return value;
};
};

// Transforms the given method to always receive a second argument, which is
// a number constant.
var fix = function (len, method) {
return function (decoder) {
return method(decoder, len);
};
};

// Transforms the given method to always receive a second argument, which is
// a number returned by lenFunc when given a Paper.
var flex = function (lenFunc, method) {
return function (decoder) {
return method(decoder, lenFunc(decoder));
};
};

// positive fixint -- 0x00 - 0x7f
for (i=0x00; i<=0x7f; ++i) {
readToken[i] = constant(i);
}

// fixmap -- 0x80 - 0x8f
for (i=0x80; i<=0x8f; ++i) {
readToken[i] = fix(i - 0x80, read.map);
}

// fixarray -- 0x90 - 0x9f
for (i=0x90; i<=0x9f; ++i) {
readToken[i] = fix(i - 0x90, read.array);
}

// fixstr -- 0xa0 - 0xbf
for (i=0xa0; i<=0xbf; ++i) {
readToken[i] = fix(i - 0xa0, read.str);
}

// nil -- 0xc0
readToken[0xc0] = constant(null);

// (never used) -- 0xc1
readToken[0xc1] = null;

// false -- 0xc2
// true -- 0xc3
readToken[0xc2] = constant(false);
readToken[0xc3] = constant(true);

// bin 8 -- 0xc4
// bin 16 -- 0xc5
// bin 32 -- 0xc6
readToken[0xc4] = flex(read.uint8, read.bin);
readToken[0xc5] = flex(read.uint16, read.bin);
readToken[0xc6] = flex(read.uint32, read.bin);

// ext 8 -- 0xc7
// ext 16 -- 0xc8
// ext 32 -- 0xc9
readToken[0xc7] = flex(read.uint8, read.ext);
readToken[0xc8] = flex(read.uint16, read.ext);
readToken[0xc9] = flex(read.uint32, read.ext);

// float 32 -- 0xca
// float 64 -- 0xcb
readToken[0xca] = read.float32;
readToken[0xcb] = read.float64;

// uint 8 -- 0xcc
// uint 16 -- 0xcd
// uint 32 -- 0xce
// uint 64 -- 0xcf
readToken[0xcc] = read.uint8;
readToken[0xcd] = read.uint16;
readToken[0xce] = read.uint32;
readToken[0xcf] = null;
readToken[0xcf] = read.uint64;

// int 8 -- 0xd0
// int 16 -- 0xd1
// int 32 -- 0xd2
// int 64 -- 0xd3
readToken[0xd0] = read.int8;
readToken[0xd1] = read.int16;
readToken[0xd2] = read.int32;
readToken[0xd3] = null;
readToken[0xd3] = read.int64;

// fixext 1 -- 0xd4
// fixext 2 -- 0xd5
// fixext 4 -- 0xd6
Expand All @@ -118,24 +118,24 @@ var readToken = new Array(256);
readToken[0xd6] = fix(4, read.ext);
readToken[0xd7] = fix(8, read.ext);
readToken[0xd8] = fix(16, read.ext);

// str 8 -- 0xd9
// str 16 -- 0xda
// str 32 -- 0xdb
readToken[0xd9] = flex(read.uint8, read.str);
readToken[0xda] = flex(read.uint16, read.str);
readToken[0xdb] = flex(read.uint32, read.str);

// array 16 -- 0xdc
// array 32 -- 0xdd
readToken[0xdc] = flex(read.uint16, read.array);
readToken[0xdd] = flex(read.uint32, read.array);

// map 16 -- 0xde
// map 32 -- 0xdf
readToken[0xde] = flex(read.uint16, read.map);
readToken[0xdf] = flex(read.uint32, read.map);

// negative fixint -- 0xe0 - 0xff
for (i=0xe0; i<=0xff; ++i) {
readToken[i] = constant(i - 0x100);
Expand Down
Loading