cbor-x 0.9.2 → 1.1.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/encode.js CHANGED
@@ -7,7 +7,7 @@ let extensions, extensionClasses
7
7
  const hasNodeBuffer = typeof Buffer !== 'undefined'
8
8
  const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array
9
9
  const ByteArray = hasNodeBuffer ? Buffer : Uint8Array
10
- const RECORD_STARTING_ID_PREFIX = 0x69 // tag 105/0x69
10
+ const RECORD_INLINE_ID = 0xdfff // temporary first-come first-serve tag // proposed tag: 0x7265 // 're'
11
11
  const MAX_STRUCTURES = 0x100
12
12
  const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000
13
13
  let serializationId = 1
@@ -15,6 +15,9 @@ let target
15
15
  let targetView
16
16
  let position = 0
17
17
  let safeEnd
18
+ let bundledStrings = null
19
+ const MAX_BUNDLE_SIZE = 0xf000
20
+ const hasNonLatin = /[\u0080-\uFFFF]/
18
21
  const RECORD_SYMBOL = Symbol('record-id')
19
22
  export class Encoder extends Decoder {
20
23
  constructor(options) {
@@ -27,7 +30,6 @@ export class Encoder extends Decoder {
27
30
  let structures
28
31
  let referenceMap
29
32
  options = options || {}
30
- let lastSharedStructuresLength = 0
31
33
  let encodeUtf8 = ByteArray.prototype.utf8Write ? function(string, position, maxBytes) {
32
34
  return target.utf8Write(string, position, maxBytes)
33
35
  } : (textEncoder && textEncoder.encodeInto) ?
@@ -36,12 +38,20 @@ export class Encoder extends Decoder {
36
38
  } : false
37
39
 
38
40
  let encoder = this
39
- let maxSharedStructures = 64
41
+ let hasSharedStructures = options.structures || options.saveStructures
42
+ let maxSharedStructures = options.maxSharedStructures
43
+ if (maxSharedStructures == null)
44
+ maxSharedStructures = hasSharedStructures ? 128 : 0
45
+ if (maxSharedStructures > 8190)
46
+ throw new Error('Maximum maxSharedStructure is 8190')
40
47
  let isSequential = options.sequential
41
48
  if (isSequential) {
42
49
  maxSharedStructures = 0
43
- this.structures = []
44
50
  }
51
+ if (!this.structures)
52
+ this.structures = []
53
+ if (this.saveStructures)
54
+ this.saveShared = this.saveStructures
45
55
  let samplingPackedValues, packedObjectMap, sharedValues = options.sharedValues
46
56
  let sharedPackedObjectMap
47
57
  if (sharedValues) {
@@ -49,13 +59,29 @@ export class Encoder extends Decoder {
49
59
  for (let i = 0, l = sharedValues.length; i < l; i++) {
50
60
  sharedPackedObjectMap[sharedValues[i]] = i
51
61
  }
52
-
53
62
  }
54
63
  let recordIdsToRemove = []
55
64
  let transitionsCount = 0
56
65
  let serializationsSinceTransitionRebuild = 0
57
-
58
- this.encode = function(value, encodeOptions) {
66
+
67
+ this.mapEncode = function(value, encodeOptions) {
68
+ // Experimental support for premapping keys using _keyMap instad of keyMap - not optiimised yet)
69
+ if (this._keyMap && !this._mapped) {
70
+ //console.log('encoding ', value)
71
+ switch (value.constructor.name) {
72
+ case 'Array':
73
+ value = value.map(r => this.encodeKeys(r))
74
+ break
75
+ //case 'Map':
76
+ // value = this.encodeKeys(value)
77
+ // break
78
+ }
79
+ //this._mapped = true
80
+ }
81
+ return this.encode(value, encodeOptions)
82
+ }
83
+
84
+ this.encode = function(value, encodeOptions) {
59
85
  if (!target) {
60
86
  target = new ByteArrayAllocate(8192)
61
87
  targetView = new DataView(target.buffer, 0, 8192)
@@ -71,11 +97,30 @@ export class Encoder extends Decoder {
71
97
  } else if (encodeOptions === REUSE_BUFFER_MODE)
72
98
  position = (position + 7) & 0x7ffffff8 // Word align to make any future copying of this buffer faster
73
99
  start = position
100
+ if (encoder.useSelfDescribedHeader) {
101
+ targetView.setUint32(position, 0xd9d9f700) // tag two byte, then self-descriptive tag
102
+ position += 3
103
+ }
74
104
  referenceMap = encoder.structuredClone ? new Map() : null
105
+ if (encoder.bundleStrings && typeof value !== 'string') {
106
+ bundledStrings = []
107
+ bundledStrings.size = Infinity // force a new bundle start on first string
108
+ } else
109
+ bundledStrings = null
110
+
75
111
  sharedStructures = encoder.structures
76
112
  if (sharedStructures) {
77
- if (sharedStructures.uninitialized)
78
- encoder.structures = sharedStructures = encoder.getStructures()
113
+ if (sharedStructures.uninitialized) {
114
+ let sharedData = encoder.getShared() || {}
115
+ encoder.structures = sharedStructures = sharedData.structures || []
116
+ encoder.sharedVersion = sharedData.version
117
+ let sharedValues = encoder.sharedValues = sharedData.packedValues
118
+ if (sharedValues) {
119
+ sharedPackedObjectMap = {}
120
+ for (let i = 0, l = sharedValues.length; i < l; i++)
121
+ sharedPackedObjectMap[sharedValues[i]] = i
122
+ }
123
+ }
79
124
  let sharedStructuresLength = sharedStructures.length
80
125
  if (sharedStructuresLength > maxSharedStructures && !isSequential)
81
126
  sharedStructuresLength = maxSharedStructures
@@ -84,10 +129,13 @@ export class Encoder extends Decoder {
84
129
  sharedStructures.transitions = Object.create(null)
85
130
  for (let i = 0; i < sharedStructuresLength; i++) {
86
131
  let keys = sharedStructures[i]
132
+ //console.log('shared struct keys:', keys)
87
133
  if (!keys)
88
134
  continue
89
135
  let nextTransition, transition = sharedStructures.transitions
90
136
  for (let j = 0, l = keys.length; j < l; j++) {
137
+ if (transition[RECORD_SYMBOL] === undefined)
138
+ transition[RECORD_SYMBOL] = i
91
139
  let key = keys[j]
92
140
  nextTransition = transition[key]
93
141
  if (!nextTransition) {
@@ -95,9 +143,8 @@ export class Encoder extends Decoder {
95
143
  }
96
144
  transition = nextTransition
97
145
  }
98
- transition[RECORD_SYMBOL] = i
146
+ transition[RECORD_SYMBOL] = i | 0x100000
99
147
  }
100
- lastSharedStructuresLength = sharedStructures.length
101
148
  }
102
149
  if (!isSequential)
103
150
  sharedStructures.nextId = sharedStructuresLength
@@ -130,9 +177,12 @@ export class Encoder extends Decoder {
130
177
  }
131
178
  try {
132
179
  encode(value)
180
+ if (bundledStrings) {
181
+ writeBundles(start, encode)
182
+ }
133
183
  encoder.offset = position // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
134
184
  if (referenceMap && referenceMap.idsToInsert) {
135
- position += referenceMap.idsToInsert.length * 8
185
+ position += referenceMap.idsToInsert.length * 2
136
186
  if (position > safeEnd)
137
187
  makeRoom(position)
138
188
  encoder.offset = position
@@ -140,16 +190,18 @@ export class Encoder extends Decoder {
140
190
  referenceMap = null
141
191
  return serialized
142
192
  }
143
- if (encodeOptions === REUSE_BUFFER_MODE) {
193
+ if (encodeOptions & REUSE_BUFFER_MODE) {
144
194
  target.start = start
145
195
  target.end = position
146
196
  return target
147
197
  }
148
- return target.subarray(start, position) // position can change if we call pack again in saveStructures, so we get the buffer now
198
+ return target.subarray(start, position) // position can change if we call encode again in saveShared, so we get the buffer now
149
199
  } finally {
150
200
  if (sharedStructures) {
151
201
  if (serializationsSinceTransitionRebuild < 10)
152
202
  serializationsSinceTransitionRebuild++
203
+ if (sharedStructures.length > maxSharedStructures)
204
+ sharedStructures.length = maxSharedStructures
153
205
  if (transitionsCount > 10000) {
154
206
  // force a rebuild occasionally after a lot of transitions so it can get cleaned up
155
207
  sharedStructures.transitions = null
@@ -162,35 +214,29 @@ export class Encoder extends Decoder {
162
214
  recordIdsToRemove[i][RECORD_SYMBOL] = undefined
163
215
  }
164
216
  recordIdsToRemove = []
217
+ //sharedStructures.nextId = maxSharedStructures
165
218
  }
166
- if (hasSharedUpdate && encoder.saveStructures) {
167
- if (encoder.structures.length > maxSharedStructures) {
168
- encoder.structures = encoder.structures.slice(0, maxSharedStructures)
169
- }
170
- // we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
171
- let returnBuffer = target.subarray(start, position)
172
- let shared = encoder.structures || []
173
- if (sharedValues) {
174
- shared = shared.concat(sharedValues)
175
- }
176
-
177
- if (encoder.saveStructures(encoder.structures, lastSharedStructuresLength) === false) {
178
- // get updated structures and try again if the update failed
179
- encoder.structures = encoder.getStructures() || []
180
- return encoder.encode(value)
181
- }
182
- lastSharedStructuresLength = shared.length
183
- return returnBuffer
219
+ }
220
+ if (hasSharedUpdate && encoder.saveShared) {
221
+ if (encoder.structures.length > maxSharedStructures) {
222
+ encoder.structures = encoder.structures.slice(0, maxSharedStructures)
184
223
  }
224
+ // we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
225
+ let returnBuffer = target.subarray(start, position)
226
+ if (encoder.updateSharedData() === false)
227
+ return encoder.encode(value) // re-encode if it fails
228
+ return returnBuffer
185
229
  }
230
+ if (encodeOptions & RESET_BUFFER_MODE)
231
+ position = start
186
232
  }
187
233
  }
188
234
  this.findCommonStringsToPack = () => {
189
235
  samplingPackedValues = new Map()
190
236
  if (!sharedPackedObjectMap)
191
237
  sharedPackedObjectMap = Object.create(null)
192
- return ({ threshold }) => {
193
- threshold = threshold || 4
238
+ return (options) => {
239
+ let threshold = options && options.threshold || 4
194
240
  let position = this.pack ? options.maxPrivatePackedValues || 16 : 0
195
241
  if (!sharedValues)
196
242
  sharedValues = this.sharedValues = []
@@ -201,6 +247,7 @@ export class Encoder extends Decoder {
201
247
  hasSharedUpdate = true
202
248
  }
203
249
  }
250
+ while (this.saveShared && this.updateSharedData() === false) {}
204
251
  samplingPackedValues = null
205
252
  }
206
253
  }
@@ -249,6 +296,33 @@ export class Encoder extends Decoder {
249
296
  }
250
297
  }
251
298
  let strLength = value.length
299
+ if (bundledStrings && strLength >= 4 && strLength < 0x400) {
300
+ if ((bundledStrings.size += strLength) > MAX_BUNDLE_SIZE) {
301
+ let extStart
302
+ let maxBytes = (bundledStrings[0] ? bundledStrings[0].length * 3 + bundledStrings[1].length : 0) + 10
303
+ if (position + maxBytes > safeEnd)
304
+ target = makeRoom(position + maxBytes)
305
+ target[position++] = 0xd9 // tag 16-bit
306
+ target[position++] = 0xdf // tag 0xdff9
307
+ target[position++] = 0xf9
308
+ // TODO: If we only have one bundle with any string data, only write one string bundle
309
+ target[position++] = bundledStrings.position ? 0x84 : 0x82 // array of 4 or 2 elements depending on if we write bundles
310
+ target[position++] = 0x1a // 32-bit unsigned int
311
+ extStart = position - start
312
+ position += 4 // reserve for writing bundle reference
313
+ if (bundledStrings.position) {
314
+ writeBundles(start, encode) // write the last bundles
315
+ }
316
+ bundledStrings = ['', ''] // create new ones
317
+ bundledStrings.size = 0
318
+ bundledStrings.position = extStart
319
+ }
320
+ let twoByte = hasNonLatin.test(value)
321
+ bundledStrings[twoByte ? 0 : 1] += value
322
+ target[position++] = twoByte ? 0xce : 0xcf
323
+ encode(strLength);
324
+ return
325
+ }
252
326
  let headerSize
253
327
  // first we estimate the header size, so we can write to the correct location
254
328
  if (strLength < 0x20) {
@@ -375,16 +449,16 @@ export class Encoder extends Decoder {
375
449
  if (referenceMap) {
376
450
  let referee = referenceMap.get(value)
377
451
  if (referee) {
378
- if (!referee.id) {
452
+ target[position++] = 0xd8
453
+ target[position++] = 29 // http://cbor.schmorp.de/value-sharing
454
+ target[position++] = 0x19 // 16-bit uint
455
+ if (!referee.references) {
379
456
  let idsToInsert = referenceMap.idsToInsert || (referenceMap.idsToInsert = [])
380
- referee.id = idsToInsert.push(referee)
457
+ referee.references = []
458
+ idsToInsert.push(referee)
381
459
  }
382
- target[position++] = 0xd9
383
- target[position++] = 40010 >> 8
384
- target[position++] = 40010 & 0xff
385
- target[position++] = 0x1a // uint32
386
- targetView.setUint32(position, referee.id)
387
- position += 4
460
+ referee.references.push(position - start)
461
+ position += 2 // TODO: also support 32-bit
388
462
  return
389
463
  } else
390
464
  referenceMap.set(value, { offset: position - start })
@@ -424,16 +498,23 @@ export class Encoder extends Decoder {
424
498
  targetView.setUint32(position, length)
425
499
  position += 4
426
500
  }
427
- for (let [ key, entryValue ] of value) {
428
- encode(key)
429
- encode(entryValue)
501
+ if (encoder.keyMap) {
502
+ for (let [ key, entryValue ] of value) {
503
+ encode(encoder.encodeKey(key))
504
+ encode(entryValue)
505
+ }
506
+ } else {
507
+ for (let [ key, entryValue ] of value) {
508
+ encode(key)
509
+ encode(entryValue)
510
+ }
430
511
  }
431
- } else {
512
+ } else {
432
513
  for (let i = 0, l = extensions.length; i < l; i++) {
433
514
  let extensionClass = extensionClasses[i]
434
515
  if (value instanceof extensionClass) {
435
516
  let extension = extensions[i]
436
- let tag = extension.tag
517
+ let tag = extension.tag || extension.getTag && extension.getTag(value)
437
518
  if (tag < 0x18) {
438
519
  target[position++] = 0xc0 | tag
439
520
  } else if (tag < 0x100) {
@@ -495,6 +576,7 @@ export class Encoder extends Decoder {
495
576
  const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => {
496
577
  // this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
497
578
  let keys = Object.keys(object)
579
+ let vals = Object.values(object)
498
580
  let length = keys.length
499
581
  if (length < 0x18) {
500
582
  target[position++] = 0xa0 | length
@@ -511,9 +593,16 @@ export class Encoder extends Decoder {
511
593
  position += 4
512
594
  }
513
595
  let key
514
- for (let i = 0; i < length; i++) {
515
- encode(key = keys[i])
516
- encode(object[key])
596
+ if (encoder.keyMap) {
597
+ for (let i = 0; i < length; i++) {
598
+ encode(encodeKey(keys[i]))
599
+ encode(vals[i])
600
+ }
601
+ } else {
602
+ for (let i = 0; i < length; i++) {
603
+ encode(keys[i])
604
+ encode(vals[i])
605
+ }
517
606
  }
518
607
  } :
519
608
  (object, safePrototype) => {
@@ -521,95 +610,92 @@ export class Encoder extends Decoder {
521
610
  let objectOffset = position - start
522
611
  position += 2
523
612
  let size = 0
524
- for (let key in object) {
525
- if (safePrototype || object.hasOwnProperty(key)) {
526
- encode(key)
613
+ if (encoder.keyMap) {
614
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
615
+ encode(encoder.encodeKey(key))
527
616
  encode(object[key])
528
617
  size++
529
618
  }
619
+ } else {
620
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
621
+ encode(key)
622
+ encode(object[key])
623
+ size++
624
+ }
530
625
  }
531
626
  target[objectOffset++ + start] = size >> 8
532
627
  target[objectOffset + start] = size & 0xff
533
628
  } :
534
-
535
- /* sharedStructures ? // For highly stable structures, using for-in can a little bit faster
536
629
  (object, safePrototype) => {
537
630
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
538
- let objectOffset = position++ - start
539
- let wroteKeys
540
- for (let key in object) {
541
- if (safePrototype || object.hasOwnProperty(key)) {
631
+ let newTransitions = 0
632
+ let length = 0
633
+ let parentRecordId
634
+ let keys
635
+ if (this.keyMap) {
636
+ keys = Object.keys(object).map(k => this.encodeKey(k))
637
+ length = keys.length
638
+ for (let i = 0; i < length; i++) {
639
+ let key = keys[i]
542
640
  nextTransition = transition[key]
543
641
  if (!nextTransition) {
544
642
  nextTransition = transition[key] = Object.create(null)
545
- nextTransition.__keys__ = (transition.__keys__ || []).concat([key])
546
- /*let keys = Object.keys(object)
547
- if
548
- let size = 0
549
- let startBranch = transition.__keys__ ? transition.__keys__.length : 0
550
- for (let i = 0, l = keys.length; i++) {
551
- let key = keys[i]
552
- size += key.length << 2
553
- if (i >= startBranch) {
554
- nextTransition = nextTransition[key] = Object.create(null)
555
- nextTransition.__keys__ = keys.slice(0, i + 1)
556
- }
643
+ newTransitions++
644
+ }
645
+ transition = nextTransition
646
+ }
647
+ } else {
648
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
649
+ nextTransition = transition[key]
650
+ if (!nextTransition) {
651
+ if (transition[RECORD_SYMBOL] & 0x100000) {// this indicates it is a brancheable/extendable terminal node, so we will use this record id and extend it
652
+ parentRecordId = transition[RECORD_SYMBOL] & 0xffff
557
653
  }
558
- makeRoom(position + size)
559
- nextTransition = transition[key]
560
- target.copy(target, )
561
- objectOffset
654
+ nextTransition = transition[key] = Object.create(null)
655
+ newTransitions++
562
656
  }
563
657
  transition = nextTransition
564
- encode(object[key])
658
+ length++
565
659
  }
566
660
  }
567
- let id = transition.id
568
- if (!id) {
569
- id = transition.id = structures.push(transition.__keys__) + 63
570
- if (sharedStructures.onUpdate)
571
- sharedStructures.onUpdate(id, transition.__keys__)
572
- }
573
- target[objectOffset + start] = id
574
- }*/
575
- (object) => {
576
- let keys = Object.keys(object)
577
- let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
578
- let newTransitions = 0
579
- let length = keys.length
580
- for (let i = 0; i < length; i++) {
581
- let key = keys[i]
582
- nextTransition = transition[key]
583
- if (!nextTransition) {
584
- nextTransition = transition[key] = Object.create(null)
585
- newTransitions++
586
- }
587
- transition = nextTransition
588
- }
589
661
  let recordId = transition[RECORD_SYMBOL]
590
662
  if (recordId !== undefined) {
591
- target[position++] = 0xd9 // tag two byte
592
- target[position++] = RECORD_STARTING_ID_PREFIX
593
- target[position++] = recordId
663
+ recordId &= 0xffff
664
+ target[position++] = 0xd9
665
+ target[position++] = (recordId >> 8) | 0xe0
666
+ target[position++] = recordId & 0xff
594
667
  } else {
595
- recordId = structures.nextId++
596
- if (!recordId) {
597
- recordId = 0
598
- structures.nextId = 1
599
- }
600
- if (recordId >= MAX_STRUCTURES) {// cycle back around
601
- structures.nextId = (recordId = maxSharedStructures) + 1
668
+ if (!keys)
669
+ keys = transition.__keys__ || (transition.__keys__ = Object.keys(object))
670
+ if (parentRecordId === undefined) {
671
+ recordId = structures.nextId++
672
+ if (!recordId) {
673
+ recordId = 0
674
+ structures.nextId = 1
675
+ }
676
+ if (recordId >= MAX_STRUCTURES) {// cycle back around
677
+ structures.nextId = (recordId = maxSharedStructures) + 1
678
+ }
679
+ } else {
680
+ recordId = parentRecordId
602
681
  }
603
- transition[RECORD_SYMBOL] = recordId
604
682
  structures[recordId] = keys
605
- if (sharedStructures && sharedStructures.length <= maxSharedStructures) {
606
- target[position++] = 0xd9 // tag two byte
607
- target[position++] = RECORD_STARTING_ID_PREFIX
608
- target[position++] = recordId // tag number
683
+ if (recordId < maxSharedStructures) {
684
+ target[position++] = 0xd9
685
+ target[position++] = (recordId >> 8) | 0xe0
686
+ target[position++] = recordId & 0xff
687
+ transition = structures.transitions
688
+ for (let i = 0; i < length; i++) {
689
+ if (transition[RECORD_SYMBOL] === undefined || (transition[RECORD_SYMBOL] & 0x100000))
690
+ transition[RECORD_SYMBOL] = recordId
691
+ transition = transition[keys[i]]
692
+ }
693
+ transition[RECORD_SYMBOL] = recordId | 0x100000 // indicates it is a extendable terminal
609
694
  hasSharedUpdate = true
610
695
  } else {
611
- target[position++] = 0xd8
612
- target[position++] = RECORD_STARTING_ID_PREFIX
696
+ transition[RECORD_SYMBOL] = recordId
697
+ targetView.setUint32(position, 0xd9dfff00) // tag two byte, then record definition id
698
+ position += 3
613
699
  if (newTransitions)
614
700
  transitionsCount += serializationsSinceTransitionRebuild * newTransitions
615
701
  // record the removal of the id, we can maintain our shared structure
@@ -617,13 +703,9 @@ export class Encoder extends Decoder {
617
703
  recordIdsToRemove.shift()[RECORD_SYMBOL] = undefined // we are cycling back through, and have to remove old ones
618
704
  recordIdsToRemove.push(transition)
619
705
  writeArrayHeader(length + 2)
706
+ encode(0xe000 + recordId)
620
707
  encode(keys)
621
- target[position++] = 0x19 // uint16
622
- target[position++] = RECORD_STARTING_ID_PREFIX
623
- target[position++] = recordId
624
- // now write the values
625
- for (let i =0; i < length; i++)
626
- encode(object[keys[i]])
708
+ for (let v of Object.values(object)) encode(v)
627
709
  return
628
710
  }
629
711
  }
@@ -632,8 +714,9 @@ export class Encoder extends Decoder {
632
714
  } else {
633
715
  writeArrayHeader(length)
634
716
  }
635
- for (let i =0; i < length; i++)
636
- encode(object[keys[i]])
717
+ for (let key in object)
718
+ if (safePrototype || object.hasOwnProperty(key))
719
+ encode(object[key])
637
720
  }
638
721
  const makeRoom = (end) => {
639
722
  let newSize
@@ -642,7 +725,7 @@ export class Encoder extends Decoder {
642
725
  if ((end - start) > MAX_BUFFER_SIZE)
643
726
  throw new Error('Encoded buffer would be larger than maximum buffer size')
644
727
  newSize = Math.min(MAX_BUFFER_SIZE,
645
- Math.round(Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2), 0x1000000) / 0x1000) * 0x1000)
728
+ Math.round(Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2), 0x400000) / 0x1000) * 0x1000)
646
729
  } else // faster handling for smaller buffers
647
730
  newSize = ((Math.max((end - start) << 2, target.length - 1) >> 12) + 1) << 12
648
731
  let newBuffer = new ByteArrayAllocate(newSize)
@@ -663,11 +746,39 @@ export class Encoder extends Decoder {
663
746
  targetView = new DataView(target.buffer, target.byteOffset, target.byteLength)
664
747
  position = 0
665
748
  }
749
+ clearSharedData() {
750
+ if (this.structures)
751
+ this.structures = []
752
+ if (this.sharedValues)
753
+ this.sharedValues = undefined
754
+ }
755
+ updateSharedData() {
756
+ let lastVersion = this.sharedVersion || 0
757
+ this.sharedVersion = lastVersion + 1
758
+ let structuresCopy = this.structures.slice(0)
759
+ let sharedData = new SharedData(structuresCopy, this.sharedValues, this.sharedVersion)
760
+ let saveResults = this.saveShared(sharedData,
761
+ existingShared => (existingShared && existingShared.version || 0) == lastVersion)
762
+ if (saveResults === false) {
763
+ // get updated structures and try again if the update failed
764
+ sharedData = this.getShared() || {}
765
+ this.structures = sharedData.structures || []
766
+ this.sharedValues = sharedData.packedValues
767
+ this.sharedVersion = sharedData.version
768
+ this.structures.nextId = this.structures.length
769
+ } else {
770
+ // restore structures
771
+ structuresCopy.forEach((structure, i) => this.structures[i] = structure)
772
+ }
773
+ // saveShared may fail to write and reload, or may have reloaded to check compatibility and overwrite saved data, either way load the correct shared data
774
+ return saveResults
775
+ }
666
776
  }
667
-
668
- function copyBinary(source, target, targetOffset, offset, endOffset) {
669
- while (offset < endOffset) {
670
- target[targetOffset++] = source[offset++]
777
+ class SharedData {
778
+ constructor(structures, values, version) {
779
+ this.structures = structures
780
+ this.packedValues = values
781
+ this.version = version
671
782
  }
672
783
  }
673
784
 
@@ -738,9 +849,11 @@ function findRepetitiveStrings(value, packedValues) {
738
849
  }
739
850
  }
740
851
 
741
- extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, ByteArray,
742
- Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array, BigUint64Array, Int8Array, Int16Array, Int32Array, BigInt64Array,
743
- Float32Array, Float64Array]
852
+ extensionClasses = [ Date, Set, Error, RegExp, Tag, ArrayBuffer, ByteArray,
853
+ Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array,
854
+ typeof BigUint64Array == 'undefined' ? function() {} : BigUint64Array, Int8Array, Int16Array, Int32Array,
855
+ typeof BigInt64Array == 'undefined' ? function() {} : BigInt64Array,
856
+ Float32Array, Float64Array, SharedData]
744
857
 
745
858
  //Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/
746
859
  extensions = [{
@@ -775,6 +888,13 @@ extensions = [{
775
888
  encode(regex, encode) {
776
889
  encode([ 'RegExp', regex.source, regex.flags ])
777
890
  }
891
+ }, {
892
+ getTag(tag) {
893
+ return tag.tag
894
+ },
895
+ encode(tag, encode) {
896
+ encode(tag.value)
897
+ }
778
898
  }, {
779
899
  encode(arrayBuffer, encode, makeRoom) {
780
900
  writeBuffer(arrayBuffer, makeRoom)
@@ -793,7 +913,35 @@ extensions = [{
793
913
  typedArrayEncoder(78),
794
914
  typedArrayEncoder(79),
795
915
  typedArrayEncoder(81),
796
- typedArrayEncoder(82)]
916
+ typedArrayEncoder(82),
917
+ {
918
+ encode(sharedData, encode) { // write SharedData
919
+ let packedValues = sharedData.packedValues || []
920
+ let sharedStructures = sharedData.structures || []
921
+ if (packedValues.values.length > 0) {
922
+ target[position++] = 0xd8 // one-byte tag
923
+ target[position++] = 51 // tag 51 for packed shared structures https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
924
+ writeArrayHeader(4)
925
+ let valuesArray = packedValues.values
926
+ encode(valuesArray)
927
+ writeArrayHeader(0) // prefixes
928
+ writeArrayHeader(0) // suffixes
929
+ packedObjectMap = Object.create(sharedPackedObjectMap || null)
930
+ for (let i = 0, l = valuesArray.length; i < l; i++) {
931
+ packedObjectMap[valuesArray[i]] = i
932
+ }
933
+ }
934
+ if (sharedStructures) {
935
+ targetView.setUint32(position, 0xd9dffe00)
936
+ position += 3
937
+ let definitions = sharedStructures.slice(0)
938
+ definitions.unshift(0xe000)
939
+ definitions.push(new Tag(sharedData.version, 0x53687264))
940
+ encode(definitions)
941
+ } else
942
+ encode(new Tag(sharedData.version, 0x53687264))
943
+ }
944
+ }]
797
945
 
798
946
  function typedArrayEncoder(tag) {
799
947
  return {
@@ -833,27 +981,35 @@ function writeBuffer(buffer, makeRoom) {
833
981
  function insertIds(serialized, idsToInsert) {
834
982
  // insert the ids that need to be referenced for structured clones
835
983
  let nextId
836
- let distanceToMove = idsToInsert.length * 8
984
+ let distanceToMove = idsToInsert.length * 2
837
985
  let lastEnd = serialized.length - distanceToMove
838
986
  idsToInsert.sort((a, b) => a.offset > b.offset ? 1 : -1)
987
+ for (let id = 0; id < idsToInsert.length; id++) {
988
+ let referee = idsToInsert[id]
989
+ referee.id = id
990
+ for (let position of referee.references) {
991
+ serialized[position++] = id >> 8
992
+ serialized[position] = id & 0xff
993
+ }
994
+ }
839
995
  while (nextId = idsToInsert.pop()) {
840
996
  let offset = nextId.offset
841
- let id = nextId.id
842
997
  serialized.copyWithin(offset + distanceToMove, offset, lastEnd)
843
- distanceToMove -= 8
998
+ distanceToMove -= 2
844
999
  let position = offset + distanceToMove
845
- serialized[position++] = 0xd9
846
- serialized[position++] = 40009 >> 8
847
- serialized[position++] = 40009 & 0xff
848
- serialized[position++] = 0x1a // uint32
849
- serialized[position++] = id >> 24
850
- serialized[position++] = (id >> 16) & 0xff
851
- serialized[position++] = (id >> 8) & 0xff
852
- serialized[position++] = id & 0xff
1000
+ serialized[position++] = 0xd8
1001
+ serialized[position++] = 28 // http://cbor.schmorp.de/value-sharing
853
1002
  lastEnd = offset
854
1003
  }
855
1004
  return serialized
856
1005
  }
1006
+ function writeBundles(start, encode) {
1007
+ targetView.setUint32(bundledStrings.position + start, position - bundledStrings.position - start + 1) // the offset to bundle
1008
+ let writeStrings = bundledStrings
1009
+ bundledStrings = null
1010
+ encode(writeStrings[0])
1011
+ encode(writeStrings[1])
1012
+ }
857
1013
 
858
1014
  export function addExtension(extension) {
859
1015
  if (extension.Class) {
@@ -869,4 +1025,6 @@ export const encode = defaultEncoder.encode
869
1025
  export { FLOAT32_OPTIONS } from './decode.js'
870
1026
  import { FLOAT32_OPTIONS } from './decode.js'
871
1027
  export const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS
872
- export const REUSE_BUFFER_MODE = 1000
1028
+ export const REUSE_BUFFER_MODE = 512
1029
+ export const RESET_BUFFER_MODE = 1024
1030
+