cbor-x 1.6.0 → 1.6.4

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
@@ -29,7 +29,7 @@ This `encode` function will generate standard CBOR without any extensions that s
29
29
  Cbor-x modules are standard ESM modules and can be loaded directly from the [deno.land registry for cbor](https://deno.land/x/cbor) for use in Deno. The standard encode and decode functionality is available on Deno, like other platforms.
30
30
 
31
31
  ### Streams
32
- We can use the including streaming functionality (which further improves performance). The `EncoderStream` is a NodeJS transform stream that can be used to serialize objects to a binary stream (writing to network/socket, IPC, etc.), and the `DecoderStream` can be used to deserialize objects from a binary sream (reading from network/socket, etc.):
32
+ We can use the including streaming functionality (which further improves performance). The `EncoderStream` is a NodeJS transform stream that can be used to serialize objects to a binary stream (writing to network/socket, IPC, etc.), and the `DecoderStream` can be used to deserialize objects from a binary stream (reading from network/socket, etc.):
33
33
 
34
34
  ```JavaScript
35
35
  import { EncoderStream } from 'cbor-x';
@@ -89,23 +89,22 @@ import { decode } from 'cbor-x/decode' // if you only need to decode
89
89
  ```
90
90
 
91
91
  ## Structured Cloning
92
- You can also use cbor-x for [structured cloning](https://html.spec.whatwg.org/multipage/structured-data.html). By enabling the `structuredClone` option, you can include references to other objects or cyclic references, and object identity will be preserved. Structured cloning also enables preserving certain typed objects like `Error`, `Set`, `RegExp` and TypedArray instances, using [registered CBOR tag extensions](https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml). For example:
92
+ You can also use cbor-x for [structured cloning](https://html.spec.whatwg.org/multipage/structured-data.html). By enabling the `structuredClone` option, you can include references to other objects or cyclic references, and object identity will be preserved.For example:
93
93
  ```JavaScript
94
94
  let obj = {
95
- set: new Set(['a', 'b']),
96
- regular: /a\spattern/
97
95
  };
98
96
  obj.self = obj;
99
97
  let encoder = new Encoder({ structuredClone: true });
100
98
  let serialized = encoder.encode(obj);
101
99
  let copy = encoder.decode(serialized);
102
100
  copy.self === copy // true
103
- copy.set.has('a') // true
104
101
 
105
102
  ```
106
103
 
107
104
  This option is disabled by default because reference checking degrades performance (by about 25-30%). (Note this implementation doesn't serialize every class/type specified in the HTML specification since not all of them make sense for storing across platforms.)
108
105
 
106
+ cbor-x also preserves certain typed objects like `Error`, `Set`, `RegExp` and TypedArray instances, using [registered CBOR tag extensions](https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml). This works with or without structured cloning enabled.
107
+
109
108
  ## Record / Object Structures
110
109
  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. Typical JS objects/records may have many instances re(use) the same structure. By using the record extension, this distinction is preserved in CBOR and the encoding can reuse structures and not only provides better type preservation, but yield much more compact encodings and increase decoding performance by 2-3x. Cbor-x automatically generates record definitions that are reused and referenced by objects with the same structure. Records use CBOR's tags to align well CBOR's tag/extension mechanism. 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 `Encoder` instance. By default a new `Encoder` instance will have the record extension enabled:
111
110
  ```JavaScript
@@ -166,7 +165,7 @@ The principle driver for this feature is to support `application/senml+cbor`cont
166
165
  Records are also supported in conjunction with keyMaps, but these are disabled by default when keyMaps are specified as use of the two features does not introduce any additional compression efficiency unless that the data arrays are quite large (> 10 items).
167
166
 
168
167
  ```JavaScript
169
- import { Encoder } from 'cbor-x'
168
+ import { Decoder, Encoder } from 'cbor-x'
170
169
  const data = [
171
170
  { bn: '/3303/0/5700', bt: 1278887, v: 35.5 },
172
171
  { t: 10, v: 34 },
@@ -183,7 +182,8 @@ let senmlBuff = senmlCbor.encode(data)
183
182
  let basicBuff = basicCbor.encode(data)
184
183
  console.log('Senml CBOR size:', senmlBuff.length) // 77
185
184
  console.log('Basic CBOR size:', basicBuff.length) // 90
186
- assert.deepEqual(senmlEncoder.decode(senmlBuff), data)
185
+ let senmlDecoder = new Decoder({ keyMap: senmlKeys });
186
+ assert.deepEqual(senmlDecoder.decode(senmlBuff), data)
187
187
 
188
188
  ```
189
189
 
@@ -213,6 +213,7 @@ The following options properties can be provided to the Encoder or Decoder const
213
213
  * `useTag259ForMaps` - This flag indicates if [tag 259 (explicit maps)](https://github.com/shanewholloway/js-cbor-codec/blob/master/docs/CBOR-259-spec--explicit-maps.md) should be used to encode JS `Map`s. When using records is enabled, this is disabled by default, since plain objects are encoded with record structures and unambigiously differentiated from `Map`s, which are encoded as CBOR maps. Without using records, this enabled by default and is necessary to distinguish plain objects from `Map`s (but can be disabled by setting this to `false`).
214
214
  * `tagUint8Array` - Indicates if tag 64 should be used for `Uint8Array`s.
215
215
  * `int64AsNumber` - This will decode uint64 and int64 numbers as standard JS numbers rather than as bigint numbers.
216
+ * `skipFunction` - This skip functions in encode object.
216
217
 
217
218
  ### 32-bit Float Options
218
219
  By default all non-integer numbers are serialized as 64-bit float (double). This is fast, and ensures maximum precision. However, often real-world data doesn't not need 64-bits of precision, and using 32-bit encoding can be much more space efficient. There are several options that provide more efficient encodings. Using the decimal rounding options for encoding and decoding provides lossless storage of common decimal representations like 7.99, in more efficient 32-bit format (rather than 64-bit). The `useFloat32` property has several possible options, available from the module as constants:
package/decode.js CHANGED
@@ -273,6 +273,7 @@ export function read() {
273
273
  }
274
274
  token = dataView.getUint32(position)
275
275
  position += 4
276
+ if (majorType === 1) return -1 - token; // can't safely use negation operator here
276
277
  break
277
278
  case 0x1b:
278
279
  if (majorType == 7) {
@@ -287,8 +288,7 @@ export function read() {
287
288
  } else if (currentDecoder.int64AsNumber) {
288
289
  token = dataView.getUint32(position) * 0x100000000
289
290
  token += dataView.getUint32(position + 4)
290
- } else
291
- token = dataView.getBigUint64(position)
291
+ } else token = dataView.getBigUint64(position)
292
292
  position += 8
293
293
  break
294
294
  case 0x1f:
@@ -611,26 +611,45 @@ function readStringJS(length) {
611
611
  } else if ((byte1 & 0xe0) === 0xc0) {
612
612
  // 2 bytes
613
613
  const byte2 = src[position++] & 0x3f
614
- units.push(((byte1 & 0x1f) << 6) | byte2)
614
+ const codePoint = ((byte1 & 0x1f) << 6) | byte2
615
+ // Reject overlong encoding: 2-byte sequences must encode values >= 0x80
616
+ if (codePoint < 0x80) {
617
+ units.push(0xFFFD) // replacement character
618
+ } else {
619
+ units.push(codePoint)
620
+ }
615
621
  } else if ((byte1 & 0xf0) === 0xe0) {
616
622
  // 3 bytes
617
623
  const byte2 = src[position++] & 0x3f
618
624
  const byte3 = src[position++] & 0x3f
619
- units.push(((byte1 & 0x1f) << 12) | (byte2 << 6) | byte3)
625
+ const codePoint = ((byte1 & 0x1f) << 12) | (byte2 << 6) | byte3
626
+ // Reject overlong encoding: 3-byte sequences must encode values >= 0x800
627
+ // Also reject surrogates (0xD800-0xDFFF)
628
+ if (codePoint < 0x800 || (codePoint >= 0xD800 && codePoint <= 0xDFFF)) {
629
+ units.push(0xFFFD) // replacement character
630
+ } else {
631
+ units.push(codePoint)
632
+ }
620
633
  } else if ((byte1 & 0xf8) === 0xf0) {
621
634
  // 4 bytes
622
635
  const byte2 = src[position++] & 0x3f
623
636
  const byte3 = src[position++] & 0x3f
624
637
  const byte4 = src[position++] & 0x3f
625
638
  let unit = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0c) | (byte3 << 0x06) | byte4
626
- if (unit > 0xffff) {
639
+ // Reject overlong encoding: 4-byte sequences must encode values >= 0x10000
640
+ // Also reject values > 0x10FFFF (maximum valid Unicode)
641
+ if (unit < 0x10000 || unit > 0x10FFFF) {
642
+ units.push(0xFFFD) // replacement character
643
+ } else if (unit > 0xffff) {
627
644
  unit -= 0x10000
628
645
  units.push(((unit >>> 10) & 0x3ff) | 0xd800)
629
646
  unit = 0xdc00 | (unit & 0x3ff)
647
+ units.push(unit)
648
+ } else {
649
+ units.push(unit)
630
650
  }
631
- units.push(unit)
632
651
  } else {
633
- units.push(byte1)
652
+ units.push(0xFFFD) // replacement character for invalid lead byte
634
653
  }
635
654
 
636
655
  if (units.length >= 0x1000) {
@@ -274,6 +274,7 @@
274
274
  }
275
275
  token = dataView.getUint32(position);
276
276
  position += 4;
277
+ if (majorType === 1) return -1 - token; // can't safely use negation operator here
277
278
  break
278
279
  case 0x1b:
279
280
  if (majorType == 7) {
@@ -288,8 +289,7 @@
288
289
  } else if (currentDecoder.int64AsNumber) {
289
290
  token = dataView.getUint32(position) * 0x100000000;
290
291
  token += dataView.getUint32(position + 4);
291
- } else
292
- token = dataView.getBigUint64(position);
292
+ } else token = dataView.getBigUint64(position);
293
293
  position += 8;
294
294
  break
295
295
  case 0x1f:
@@ -606,26 +606,45 @@
606
606
  } else if ((byte1 & 0xe0) === 0xc0) {
607
607
  // 2 bytes
608
608
  const byte2 = src[position++] & 0x3f;
609
- units.push(((byte1 & 0x1f) << 6) | byte2);
609
+ const codePoint = ((byte1 & 0x1f) << 6) | byte2;
610
+ // Reject overlong encoding: 2-byte sequences must encode values >= 0x80
611
+ if (codePoint < 0x80) {
612
+ units.push(0xFFFD); // replacement character
613
+ } else {
614
+ units.push(codePoint);
615
+ }
610
616
  } else if ((byte1 & 0xf0) === 0xe0) {
611
617
  // 3 bytes
612
618
  const byte2 = src[position++] & 0x3f;
613
619
  const byte3 = src[position++] & 0x3f;
614
- units.push(((byte1 & 0x1f) << 12) | (byte2 << 6) | byte3);
620
+ const codePoint = ((byte1 & 0x1f) << 12) | (byte2 << 6) | byte3;
621
+ // Reject overlong encoding: 3-byte sequences must encode values >= 0x800
622
+ // Also reject surrogates (0xD800-0xDFFF)
623
+ if (codePoint < 0x800 || (codePoint >= 0xD800 && codePoint <= 0xDFFF)) {
624
+ units.push(0xFFFD); // replacement character
625
+ } else {
626
+ units.push(codePoint);
627
+ }
615
628
  } else if ((byte1 & 0xf8) === 0xf0) {
616
629
  // 4 bytes
617
630
  const byte2 = src[position++] & 0x3f;
618
631
  const byte3 = src[position++] & 0x3f;
619
632
  const byte4 = src[position++] & 0x3f;
620
633
  let unit = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0c) | (byte3 << 0x06) | byte4;
621
- if (unit > 0xffff) {
634
+ // Reject overlong encoding: 4-byte sequences must encode values >= 0x10000
635
+ // Also reject values > 0x10FFFF (maximum valid Unicode)
636
+ if (unit < 0x10000 || unit > 0x10FFFF) {
637
+ units.push(0xFFFD); // replacement character
638
+ } else if (unit > 0xffff) {
622
639
  unit -= 0x10000;
623
640
  units.push(((unit >>> 10) & 0x3ff) | 0xd800);
624
641
  unit = 0xdc00 | (unit & 0x3ff);
642
+ units.push(unit);
643
+ } else {
644
+ units.push(unit);
625
645
  }
626
- units.push(unit);
627
646
  } else {
628
- units.push(byte1);
647
+ units.push(0xFFFD); // replacement character for invalid lead byte
629
648
  }
630
649
 
631
650
  if (units.length >= 0x1000) {