cbor-x 1.4.0 → 1.5.0

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
@@ -52,6 +52,26 @@ receivingStream.on('data', (data) => {
52
52
  ```
53
53
  The `EncoderStream` and `DecoderStream` instances will have also the record structure extension enabled by default (see below).
54
54
 
55
+ ### Iterables
56
+ In addition to using CBOR with streams, CBOR can also encode to an iterable that can be iterated as a sequence of binary chunks with `encodeAsIterable`, which facilitates progressive encoding:
57
+ ```JavaScript
58
+ import { encodeAsIterable } from 'cbor-x';
59
+
60
+ for (let binaryChunk of encodeAsIterable(data)){
61
+ // progressively get binary chunks as data is encoded
62
+ }
63
+ ```
64
+ And `encodeAsAsyncIterable` is also available, which returns an async iterable, and can be used to encode data from async iterables as well as Blob data.
65
+
66
+ ```JavaScript
67
+ import { encodeAsAsyncIterable } from 'cbor-x';
68
+
69
+ let data = { blob: new Blob(...) };
70
+ for await (let binaryChunk of encodeAsAsyncIterable(data)){
71
+ // progressively get binary chunks as asynchronous data source is encoded
72
+ }
73
+ ```
74
+
55
75
  ## Deno Usage
56
76
  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 pack/encode and unpack/decode functionality is available on Deno, like other platforms.
57
77
 
@@ -168,7 +188,7 @@ assert.deepEqual(senmlEncoder.decode(senmlBuff), data)
168
188
  ```
169
189
 
170
190
  ### CBOR Packing
171
- [Packed CBOR](https://datatracker.ietf.org/doc/html/draft-ietf-cbor-packed) is additional specification for CBOR which allows for compact encoding of data that has repeated values. Cbor-x supports decoding packed CBOR, no or flags/options needed. Cbor-x can also optionally generate packed CBOR (with the `pack` option), which will cause the encoder to look for repeated strings in a data structure that is being encoded, and store the strings in a packed table that can be referenced, to reduce encoding size. This involves extra overhead and reduces encoding performance, and generally does not yield as much compaction as standard compression tools. However, this is can be much faster than encoding plus compression, while still providing some level of reduction in encoding size. In addition to size reduction, packed CBOR is also usually faster to decode (assuming that some repetitive values could be found/packed).
191
+ [Packed CBOR](https://datatracker.ietf.org/doc/html/draft-ietf-cbor-packed) is additional specification for CBOR which allows for compact encoding of data that has repeated values. Cbor-x supports decoding packed CBOR, no flags or options needed. Cbor-x can also optionally generate packed CBOR (with the `pack` option), which will cause the encoder to look for repeated strings in a data structure that is being encoded, and store the strings in a packed table that can be referenced, to reduce encoding size. This involves extra overhead and reduces encoding performance, and generally does not yield as much compaction as standard compression tools. However, this is can be much faster than encoding plus compression, while still providing some level of reduction in encoding size. In addition to size reduction, packed CBOR is also usually faster to decode (assuming that some repetitive values could be found/packed).
172
192
 
173
193
  Cbor-x also has in-progress effort to support shared packed tables.
174
194
 
package/decode.js CHANGED
@@ -84,7 +84,7 @@ export class Decoder {
84
84
  }
85
85
  let res = {}
86
86
  //map.forEach((v,k) => res[Object.keys(this._keyMap)[Object.values(this._keyMap).indexOf(k)] || k] = v)
87
- map.forEach((v,k) => res[this._mapKey.has(k) ? this._mapKey.get(k) : k] = v)
87
+ map.forEach((v,k) => res[safeKey(this._mapKey.has(k) ? this._mapKey.get(k) : k)] = v)
88
88
  return res
89
89
  }
90
90
 
@@ -197,6 +197,7 @@ export function checkedRead() {
197
197
  }
198
198
  // bundled strings to skip past
199
199
  position = bundledStrings.postBundlePosition;
200
+ bundledStrings = null;
200
201
  }
201
202
 
202
203
  if (position == srcEnd) {
@@ -289,8 +290,8 @@ export function read() {
289
290
  let key
290
291
  if (currentDecoder.mapsAsObjects) {
291
292
  let object = {}
292
- if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) object[currentDecoder.decodeKey(key)] = read()
293
- else while ((key = read()) != STOP_CODE) object[key] = read()
293
+ if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) object[safeKey(currentDecoder.decodeKey(key))] = read()
294
+ else while ((key = read()) != STOP_CODE) object[safeKey(key)] = read()
294
295
  return object
295
296
  } else {
296
297
  if (restoreMapsAsObject) {
@@ -338,8 +339,8 @@ export function read() {
338
339
  case 5: // map
339
340
  if (currentDecoder.mapsAsObjects) {
340
341
  let object = {}
341
- if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[currentDecoder.decodeKey(read())] = read()
342
- else for (let i = 0; i < token; i++) object[read()] = read()
342
+ if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[safeKey(currentDecoder.decodeKey(read()))] = read()
343
+ else for (let i = 0; i < token; i++) object[safeKey(read())] = read()
343
344
  return object
344
345
  } else {
345
346
  if (restoreMapsAsObject) {
@@ -455,8 +456,8 @@ function createStructureReader(structure) {
455
456
  if (this.slowReads++ >= 3) { // create a fast compiled reader
456
457
  let array = this.length == length ? this : this.slice(0, length)
457
458
  compiledReader = currentDecoder.keyMap
458
- ? new Function('r', 'return {' + array.map(k => currentDecoder.decodeKey(k)).map(k => validName.test(k) ? k + ':r()' : ('[' + JSON.stringify(k) + ']:r()')).join(',') + '}')
459
- : new Function('r', 'return {' + array.map(key => validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '}')
459
+ ? new Function('r', 'return {' + array.map(k => currentDecoder.decodeKey(k)).map(k => validName.test(k) ? safeKey(k) + ':r()' : ('[' + JSON.stringify(k) + ']:r()')).join(',') + '}')
460
+ : new Function('r', 'return {' + array.map(key => validName.test(key) ? safeKey(key) + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '}')
460
461
  if (this.compiledReader)
461
462
  compiledReader.next = this.compiledReader // if there is an existing one, we store multiple readers as a linked list because it is usually pretty rare to have multiple readers (of different length) for the same structure
462
463
  compiledReader.propertyCount = length
@@ -464,14 +465,20 @@ function createStructureReader(structure) {
464
465
  return compiledReader(read)
465
466
  }
466
467
  let object = {}
467
- if (currentDecoder.keyMap) for (let i = 0; i < length; i++) object[currentDecoder.decodeKey(this[i])] = read()
468
- else for (let i = 0; i < length; i++) object[this[i]] = read()
468
+ if (currentDecoder.keyMap) for (let i = 0; i < length; i++) object[safeKey(currentDecoder.decodeKey(this[i]))] = read()
469
+ else for (let i = 0; i < length; i++) {
470
+ object[safeKey(this[i])] = read();
471
+ }
469
472
  return object
470
473
  }
471
474
  structure.slowReads = 0
472
475
  return readObject
473
476
  }
474
477
 
478
+ function safeKey(key) {
479
+ return key === '__proto__' ? '__proto_' : key
480
+ }
481
+
475
482
  let readFixedString = readStringJS
476
483
  let readString8 = readStringJS
477
484
  let readString16 = readStringJS
@@ -737,18 +744,30 @@ function readExt(length) {
737
744
  else
738
745
  throw new Error('Unknown extension type ' + type)
739
746
  }
740
-
747
+ let f32Array = new Float32Array(1)
748
+ let u8Array = new Uint8Array(f32Array.buffer, 0, 4)
741
749
  function getFloat16() {
742
750
  let byte0 = src[position++]
743
751
  let byte1 = src[position++]
744
- let half = (byte0 << 8) + byte1
745
- let exp = (half >> 10) & 0x1f
746
- let mant = half & 0x3ff
747
- let val
748
- if (exp == 0) val = Math.exp(mant, -24)
749
- else if (exp != 31) val = Math.exp(mant + 1024, exp - 25)
750
- else val = mant == 0 ? Infinity : NaN
751
- return half & 0x8000 ? -val : val
752
+ let exponent = (byte0 & 0x7f) >> 2;
753
+ if (exponent === 0x1f) { // specials
754
+ if (byte1 || (byte0 & 3))
755
+ return NaN;
756
+ return (byte0 & 0x80) ? -Infinity : Infinity;
757
+ }
758
+ if (exponent === 0) { // sub-normals
759
+ // significand with 10 fractional bits and divided by 2^14
760
+ let abs = (((byte0 & 3) << 8) | byte1) / (1 << 24)
761
+ return (byte0 & 0x80) ? -abs : abs
762
+ }
763
+
764
+ u8Array[3] = (byte0 & 0x80) | // sign bit
765
+ ((exponent >> 1) + 56) // 4 of 5 of the exponent bits, re-offset-ed
766
+ u8Array[2] = ((byte0 & 7) << 5) | // last exponent bit and first two mantissa bits
767
+ (byte1 >> 3) // next 5 bits of mantissa
768
+ u8Array[1] = byte1 << 5; // last three bits of mantissa
769
+ u8Array[0] = 0;
770
+ return f32Array[0];
752
771
  }
753
772
 
754
773
  let keyCache = new Array(4096)
@@ -869,11 +888,11 @@ const recordDefinition = (definition) => {
869
888
  let object = {}
870
889
  if (currentDecoder.keyMap) for (let i = 2,l = definition.length; i < l; i++) {
871
890
  let key = currentDecoder.decodeKey(structure[i - 2])
872
- object[key] = definition[i]
891
+ object[safeKey(key)] = definition[i]
873
892
  }
874
893
  else for (let i = 2,l = definition.length; i < l; i++) {
875
894
  let key = structure[i - 2]
876
- object[key] = definition[i]
895
+ object[safeKey(key)] = definition[i]
877
896
  }
878
897
  return object
879
898
  }
@@ -915,18 +934,23 @@ currentExtensions[PACKED_REFERENCE_TAG_ID] = (data) => { // packed reference
915
934
  return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
916
935
  throw new Error('No support for non-integer packed references yet')
917
936
  }
918
- currentExtensions[25] = (id) => {
919
- return stringRefs[id]
920
- }
921
- currentExtensions[256] = (read) => {
922
- stringRefs = []
923
- try {
924
- return read()
925
- } finally {
926
- stringRefs = null
927
- }
928
- }
929
- currentExtensions[256].handlesRead = true
937
+
938
+ // The following code is an incomplete implementation of http://cbor.schmorp.de/stringref
939
+ // the real thing would need to implemennt more logic to populate the stringRefs table and
940
+ // maintain a stack of stringRef "namespaces".
941
+ //
942
+ // currentExtensions[25] = (id) => {
943
+ // return stringRefs[id]
944
+ // }
945
+ // currentExtensions[256] = (read) => {
946
+ // stringRefs = []
947
+ // try {
948
+ // return read()
949
+ // } finally {
950
+ // stringRefs = null
951
+ // }
952
+ // }
953
+ // currentExtensions[256].handlesRead = true
930
954
 
931
955
  currentExtensions[28] = (read) => {
932
956
  // shareable http://cbor.schmorp.de/value-sharing (for structured clones)
@@ -1160,8 +1184,6 @@ export const FLOAT32_OPTIONS = {
1160
1184
  DECIMAL_ROUND: 3,
1161
1185
  DECIMAL_FIT: 4
1162
1186
  }
1163
- let f32Array = new Float32Array(1)
1164
- let u8Array = new Uint8Array(f32Array.buffer, 0, 4)
1165
1187
  export function roundFloat32(float32Number) {
1166
1188
  f32Array[0] = float32Number
1167
1189
  let multiplier = mult10[((u8Array[3] & 0x7f) << 1) | (u8Array[2] >> 7)]