Comparing version 0.2.2 to 0.3.0
exports.Packr = require('./pack').Packr | ||
exports.Encoder = exports.Packr | ||
let unpackModule = require('./unpack') | ||
@@ -7,7 +8,12 @@ let extractor = tryRequire('msgpackr-extract') | ||
exports.Unpackr = unpackModule.Unpackr | ||
exports.Decoder = exports.Unpackr | ||
exports.PackrStream = require('./stream').PackrStream | ||
exports.UnpackrStream = require('./stream').UnpackrStream | ||
let packr = new exports.Packr({ objectsAsMaps: true }) | ||
exports.EncoderStream = exports.PackrStream | ||
exports.DecoderStream = exports.UnpackrStream | ||
let packr = new exports.Packr({ useRecords: false }) | ||
exports.unpack = packr.unpack | ||
exports.pack = packr.pack | ||
exports.decode = packr.unpack | ||
exports.encode = packr.pack | ||
@@ -14,0 +20,0 @@ function tryRequire(moduleId) { |
23
pack.js
@@ -332,3 +332,23 @@ "use strict" | ||
const writeObject = this.objectsAsMaps ? (object, safePrototype) => { | ||
const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => { | ||
let keys = Object.keys(object) | ||
let length = keys.length | ||
if (length < 0x10) { | ||
target[position++] = 0x80 | length | ||
} else if (length < 0x10000) { | ||
target[position++] = 0xde | ||
target[position++] = length >> 8 | ||
target[position++] = length & 0xff | ||
} else { | ||
target[position++] = 0xdf | ||
targetView.setUint32(position, length) | ||
position += 4 | ||
} | ||
let key | ||
for (let i = 0; i < length; i++) { | ||
pack(key = keys[i]) | ||
pack(object[key]) | ||
} | ||
} : | ||
(object, safePrototype) => { | ||
target[position++] = 0xde // always use map 16, so we can preallocate and set the length afterwards | ||
@@ -348,2 +368,3 @@ let objectOffset = position - start | ||
} : | ||
/* sharedStructures ? // For highly stable structures, using for-in can a little bit faster | ||
@@ -350,0 +371,0 @@ (object, safePrototype) => { |
{ | ||
"name": "msgpackr", | ||
"author": "Kris Zyp", | ||
"version": "0.2.2", | ||
"version": "0.3.0", | ||
"description": "Fast MessagePack implementation with extension for record structures", | ||
@@ -15,3 +15,3 @@ "license": "MIT", | ||
"scripts": { | ||
"benchmark": "./tests/benchmark.js", | ||
"benchmark": "node ./tests/benchmark.js", | ||
"test": "./node_modules/.bin/mocha tests/test*.js -u tdd" | ||
@@ -21,3 +21,3 @@ }, | ||
"optionalDependencies": { | ||
"msgpackr-extract": "^0.1.1" | ||
"msgpackr-extract": "^0.2.0" | ||
}, | ||
@@ -24,0 +24,0 @@ "devDependencies": { |
@@ -47,2 +47,5 @@ # msgpackr | ||
## Alternate Terminology | ||
If you prefer to use encoder/decode terminology, msgpackr exports aliases, so `decode` is equivalent to `unpack`, `encode` is `pack`, `Encoder` is `Packr`, `Decoder` is `Unpackr`, and `EncoderStream` and `DecoderStream` can be used as well. | ||
## Browser Usage | ||
@@ -53,6 +56,6 @@ Msgpackr works as standalone JavaScript as well, and runs on modern browsers. It includes a bundled script for ease of direct loading. For module-based development, it is recommended that you directly import the module of interest, to minimize dependencies that get pulled into your application: | ||
``` | ||
(It is worth noting that while msgpackr works well in browsers, the MessagePack format itself is usually not an ideal format for web use. If you want compact data, brotli or gzip are most effective in compressing, and MessagePack's character frequency tends to defeat Huffman encoding used by these standard compression algorithms, resulting in less compact data than compressed JSON. The modern browser architecture is heavily optimized for parsing JSON from HTTP traffic, and it is difficult to achieve the same level of overall efficiency and ease with MessagePack.) | ||
(It is worth noting that while msgpackr works well in modern browsers, the MessagePack format itself is usually not an ideal format for web use. If you want compact data, brotli or gzip are most effective in compressing, and MessagePack's character frequency tends to defeat Huffman encoding used by these standard compression algorithms, resulting in less compact data than compressed JSON. The modern browser architecture is heavily optimized for parsing JSON from HTTP traffic, and it is difficult to achieve the same level of overall efficiency and ease with MessagePack.) | ||
## Record / Object Structures | ||
There is a critical difference between maps (or dictionaries) that hold an arbitrary set of keys and values (JavaScript `Map` is designed for these), and records or object structures that have a well-defined set of fields which may have many instances using that same structure (most objects in JS). By using the record extension, this distinction is preserved in MessagePack and the encoding can reuse structures and not only provides better type preservation, but yield much more compact encodings and increase parsing/deserialization performance by 2-3x. Msgpackr automatically generates record definitions that are reused and referenced by objects with the same structure. There are a number of ways to use this to our advantage. For large object structures with repeating nested objects with similar structures, simply serializing with the record extension can yield benefits. To use the record structures extension, we create a new `Packr` instance. By default a new `Packr` instance will have the record extension enabled: | ||
There is a critical difference between maps (or dictionaries) that hold an arbitrary set of keys and values (JavaScript `Map` is designed for these), and records or object structures that have a well-defined set of fields which may have many instances using that same structure (most objects in JS). By using the record extension, this distinction is preserved in MessagePack and the encoding can reuse structures and not only provides better type preservation, but yield much more compact encodings and increase parsing/deserialization performance by 2-3x. Msgpackr automatically generates record definitions that are reused and referenced by objects with the same structure. There are a number of ways to use this to our advantage. For large object structures with repeating nested objects with similar structures, simply serializing with the record extension can yield significant benefits. To use the record structures extension, we create a new `Packr` instance. By default a new `Packr` instance will have the record extension enabled: | ||
``` | ||
@@ -67,3 +70,3 @@ import { Packr } from 'msgpackr'; | ||
When creating a new `Packr`, `PackrStream`, or `UnpackrStream` instance, we can enable or disable the record structure extension with the `objectsAsMaps` property. When this is `true`, the record structure extension will be disabled, and all objects will revert to being serialized using MessageMap `map`s, and all `map`s will be deserialized to JS `Object`s as properties (like the standalone `pack` and `unpack` functions). | ||
When creating a new `Packr`, `PackrStream`, or `UnpackrStream` instance, we can enable or disable the record structure extension with the `useRecords` property. When this is `false`, the record structure extension will be disabled (standard/compatibility mode), and all objects will revert to being serialized using MessageMap `map`s, and all `map`s will be deserialized to JS `Object`s as properties (like the standalone `pack` and `unpack` functions). | ||
@@ -90,4 +93,6 @@ ### Shared Record Structures | ||
The following options properties can be provided to the Packr or Unpackr constructor: | ||
* `objectsAsMaps` - Turning this on disables the record extension and stores JavaScript objects as MessagePack maps, and unpacks maps as JavaScript `Object`s. | ||
* `useRecords` - Setting this to `false` disables the record extension and stores JavaScript objects as MessagePack maps, and unpacks maps as JavaScript `Object`s, which ensures compatibilty with other decoders. | ||
* `structures` - Provides the array of structures that is to be used for record extension, if you want the structures saved and used again. | ||
* `mapsAsObjects` - If `true`, this will decode MessagePack maps and JS `Object`s with the map entries decoded to object properties. If `false`, maps are decoded as JavaScript `Map`s. This is disabled by default if `useRecords` is enabled (which allows `Map`s to be preserved), and is enabled by default if `useRecords` is disabled. | ||
* `variableMapSize` - This will use varying map size definition (fixmap, map16, map32) based on the number of keys when encoding data, which yields slightly more compact encodings (for small objects), but is typically 5-10% slower during encoding. | ||
@@ -94,0 +99,0 @@ ## Performance |
@@ -20,3 +20,3 @@ var msgpackr = tryRequire(".."); | ||
var pkg = require("../package.json"); | ||
var data = require("./example4.json"); | ||
var data = require("./example.json"); | ||
var packed = msgpack_lite && msgpack_lite.encode(data); | ||
@@ -23,0 +23,0 @@ var expected = JSON.stringify(data); |
@@ -33,3 +33,3 @@ //var inspector = require('inspector') | ||
var fs = require('fs') | ||
var sampleData = JSON.parse(fs.readFileSync(__dirname + '/example4.json')) | ||
var sampleData = JSON.parse(fs.readFileSync(__dirname + '/example.json')) | ||
} else { | ||
@@ -98,3 +98,3 @@ var xhr = new XMLHttpRequest() | ||
let structures = [] | ||
let packr = new Packr({ structures, objectsAsMaps: false }) | ||
let packr = new Packr({ structures, useRecords: true }) | ||
var serialized = packr.pack(data) | ||
@@ -235,3 +235,3 @@ var deserialized = packr.unpack(serialized) | ||
let structures = [] | ||
let packr = new Packr({ structures, objectsAsMaps: false }) | ||
let packr = new Packr({ structures, useRecords: true }) | ||
var serialized = packr.pack(data) | ||
@@ -247,3 +247,3 @@ console.log('msgpackr size', serialized.length) | ||
let structures = [] | ||
let packr = new Packr({ structures, objectsAsMaps: false }) | ||
let packr = new Packr({ structures, useRecords: true }) | ||
@@ -250,0 +250,0 @@ for (var i = 0; i < ITERATIONS; i++) { |
106
unpack.js
@@ -20,3 +20,6 @@ "use strict" | ||
let dataView | ||
let defaultOptions = { objectsAsMaps: true } | ||
let defaultOptions = { | ||
useRecords: false, | ||
mapsAsObjects: true | ||
} | ||
// the registration of the record definition extension (as "r") | ||
@@ -38,2 +41,4 @@ const recordDefinition = currentExtensions[0x72] = (id) => { | ||
constructor(options) { | ||
if (options && options.useRecords === false && options.mapsAsObjects === undefined) | ||
options.mapsAsObjects = true | ||
Object.assign(this, options) | ||
@@ -111,3 +116,3 @@ } | ||
token -= 0x80 | ||
if (currentUnpackr.objectsAsMaps) { | ||
if (currentUnpackr.mapsAsObjects) { | ||
let object = {} | ||
@@ -139,5 +144,5 @@ for (let i = 0; i < token; i++) { | ||
} | ||
if (srcStringEnd == 0 && length < 8 && srcEnd < 128) { | ||
if (srcStringEnd == 0 && srcEnd < 120 && length < 16) { | ||
// for small blocks, avoiding the overhead of the extract call is helpful | ||
let string = simpleString(length) | ||
let string = /*length < 16 ? */shortStringInJS(length)// : longStringInJS(length) | ||
if (string != null) | ||
@@ -422,3 +427,3 @@ return string | ||
function readMap(length) { | ||
if (currentUnpackr.objectsAsMaps) { | ||
if (currentUnpackr.mapsAsObjects) { | ||
let object = {} | ||
@@ -439,14 +444,16 @@ for (let i = 0; i < length; i++) { | ||
let fromCharCode = String.fromCharCode | ||
/*function simpleString(length) { | ||
function longStringInJS(length) { | ||
let start = position | ||
let bytes = new Array(length) | ||
for (let i = 0; i < length; i++) { | ||
const byte = src[position++]; | ||
if ((byte & 0x80) > 0) { | ||
position -= i + 1 | ||
position = start | ||
return | ||
} | ||
bytes[i] = byte | ||
} | ||
return asString.slice(start, position) | ||
}*/ | ||
function simpleString(length) { | ||
return fromCharCode.apply(String, bytes) | ||
} | ||
function shortStringInJS(length) { | ||
if (length < 4) { | ||
@@ -494,3 +501,3 @@ if (length < 2) { | ||
let e = src[position++] | ||
if ((e & 0x80) > 1) { | ||
if ((e & 0x80) > 0) { | ||
position -= 5 | ||
@@ -501,3 +508,3 @@ return | ||
} | ||
} else { | ||
} else if (length < 8) { | ||
let e = src[position++] | ||
@@ -513,8 +520,79 @@ let f = src[position++] | ||
if ((g & 0x80) > 0) { | ||
position -= 3 | ||
position -= 7 | ||
return | ||
} | ||
return fromCharCode(a, b, c, d, e, f, g) | ||
} else { | ||
let e = src[position++] | ||
let f = src[position++] | ||
let g = src[position++] | ||
let h = src[position++] | ||
if ((e & 0x80) > 0 || (f & 0x80) > 0 || (g & 0x80) > 0 || (h & 0x80) > 0) { | ||
position -= 8 | ||
return | ||
} | ||
if (length < 10) { | ||
if (length === 8) | ||
return fromCharCode(a, b, c, d, e, f, g, h) | ||
else { | ||
let i = src[position++] | ||
if ((i & 0x80) > 0) { | ||
position -= 9 | ||
return | ||
} | ||
return fromCharCode(a, b, c, d, e, f, g, h, i) | ||
} | ||
} else if (length < 12) { | ||
let i = src[position++] | ||
let j = src[position++] | ||
if ((i & 0x80) > 0 || (j & 0x80) > 0) { | ||
position -= 10 | ||
return | ||
} | ||
if (length < 11) | ||
return fromCharCode(a, b, c, d, e, f, g, h, i, j) | ||
let k = src[position++] | ||
if ((k & 0x80) > 0) { | ||
position -= 11 | ||
return | ||
} | ||
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k) | ||
} else { | ||
let i = src[position++] | ||
let j = src[position++] | ||
let k = src[position++] | ||
let l = src[position++] | ||
if ((i & 0x80) > 0 || (j & 0x80) > 0 || (k & 0x80) > 0 || (l & 0x80) > 0) { | ||
position -= 12 | ||
return | ||
} | ||
if (length < 14) { | ||
if (length === 12) | ||
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l) | ||
else { | ||
let m = src[position++] | ||
if ((m & 0x80) > 0) { | ||
position -= 13 | ||
return | ||
} | ||
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m) | ||
} | ||
} else { | ||
let m = src[position++] | ||
let n = src[position++] | ||
if ((m & 0x80) > 0 || (n & 0x80) > 0) { | ||
position -= 14 | ||
return | ||
} | ||
if (length < 15) | ||
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n) | ||
let o = src[position++] | ||
if ((o & 0x80) > 0) { | ||
position -= 15 | ||
return | ||
} | ||
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) | ||
} | ||
} | ||
} | ||
} | ||
@@ -521,0 +599,0 @@ } |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
182485
22
3294
177