msgpackr 1.11.8 → 1.11.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,8 +7,6 @@
7
7
  [![module](https://img.shields.io/badge/module-ESM%2FCJS-blue)](README.md)
8
8
  [![license](https://img.shields.io/badge/license-MIT-brightgreen)](LICENSE)
9
9
 
10
- <img align="right" src="./assets/performance.png" width="380"/>
11
-
12
10
  The msgpackr package is an extremely fast MessagePack NodeJS/JavaScript implementation. Currently, it is significantly faster than any other known implementations, faster than Avro (for JS), and generally faster than native V8 JSON.stringify/parse, on NodeJS. It also includes an optional record extension (the `r` in msgpackr), for defining record structures that makes MessagePack even faster and more compact, often over twice as fast as even native JSON functions, several times faster than other JS implementations, and 15-50% more compact. See the performance section for more details. Structured cloning (with support for cyclical references) is also supported through optional extensions.
13
11
 
14
12
  ## Basic Usage
package/benchmark.md CHANGED
@@ -1,24 +1,34 @@
1
1
  Here are more comprehensive benchmarks. This is comparison with the next fastest JS projects using the benchmark tool from `msgpack-lite` (and data is from some clinical research data we use that has a good mix of different value types and structures). It also includes comparison to V8 native JSON functionality, and JavaScript Avro (`avsc`, a very optimized Avro implementation):
2
2
 
3
- operation | op | ms | op/s
4
- ---------------------------------------------------------- | ------: | ----: | -----:
5
- buf = Buffer(JSON.stringify(obj)); | 82000 | 5004 | 16386
6
- obj = JSON.parse(buf); | 88600 | 5000 | 17720
7
- require("msgpackr").pack(obj); | 161500 | 5002 | 32287
8
- require("msgpackr").unpack(buf); | 94600 | 5004 | 18904
9
- msgpackr w/ shared structures: packr.pack(obj); | 178400 | 5002 | 35665
10
- msgpackr w/ shared structures: packr.unpack(buf); | 376700 | 5000 | 75340
11
- buf = require("msgpack-lite").encode(obj); | 30100 | 5012 | 6005
12
- obj = require("msgpack-lite").decode(buf); | 16200 | 5001 | 3239
13
- buf = require("notepack").encode(obj); | 62600 | 5005 | 12507
14
- obj = require("notepack").decode(buf); | 32400 | 5007 | 6470
15
- require("what-the-pack")... encoder.encode(obj); | 63500 | 5002 | 12694
16
- require("what-the-pack")... encoder.decode(buf); | 32000 | 5001 | 6398
17
- require("avsc")...make schema/type...type.toBuffer(obj); | 84600 | 5003 | 16909
18
- require("avsc")...make schema/type...type.toBuffer(obj); | 99300 | 5001 | 19856
3
+ ---------------------------------------------------------- | ------: | ----: | -----: | -----:
4
+ msgpackr w/ shared structures: packr.pack(obj); | 254700 | 5001 | 50929
5
+ msgpackr w/ shared structures: packr.unpack(buf); | 711700 | 5000 | 142340
6
+ require("msgpackr").pack(obj); | 234000 | 5000 | 46800
7
+ require("msgpackr").unpack(buf); | 186500 | 5000 | 37300
8
+ buf = Buffer(JSON.stringify(obj)); | 297900 | 5000 | 59580
9
+ obj = JSON.parse(buf); | 216600 | 5001 | 43311
10
+ buf = require("msgpack-lite").encode(obj); | 114000 | 5001 | 22795
11
+ obj = require("msgpack-lite").decode(buf); | 40700 | 5006 | 8130
12
+ buf = require("@msgpack/msgpack").encode(obj); | 166100 | 5000 | 33220
13
+ obj = require("@msgpack/msgpack").decode(buf); | 136500 | 5002 | 27289
14
+ buf = require("msgpack-js-v5").encode(obj); | 41600 | 5000 | 8320
15
+ obj = require("msgpack-js-v5").decode(buf); | 70200 | 5004 | 14028
16
+ buf = require("msgpack-js").encode(obj); | 40400 | 5012 | 8060
17
+ obj = require("msgpack-js").decode(buf); | 67100 | 5002 | 13414
18
+ buf = require("msgpack5")().encode(obj); | 15800 | 5024 | 3144
19
+ obj = require("msgpack5")().decode(buf); | 30600 | 5004 | 6115
20
+ buf = require("notepack").encode(obj); | 125100 | 5002 | 25009
21
+ obj = require("notepack").decode(buf); | 98600 | 5004 | 19704
22
+ require("what-the-pack")... encoder.encode(obj); | 150300 | 5001 | 30053
23
+ require("what-the-pack")... encoder.decode(buf); | 100100 | 5000 | 20020
24
+ obj = require("msgpack-unpack").decode(buf); | 14900 | 5031 | 2961
25
+ require("avsc")...make schema/type...type.toBuffer(obj); | 266600 | 5000 | 53320
26
+ require("avsc")...make schema/type...type.fromBuffer(obj); | 370200 | 5000 | 74040
19
27
 
20
28
  (`avsc` is schema-based and more comparable in style to msgpackr with shared structures).
21
29
 
30
+ (note that benchmarks below are several years old)
31
+
22
32
  Here is a benchmark of streaming data (again borrowed from `msgpack-lite`'s benchmarking), where msgpackr is able to take advantage of the structured record extension and really pull away from other tools:
23
33
 
24
34
  operation (1000000 x 2) | op | ms | op/s
@@ -31,13 +31,6 @@
31
31
  var inlineObjectReadThreshold = 2;
32
32
  var readStruct;
33
33
  var BlockedFunction; // we use search and replace to change the next call to BlockedFunction to avoid CSP issues for
34
- // no-eval build
35
- try {
36
- new BlockedFunction ('');
37
- } catch(error) {
38
- // if eval variants are not supported, do not create inline object readers ever
39
- inlineObjectReadThreshold = Infinity;
40
- }
41
34
 
42
35
  class Unpackr {
43
36
  constructor(options) {
@@ -503,11 +496,18 @@
503
496
  function readObject() {
504
497
  // This initial function is quick to instantiate, but runs slower. After several iterations pay the cost to build the faster function
505
498
  if (readObject.count++ > inlineObjectReadThreshold) {
506
- let readObject = structure.read = (new BlockedFunction ('r', 'return function(){return ' + (currentUnpackr.freezeData ? 'Object.freeze' : '') +
507
- '({' + structure.map(key => key === '__proto__' ? '__proto_:r()' : validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '})}'))(read);
499
+ let optimizedReadObject;
500
+ try {
501
+ optimizedReadObject = structure.read = (new BlockedFunction ('r', 'return function(){return ' + (currentUnpackr.freezeData ? 'Object.freeze' : '') +
502
+ '({' + structure.map(key => key === '__proto__' ? '__proto_:r()' : validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '})}'))(read);
503
+ } catch(error) {
504
+ // in CF workers, the new BlockedFunction call could begin to fail at any point in time
505
+ inlineObjectReadThreshold = Infinity; // disable going forward
506
+ return readObject(); // recursively try again
507
+ }
508
508
  if (structure.highByte === 0)
509
509
  structure.read = createSecondByteReader(firstId, structure.read);
510
- return readObject() // second byte is already read, if there is one so immediately read object
510
+ return optimizedReadObject() // second byte is already read, if there is one so immediately read object
511
511
  }
512
512
  let object = {};
513
513
  for (let i = 0, l = structure.length; i < l; i++) {
@@ -576,26 +576,45 @@
576
576
  } else if ((byte1 & 0xe0) === 0xc0) {
577
577
  // 2 bytes
578
578
  const byte2 = src[position$1++] & 0x3f;
579
- units.push(((byte1 & 0x1f) << 6) | byte2);
579
+ const codePoint = ((byte1 & 0x1f) << 6) | byte2;
580
+ // Reject overlong encoding: 2-byte sequences must encode values >= 0x80
581
+ if (codePoint < 0x80) {
582
+ units.push(0xFFFD); // replacement character
583
+ } else {
584
+ units.push(codePoint);
585
+ }
580
586
  } else if ((byte1 & 0xf0) === 0xe0) {
581
587
  // 3 bytes
582
588
  const byte2 = src[position$1++] & 0x3f;
583
589
  const byte3 = src[position$1++] & 0x3f;
584
- units.push(((byte1 & 0x1f) << 12) | (byte2 << 6) | byte3);
590
+ const codePoint = ((byte1 & 0x1f) << 12) | (byte2 << 6) | byte3;
591
+ // Reject overlong encoding: 3-byte sequences must encode values >= 0x800
592
+ // Also reject surrogates (0xD800-0xDFFF)
593
+ if (codePoint < 0x800 || (codePoint >= 0xD800 && codePoint <= 0xDFFF)) {
594
+ units.push(0xFFFD); // replacement character
595
+ } else {
596
+ units.push(codePoint);
597
+ }
585
598
  } else if ((byte1 & 0xf8) === 0xf0) {
586
599
  // 4 bytes
587
600
  const byte2 = src[position$1++] & 0x3f;
588
601
  const byte3 = src[position$1++] & 0x3f;
589
602
  const byte4 = src[position$1++] & 0x3f;
590
603
  let unit = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0c) | (byte3 << 0x06) | byte4;
591
- if (unit > 0xffff) {
604
+ // Reject overlong encoding: 4-byte sequences must encode values >= 0x10000
605
+ // Also reject values > 0x10FFFF (maximum valid Unicode)
606
+ if (unit < 0x10000 || unit > 0x10FFFF) {
607
+ units.push(0xFFFD); // replacement character
608
+ } else if (unit > 0xffff) {
592
609
  unit -= 0x10000;
593
610
  units.push(((unit >>> 10) & 0x3ff) | 0xd800);
594
611
  unit = 0xdc00 | (unit & 0x3ff);
612
+ units.push(unit);
613
+ } else {
614
+ units.push(unit);
595
615
  }
596
- units.push(unit);
597
616
  } else {
598
- units.push(byte1);
617
+ units.push(0xFFFD); // replacement character for invalid lead byte
599
618
  }
600
619
 
601
620
  if (units.length >= 0x1000) {