cbor-x 1.3.3 → 1.4.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
@@ -1,1033 +1,1041 @@
1
- import { Decoder, mult10, Tag, typedArrays, addExtension as decodeAddExtension } from './decode.js'
2
- let textEncoder
3
- try {
4
- textEncoder = new TextEncoder()
5
- } catch (error) {}
6
- let extensions, extensionClasses
7
- const hasNodeBuffer = typeof Buffer !== 'undefined'
8
- const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array
9
- const ByteArray = hasNodeBuffer ? Buffer : Uint8Array
10
- const RECORD_INLINE_ID = 0xdfff // temporary first-come first-serve tag // proposed tag: 0x7265 // 're'
11
- const MAX_STRUCTURES = 0x100
12
- const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000
13
- let serializationId = 1
14
- let target
15
- let targetView
16
- let position = 0
17
- let safeEnd
18
- let bundledStrings = null
19
- const MAX_BUNDLE_SIZE = 0xf000
20
- const hasNonLatin = /[\u0080-\uFFFF]/
21
- const RECORD_SYMBOL = Symbol('record-id')
22
- export class Encoder extends Decoder {
23
- constructor(options) {
24
- super(options)
25
- this.offset = 0
26
- let typeBuffer
27
- let start
28
- let sharedStructures
29
- let hasSharedUpdate
30
- let structures
31
- let referenceMap
32
- options = options || {}
33
- let encodeUtf8 = ByteArray.prototype.utf8Write ? function(string, position, maxBytes) {
34
- return target.utf8Write(string, position, maxBytes)
35
- } : (textEncoder && textEncoder.encodeInto) ?
36
- function(string, position) {
37
- return textEncoder.encodeInto(string, target.subarray(position)).written
38
- } : false
39
-
40
- let encoder = this
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')
47
- let isSequential = options.sequential
48
- if (isSequential) {
49
- maxSharedStructures = 0
50
- }
51
- if (!this.structures)
52
- this.structures = []
53
- if (this.saveStructures)
54
- this.saveShared = this.saveStructures
55
- let samplingPackedValues, packedObjectMap, sharedValues = options.sharedValues
56
- let sharedPackedObjectMap
57
- if (sharedValues) {
58
- sharedPackedObjectMap = Object.create(null)
59
- for (let i = 0, l = sharedValues.length; i < l; i++) {
60
- sharedPackedObjectMap[sharedValues[i]] = i
61
- }
62
- }
63
- let recordIdsToRemove = []
64
- let transitionsCount = 0
65
- let serializationsSinceTransitionRebuild = 0
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) {
85
- if (!target) {
86
- target = new ByteArrayAllocate(8192)
87
- targetView = new DataView(target.buffer, 0, 8192)
88
- position = 0
89
- }
90
- safeEnd = target.length - 10
91
- if (safeEnd - position < 0x800) {
92
- // don't start too close to the end,
93
- target = new ByteArrayAllocate(target.length)
94
- targetView = new DataView(target.buffer, 0, target.length)
95
- safeEnd = target.length - 10
96
- position = 0
97
- } else if (encodeOptions === REUSE_BUFFER_MODE)
98
- position = (position + 7) & 0x7ffffff8 // Word align to make any future copying of this buffer faster
99
- start = position
100
- if (encoder.useSelfDescribedHeader) {
101
- targetView.setUint32(position, 0xd9d9f700) // tag two byte, then self-descriptive tag
102
- position += 3
103
- }
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
-
111
- sharedStructures = encoder.structures
112
- if (sharedStructures) {
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
- }
124
- let sharedStructuresLength = sharedStructures.length
125
- if (sharedStructuresLength > maxSharedStructures && !isSequential)
126
- sharedStructuresLength = maxSharedStructures
127
- if (!sharedStructures.transitions) {
128
- // rebuild our structure transitions
129
- sharedStructures.transitions = Object.create(null)
130
- for (let i = 0; i < sharedStructuresLength; i++) {
131
- let keys = sharedStructures[i]
132
- //console.log('shared struct keys:', keys)
133
- if (!keys)
134
- continue
135
- let nextTransition, transition = sharedStructures.transitions
136
- for (let j = 0, l = keys.length; j < l; j++) {
137
- if (transition[RECORD_SYMBOL] === undefined)
138
- transition[RECORD_SYMBOL] = i
139
- let key = keys[j]
140
- nextTransition = transition[key]
141
- if (!nextTransition) {
142
- nextTransition = transition[key] = Object.create(null)
143
- }
144
- transition = nextTransition
145
- }
146
- transition[RECORD_SYMBOL] = i | 0x100000
147
- }
148
- }
149
- if (!isSequential)
150
- sharedStructures.nextId = sharedStructuresLength
151
- }
152
- if (hasSharedUpdate)
153
- hasSharedUpdate = false
154
- structures = sharedStructures || []
155
- packedObjectMap = sharedPackedObjectMap
156
- if (options.pack) {
157
- let packedValues = new Map()
158
- packedValues.values = []
159
- packedValues.encoder = encoder
160
- packedValues.maxValues = options.maxPrivatePackedValues || (sharedPackedObjectMap ? 16 : Infinity)
161
- packedValues.objectMap = sharedPackedObjectMap || false
162
- packedValues.samplingPackedValues = samplingPackedValues
163
- findRepetitiveStrings(value, packedValues)
164
- if (packedValues.values.length > 0) {
165
- target[position++] = 0xd8 // one-byte tag
166
- target[position++] = 51 // tag 51 for packed shared structures https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
167
- writeArrayHeader(4)
168
- let valuesArray = packedValues.values
169
- encode(valuesArray)
170
- writeArrayHeader(0) // prefixes
171
- writeArrayHeader(0) // suffixes
172
- packedObjectMap = Object.create(sharedPackedObjectMap || null)
173
- for (let i = 0, l = valuesArray.length; i < l; i++) {
174
- packedObjectMap[valuesArray[i]] = i
175
- }
176
- }
177
- }
178
- try {
179
- encode(value)
180
- if (bundledStrings) {
181
- writeBundles(start, encode)
182
- }
183
- encoder.offset = position // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
184
- if (referenceMap && referenceMap.idsToInsert) {
185
- position += referenceMap.idsToInsert.length * 2
186
- if (position > safeEnd)
187
- makeRoom(position)
188
- encoder.offset = position
189
- let serialized = insertIds(target.subarray(start, position), referenceMap.idsToInsert)
190
- referenceMap = null
191
- return serialized
192
- }
193
- if (encodeOptions & REUSE_BUFFER_MODE) {
194
- target.start = start
195
- target.end = position
196
- return target
197
- }
198
- return target.subarray(start, position) // position can change if we call encode again in saveShared, so we get the buffer now
199
- } finally {
200
- if (sharedStructures) {
201
- if (serializationsSinceTransitionRebuild < 10)
202
- serializationsSinceTransitionRebuild++
203
- if (sharedStructures.length > maxSharedStructures)
204
- sharedStructures.length = maxSharedStructures
205
- if (transitionsCount > 10000) {
206
- // force a rebuild occasionally after a lot of transitions so it can get cleaned up
207
- sharedStructures.transitions = null
208
- serializationsSinceTransitionRebuild = 0
209
- transitionsCount = 0
210
- if (recordIdsToRemove.length > 0)
211
- recordIdsToRemove = []
212
- } else if (recordIdsToRemove.length > 0 && !isSequential) {
213
- for (let i = 0, l = recordIdsToRemove.length; i < l; i++) {
214
- recordIdsToRemove[i][RECORD_SYMBOL] = undefined
215
- }
216
- recordIdsToRemove = []
217
- //sharedStructures.nextId = maxSharedStructures
218
- }
219
- }
220
- if (hasSharedUpdate && encoder.saveShared) {
221
- if (encoder.structures.length > maxSharedStructures) {
222
- encoder.structures = encoder.structures.slice(0, maxSharedStructures)
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
229
- }
230
- if (encodeOptions & RESET_BUFFER_MODE)
231
- position = start
232
- }
233
- }
234
- this.findCommonStringsToPack = () => {
235
- samplingPackedValues = new Map()
236
- if (!sharedPackedObjectMap)
237
- sharedPackedObjectMap = Object.create(null)
238
- return (options) => {
239
- let threshold = options && options.threshold || 4
240
- let position = this.pack ? options.maxPrivatePackedValues || 16 : 0
241
- if (!sharedValues)
242
- sharedValues = this.sharedValues = []
243
- for (let [ key, status ] of samplingPackedValues) {
244
- if (status.count > threshold) {
245
- sharedPackedObjectMap[key] = position++
246
- sharedValues.push(key)
247
- hasSharedUpdate = true
248
- }
249
- }
250
- while (this.saveShared && this.updateSharedData() === false) {}
251
- samplingPackedValues = null
252
- }
253
- }
254
- const encode = (value) => {
255
- if (position > safeEnd)
256
- target = makeRoom(position)
257
-
258
- var type = typeof value
259
- var length
260
- if (type === 'string') {
261
- if (packedObjectMap) {
262
- let packedPosition = packedObjectMap[value]
263
- if (packedPosition >= 0) {
264
- if (packedPosition < 16)
265
- target[position++] = packedPosition + 0xe0 // simple values, defined in https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
266
- else {
267
- target[position++] = 0xc6 // tag 6 defined in https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
268
- if (packedPosition & 1)
269
- encode((15 - packedPosition) >> 1)
270
- else
271
- encode((packedPosition - 16) >> 1)
272
- }
273
- return
274
- /* } else if (packedStatus.serializationId != serializationId) {
275
- packedStatus.serializationId = serializationId
276
- packedStatus.count = 1
277
- if (options.sharedPack) {
278
- let sharedCount = packedStatus.sharedCount = (packedStatus.sharedCount || 0) + 1
279
- if (shareCount > (options.sharedPack.threshold || 5)) {
280
- let sharedPosition = packedStatus.position = packedStatus.nextSharedPosition
281
- hasSharedUpdate = true
282
- if (sharedPosition < 16)
283
- target[position++] = sharedPosition + 0xc0
284
-
285
- }
286
- }
287
- } // else any in-doc incrementation?*/
288
- } else if (samplingPackedValues && !options.pack) {
289
- let status = samplingPackedValues.get(value)
290
- if (status)
291
- status.count++
292
- else
293
- samplingPackedValues.set(value, {
294
- count: 1,
295
- })
296
- }
297
- }
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
- }
326
- let headerSize
327
- // first we estimate the header size, so we can write to the correct location
328
- if (strLength < 0x20) {
329
- headerSize = 1
330
- } else if (strLength < 0x100) {
331
- headerSize = 2
332
- } else if (strLength < 0x10000) {
333
- headerSize = 3
334
- } else {
335
- headerSize = 5
336
- }
337
- let maxBytes = strLength * 3
338
- if (position + maxBytes > safeEnd)
339
- target = makeRoom(position + maxBytes)
340
-
341
- if (strLength < 0x40 || !encodeUtf8) {
342
- let i, c1, c2, strPosition = position + headerSize
343
- for (i = 0; i < strLength; i++) {
344
- c1 = value.charCodeAt(i)
345
- if (c1 < 0x80) {
346
- target[strPosition++] = c1
347
- } else if (c1 < 0x800) {
348
- target[strPosition++] = c1 >> 6 | 0xc0
349
- target[strPosition++] = c1 & 0x3f | 0x80
350
- } else if (
351
- (c1 & 0xfc00) === 0xd800 &&
352
- ((c2 = value.charCodeAt(i + 1)) & 0xfc00) === 0xdc00
353
- ) {
354
- c1 = 0x10000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff)
355
- i++
356
- target[strPosition++] = c1 >> 18 | 0xf0
357
- target[strPosition++] = c1 >> 12 & 0x3f | 0x80
358
- target[strPosition++] = c1 >> 6 & 0x3f | 0x80
359
- target[strPosition++] = c1 & 0x3f | 0x80
360
- } else {
361
- target[strPosition++] = c1 >> 12 | 0xe0
362
- target[strPosition++] = c1 >> 6 & 0x3f | 0x80
363
- target[strPosition++] = c1 & 0x3f | 0x80
364
- }
365
- }
366
- length = strPosition - position - headerSize
367
- } else {
368
- length = encodeUtf8(value, position + headerSize, maxBytes)
369
- }
370
-
371
- if (length < 0x18) {
372
- target[position++] = 0x60 | length
373
- } else if (length < 0x100) {
374
- if (headerSize < 2) {
375
- target.copyWithin(position + 2, position + 1, position + 1 + length)
376
- }
377
- target[position++] = 0x78
378
- target[position++] = length
379
- } else if (length < 0x10000) {
380
- if (headerSize < 3) {
381
- target.copyWithin(position + 3, position + 2, position + 2 + length)
382
- }
383
- target[position++] = 0x79
384
- target[position++] = length >> 8
385
- target[position++] = length & 0xff
386
- } else {
387
- if (headerSize < 5) {
388
- target.copyWithin(position + 5, position + 3, position + 3 + length)
389
- }
390
- target[position++] = 0x7a
391
- targetView.setUint32(position, length)
392
- position += 4
393
- }
394
- position += length
395
- } else if (type === 'number') {
396
- if (value >>> 0 === value) {// positive integer, 32-bit or less
397
- // positive uint
398
- if (value < 0x18) {
399
- target[position++] = value
400
- } else if (value < 0x100) {
401
- target[position++] = 0x18
402
- target[position++] = value
403
- } else if (value < 0x10000) {
404
- target[position++] = 0x19
405
- target[position++] = value >> 8
406
- target[position++] = value & 0xff
407
- } else {
408
- target[position++] = 0x1a
409
- targetView.setUint32(position, value)
410
- position += 4
411
- }
412
- } else if (value >> 0 === value) { // negative integer
413
- if (value >= -0x18) {
414
- target[position++] = 0x1f - value
415
- } else if (value >= -0x100) {
416
- target[position++] = 0x38
417
- target[position++] = ~value
418
- } else if (value >= -0x10000) {
419
- target[position++] = 0x39
420
- targetView.setUint16(position, ~value)
421
- position += 2
422
- } else {
423
- target[position++] = 0x3a
424
- targetView.setUint32(position, ~value)
425
- position += 4
426
- }
427
- } else {
428
- let useFloat32
429
- if ((useFloat32 = this.useFloat32) > 0 && value < 0x100000000 && value >= -0x80000000) {
430
- target[position++] = 0xfa
431
- targetView.setFloat32(position, value)
432
- let xShifted
433
- if (useFloat32 < 4 ||
434
- // this checks for rounding of numbers that were encoded in 32-bit float to nearest significant decimal digit that could be preserved
435
- ((xShifted = value * mult10[((target[position] & 0x7f) << 1) | (target[position + 1] >> 7)]) >> 0) === xShifted) {
436
- position += 4
437
- return
438
- } else
439
- position-- // move back into position for writing a double
440
- }
441
- target[position++] = 0xfb
442
- targetView.setFloat64(position, value)
443
- position += 8
444
- }
445
- } else if (type === 'object') {
446
- if (!value)
447
- target[position++] = 0xf6
448
- else {
449
- if (referenceMap) {
450
- let referee = referenceMap.get(value)
451
- if (referee) {
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) {
456
- let idsToInsert = referenceMap.idsToInsert || (referenceMap.idsToInsert = [])
457
- referee.references = []
458
- idsToInsert.push(referee)
459
- }
460
- referee.references.push(position - start)
461
- position += 2 // TODO: also support 32-bit
462
- return
463
- } else
464
- referenceMap.set(value, { offset: position - start })
465
- }
466
- let constructor = value.constructor
467
- if (constructor === Object) {
468
- writeObject(value, true)
469
- } else if (constructor === Array) {
470
- length = value.length
471
- if (length < 0x18) {
472
- target[position++] = 0x80 | length
473
- } else {
474
- writeArrayHeader(length)
475
- }
476
- for (let i = 0; i < length; i++) {
477
- encode(value[i])
478
- }
479
- } else if (constructor === Map) {
480
- if (this.mapsAsObjects ? this.useTag259ForMaps !== false : this.useTag259ForMaps) {
481
- // use Tag 259 (https://github.com/shanewholloway/js-cbor-codec/blob/master/docs/CBOR-259-spec--explicit-maps.md) for maps if the user wants it that way
482
- target[position++] = 0xd9
483
- target[position++] = 1
484
- target[position++] = 3
485
- }
486
- length = value.size
487
- if (length < 0x18) {
488
- target[position++] = 0xa0 | length
489
- } else if (length < 0x100) {
490
- target[position++] = 0xb8
491
- target[position++] = length
492
- } else if (length < 0x10000) {
493
- target[position++] = 0xb9
494
- target[position++] = length >> 8
495
- target[position++] = length & 0xff
496
- } else {
497
- target[position++] = 0xba
498
- targetView.setUint32(position, length)
499
- position += 4
500
- }
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
- }
511
- }
512
- } else {
513
- for (let i = 0, l = extensions.length; i < l; i++) {
514
- let extensionClass = extensionClasses[i]
515
- if (value instanceof extensionClass) {
516
- let extension = extensions[i]
517
- let tag = extension.tag
518
- if (tag == undefined)
519
- tag = extension.getTag && extension.getTag(value)
520
- if (tag < 0x18) {
521
- target[position++] = 0xc0 | tag
522
- } else if (tag < 0x100) {
523
- target[position++] = 0xd8
524
- target[position++] = tag
525
- } else if (tag < 0x10000) {
526
- target[position++] = 0xd9
527
- target[position++] = tag >> 8
528
- target[position++] = tag & 0xff
529
- } else if (tag > -1) {
530
- target[position++] = 0xda
531
- targetView.setUint32(position, tag)
532
- position += 4
533
- } // else undefined, don't write tag
534
- extension.encode.call(this, value, encode, makeRoom)
535
- return
536
- }
537
- }
538
- if (value[Symbol.iterator]) {
539
- target[position++] = 0x9f // indefinite length array
540
- for (let entry of value) {
541
- encode(entry)
542
- }
543
- target[position++] = 0xff // stop-code
544
- return
545
- }
546
- // no extension found, write as object
547
- writeObject(value, !value.hasOwnProperty) // if it doesn't have hasOwnProperty, don't do hasOwnProperty checks
548
- }
549
- }
550
- } else if (type === 'boolean') {
551
- target[position++] = value ? 0xf5 : 0xf4
552
- } else if (type === 'bigint') {
553
- if (value < (BigInt(1)<<BigInt(64)) && value >= 0) {
554
- // use an unsigned int as long as it fits
555
- target[position++] = 0x1b
556
- targetView.setBigUint64(position, value)
557
- } else if (value > -(BigInt(1)<<BigInt(64)) && value < 0) {
558
- // if we can fit an unsigned int, use that
559
- target[position++] = 0x3b
560
- targetView.setBigUint64(position, -value - BigInt(1))
561
- } else {
562
- // overflow
563
- if (this.largeBigIntToFloat) {
564
- target[position++] = 0xfb
565
- targetView.setFloat64(position, Number(value))
566
- } else {
567
- throw new RangeError(value + ' was too large to fit in CBOR 64-bit integer format, set largeBigIntToFloat to convert to float-64')
568
- }
569
- }
570
- position += 8
571
- } else if (type === 'undefined') {
572
- target[position++] = 0xf7
573
- } else {
574
- throw new Error('Unknown type: ' + type)
575
- }
576
- }
577
-
578
- const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => {
579
- // this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
580
- let keys = Object.keys(object)
581
- let vals = Object.values(object)
582
- let length = keys.length
583
- if (length < 0x18) {
584
- target[position++] = 0xa0 | length
585
- } else if (length < 0x100) {
586
- target[position++] = 0xb8
587
- target[position++] = length
588
- } else if (length < 0x10000) {
589
- target[position++] = 0xb9
590
- target[position++] = length >> 8
591
- target[position++] = length & 0xff
592
- } else {
593
- target[position++] = 0xba
594
- targetView.setUint32(position, length)
595
- position += 4
596
- }
597
- let key
598
- if (encoder.keyMap) {
599
- for (let i = 0; i < length; i++) {
600
- encode(encodeKey(keys[i]))
601
- encode(vals[i])
602
- }
603
- } else {
604
- for (let i = 0; i < length; i++) {
605
- encode(keys[i])
606
- encode(vals[i])
607
- }
608
- }
609
- } :
610
- (object, safePrototype) => {
611
- target[position++] = 0xb9 // always use map 16, so we can preallocate and set the length afterwards
612
- let objectOffset = position - start
613
- position += 2
614
- let size = 0
615
- if (encoder.keyMap) {
616
- for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
617
- encode(encoder.encodeKey(key))
618
- encode(object[key])
619
- size++
620
- }
621
- } else {
622
- for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
623
- encode(key)
624
- encode(object[key])
625
- size++
626
- }
627
- }
628
- target[objectOffset++ + start] = size >> 8
629
- target[objectOffset + start] = size & 0xff
630
- } :
631
- (object, safePrototype) => {
632
- let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
633
- let newTransitions = 0
634
- let length = 0
635
- let parentRecordId
636
- let keys
637
- if (this.keyMap) {
638
- keys = Object.keys(object).map(k => this.encodeKey(k))
639
- length = keys.length
640
- for (let i = 0; i < length; i++) {
641
- let key = keys[i]
642
- nextTransition = transition[key]
643
- if (!nextTransition) {
644
- nextTransition = transition[key] = Object.create(null)
645
- newTransitions++
646
- }
647
- transition = nextTransition
648
- }
649
- } else {
650
- for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
651
- nextTransition = transition[key]
652
- if (!nextTransition) {
653
- if (transition[RECORD_SYMBOL] & 0x100000) {// this indicates it is a brancheable/extendable terminal node, so we will use this record id and extend it
654
- parentRecordId = transition[RECORD_SYMBOL] & 0xffff
655
- }
656
- nextTransition = transition[key] = Object.create(null)
657
- newTransitions++
658
- }
659
- transition = nextTransition
660
- length++
661
- }
662
- }
663
- let recordId = transition[RECORD_SYMBOL]
664
- if (recordId !== undefined) {
665
- recordId &= 0xffff
666
- target[position++] = 0xd9
667
- target[position++] = (recordId >> 8) | 0xe0
668
- target[position++] = recordId & 0xff
669
- } else {
670
- if (!keys)
671
- keys = transition.__keys__ || (transition.__keys__ = Object.keys(object))
672
- if (parentRecordId === undefined) {
673
- recordId = structures.nextId++
674
- if (!recordId) {
675
- recordId = 0
676
- structures.nextId = 1
677
- }
678
- if (recordId >= MAX_STRUCTURES) {// cycle back around
679
- structures.nextId = (recordId = maxSharedStructures) + 1
680
- }
681
- } else {
682
- recordId = parentRecordId
683
- }
684
- structures[recordId] = keys
685
- if (recordId < maxSharedStructures) {
686
- target[position++] = 0xd9
687
- target[position++] = (recordId >> 8) | 0xe0
688
- target[position++] = recordId & 0xff
689
- transition = structures.transitions
690
- for (let i = 0; i < length; i++) {
691
- if (transition[RECORD_SYMBOL] === undefined || (transition[RECORD_SYMBOL] & 0x100000))
692
- transition[RECORD_SYMBOL] = recordId
693
- transition = transition[keys[i]]
694
- }
695
- transition[RECORD_SYMBOL] = recordId | 0x100000 // indicates it is a extendable terminal
696
- hasSharedUpdate = true
697
- } else {
698
- transition[RECORD_SYMBOL] = recordId
699
- targetView.setUint32(position, 0xd9dfff00) // tag two byte, then record definition id
700
- position += 3
701
- if (newTransitions)
702
- transitionsCount += serializationsSinceTransitionRebuild * newTransitions
703
- // record the removal of the id, we can maintain our shared structure
704
- if (recordIdsToRemove.length >= MAX_STRUCTURES - maxSharedStructures)
705
- recordIdsToRemove.shift()[RECORD_SYMBOL] = undefined // we are cycling back through, and have to remove old ones
706
- recordIdsToRemove.push(transition)
707
- writeArrayHeader(length + 2)
708
- encode(0xe000 + recordId)
709
- encode(keys)
710
- for (let v of Object.values(object)) encode(v)
711
- return
712
- }
713
- }
714
- if (length < 0x18) { // write the array header
715
- target[position++] = 0x80 | length
716
- } else {
717
- writeArrayHeader(length)
718
- }
719
- for (let key in object)
720
- if (safePrototype || object.hasOwnProperty(key))
721
- encode(object[key])
722
- }
723
- const makeRoom = (end) => {
724
- let newSize
725
- if (end > 0x1000000) {
726
- // special handling for really large buffers
727
- if ((end - start) > MAX_BUFFER_SIZE)
728
- throw new Error('Encoded buffer would be larger than maximum buffer size')
729
- newSize = Math.min(MAX_BUFFER_SIZE,
730
- Math.round(Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2), 0x400000) / 0x1000) * 0x1000)
731
- } else // faster handling for smaller buffers
732
- newSize = ((Math.max((end - start) << 2, target.length - 1) >> 12) + 1) << 12
733
- let newBuffer = new ByteArrayAllocate(newSize)
734
- targetView = new DataView(newBuffer.buffer, 0, newSize)
735
- if (target.copy)
736
- target.copy(newBuffer, 0, start, end)
737
- else
738
- newBuffer.set(target.slice(start, end))
739
- position -= start
740
- start = 0
741
- safeEnd = newBuffer.length - 10
742
- return target = newBuffer
743
- }
744
- }
745
- useBuffer(buffer) {
746
- // this means we are finished using our own buffer and we can write over it safely
747
- target = buffer
748
- targetView = new DataView(target.buffer, target.byteOffset, target.byteLength)
749
- position = 0
750
- }
751
- clearSharedData() {
752
- if (this.structures)
753
- this.structures = []
754
- if (this.sharedValues)
755
- this.sharedValues = undefined
756
- }
757
- updateSharedData() {
758
- let lastVersion = this.sharedVersion || 0
759
- this.sharedVersion = lastVersion + 1
760
- let structuresCopy = this.structures.slice(0)
761
- let sharedData = new SharedData(structuresCopy, this.sharedValues, this.sharedVersion)
762
- let saveResults = this.saveShared(sharedData,
763
- existingShared => (existingShared && existingShared.version || 0) == lastVersion)
764
- if (saveResults === false) {
765
- // get updated structures and try again if the update failed
766
- sharedData = this.getShared() || {}
767
- this.structures = sharedData.structures || []
768
- this.sharedValues = sharedData.packedValues
769
- this.sharedVersion = sharedData.version
770
- this.structures.nextId = this.structures.length
771
- } else {
772
- // restore structures
773
- structuresCopy.forEach((structure, i) => this.structures[i] = structure)
774
- }
775
- // 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
776
- return saveResults
777
- }
778
- }
779
- class SharedData {
780
- constructor(structures, values, version) {
781
- this.structures = structures
782
- this.packedValues = values
783
- this.version = version
784
- }
785
- }
786
-
787
- function writeArrayHeader(length) {
788
- if (length < 0x18)
789
- target[position++] = 0x80 | length
790
- else if (length < 0x100) {
791
- target[position++] = 0x98
792
- target[position++] = length
793
- } else if (length < 0x10000) {
794
- target[position++] = 0x99
795
- target[position++] = length >> 8
796
- target[position++] = length & 0xff
797
- } else {
798
- target[position++] = 0x9a
799
- targetView.setUint32(position, length)
800
- position += 4
801
- }
802
- }
803
-
804
- function findRepetitiveStrings(value, packedValues) {
805
- switch(typeof value) {
806
- case 'string':
807
- if (value.length > 3) {
808
- if (packedValues.objectMap[value] > -1 || packedValues.values.length >= packedValues.maxValues)
809
- return
810
- let packedStatus = packedValues.get(value)
811
- if (packedStatus) {
812
- if (++packedStatus.count == 2) {
813
- packedValues.values.push(value)
814
- }
815
- } else {
816
- packedValues.set(value, {
817
- count: 1,
818
- })
819
- if (packedValues.samplingPackedValues) {
820
- let status = packedValues.samplingPackedValues.get(value)
821
- if (status)
822
- status.count++
823
- else
824
- packedValues.samplingPackedValues.set(value, {
825
- count: 1,
826
- })
827
- }
828
- }
829
- }
830
- break
831
- case 'object':
832
- if (value) {
833
- if (value instanceof Array) {
834
- for (let i = 0, l = value.length; i < l; i++) {
835
- findRepetitiveStrings(value[i], packedValues)
836
- }
837
-
838
- } else {
839
- let includeKeys = !packedValues.encoder.useRecords
840
- for (var key in value) {
841
- if (value.hasOwnProperty(key)) {
842
- if (includeKeys)
843
- findRepetitiveStrings(key, packedValues)
844
- findRepetitiveStrings(value[key], packedValues)
845
- }
846
- }
847
- }
848
- }
849
- break
850
- case 'function': console.log(value)
851
- }
852
- }
853
- const isLittleEndianMachine = new Uint8Array(new Uint16Array([1]).buffer)[0] == 1
854
- extensionClasses = [ Date, Set, Error, RegExp, Tag, ArrayBuffer, ByteArray,
855
- Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array,
856
- typeof BigUint64Array == 'undefined' ? function() {} : BigUint64Array, Int8Array, Int16Array, Int32Array,
857
- typeof BigInt64Array == 'undefined' ? function() {} : BigInt64Array,
858
- Float32Array, Float64Array, SharedData]
859
-
860
- //Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/
861
- extensions = [{
862
- tag: 1,
863
- encode(date, encode) {
864
- let seconds = date.getTime() / 1000
865
- if ((this.useTimestamp32 || date.getMilliseconds() === 0) && seconds >= 0 && seconds < 0x100000000) {
866
- // Timestamp 32
867
- target[position++] = 0x1a
868
- targetView.setUint32(position, seconds)
869
- position += 4
870
- } else {
871
- // Timestamp float64
872
- target[position++] = 0xfb
873
- targetView.setFloat64(position, seconds)
874
- position += 8
875
- }
876
- }
877
- }, {
878
- tag: 258, // https://github.com/input-output-hk/cbor-sets-spec/blob/master/CBOR_SETS.md
879
- encode(set, encode) {
880
- let array = Array.from(set)
881
- encode(array)
882
- }
883
- }, {
884
- tag: 27, // http://cbor.schmorp.de/generic-object
885
- encode(error, encode) {
886
- encode([ error.name, error.message ])
887
- }
888
- }, {
889
- tag: 27, // http://cbor.schmorp.de/generic-object
890
- encode(regex, encode) {
891
- encode([ 'RegExp', regex.source, regex.flags ])
892
- }
893
- }, {
894
- getTag(tag) {
895
- return tag.tag
896
- },
897
- encode(tag, encode) {
898
- encode(tag.value)
899
- }
900
- }, {
901
- encode(arrayBuffer, encode, makeRoom) {
902
- writeBuffer(arrayBuffer, makeRoom)
903
- }
904
- }, {
905
- encode(arrayBuffer, encode, makeRoom) {
906
- writeBuffer(arrayBuffer, makeRoom)
907
- }
908
- }, typedArrayEncoder(64, 1),
909
- typedArrayEncoder(68, 1),
910
- typedArrayEncoder(69, 2),
911
- typedArrayEncoder(70, 4),
912
- typedArrayEncoder(71, 8),
913
- typedArrayEncoder(72, 1),
914
- typedArrayEncoder(77, 2),
915
- typedArrayEncoder(78, 4),
916
- typedArrayEncoder(79, 8),
917
- typedArrayEncoder(85, 4),
918
- typedArrayEncoder(86, 8),
919
- {
920
- encode(sharedData, encode) { // write SharedData
921
- let packedValues = sharedData.packedValues || []
922
- let sharedStructures = sharedData.structures || []
923
- if (packedValues.values.length > 0) {
924
- target[position++] = 0xd8 // one-byte tag
925
- target[position++] = 51 // tag 51 for packed shared structures https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
926
- writeArrayHeader(4)
927
- let valuesArray = packedValues.values
928
- encode(valuesArray)
929
- writeArrayHeader(0) // prefixes
930
- writeArrayHeader(0) // suffixes
931
- packedObjectMap = Object.create(sharedPackedObjectMap || null)
932
- for (let i = 0, l = valuesArray.length; i < l; i++) {
933
- packedObjectMap[valuesArray[i]] = i
934
- }
935
- }
936
- if (sharedStructures) {
937
- targetView.setUint32(position, 0xd9dffe00)
938
- position += 3
939
- let definitions = sharedStructures.slice(0)
940
- definitions.unshift(0xe000)
941
- definitions.push(new Tag(sharedData.version, 0x53687264))
942
- encode(definitions)
943
- } else
944
- encode(new Tag(sharedData.version, 0x53687264))
945
- }
946
- }]
947
- function typedArrayEncoder(tag, size) {
948
- if (!isLittleEndianMachine && size > 1)
949
- tag -= 4 // the big endian equivalents are 4 less
950
- return {
951
- tag: tag,
952
- encode: function writeExtBuffer(typedArray, encode) {
953
- let length = typedArray.byteLength
954
- let offset = typedArray.byteOffset || 0
955
- let buffer = typedArray.buffer || typedArray
956
- encode(hasNodeBuffer ? Buffer.from(buffer, offset, length) :
957
- new Uint8Array(buffer, offset, length))
958
- }
959
- }
960
- }
961
- function writeBuffer(buffer, makeRoom) {
962
- let length = buffer.byteLength
963
- if (length < 0x18) {
964
- target[position++] = 0x40 + length
965
- } else if (length < 0x100) {
966
- target[position++] = 0x58
967
- target[position++] = length
968
- } else if (length < 0x10000) {
969
- target[position++] = 0x59
970
- target[position++] = length >> 8
971
- target[position++] = length & 0xff
972
- } else {
973
- target[position++] = 0x5a
974
- targetView.setUint32(position, length)
975
- position += 4
976
- }
977
- if (position + length >= target.length) {
978
- makeRoom(position + length)
979
- }
980
- target.set(buffer, position)
981
- position += length
982
- }
983
-
984
- function insertIds(serialized, idsToInsert) {
985
- // insert the ids that need to be referenced for structured clones
986
- let nextId
987
- let distanceToMove = idsToInsert.length * 2
988
- let lastEnd = serialized.length - distanceToMove
989
- idsToInsert.sort((a, b) => a.offset > b.offset ? 1 : -1)
990
- for (let id = 0; id < idsToInsert.length; id++) {
991
- let referee = idsToInsert[id]
992
- referee.id = id
993
- for (let position of referee.references) {
994
- serialized[position++] = id >> 8
995
- serialized[position] = id & 0xff
996
- }
997
- }
998
- while (nextId = idsToInsert.pop()) {
999
- let offset = nextId.offset
1000
- serialized.copyWithin(offset + distanceToMove, offset, lastEnd)
1001
- distanceToMove -= 2
1002
- let position = offset + distanceToMove
1003
- serialized[position++] = 0xd8
1004
- serialized[position++] = 28 // http://cbor.schmorp.de/value-sharing
1005
- lastEnd = offset
1006
- }
1007
- return serialized
1008
- }
1009
- function writeBundles(start, encode) {
1010
- targetView.setUint32(bundledStrings.position + start, position - bundledStrings.position - start + 1) // the offset to bundle
1011
- let writeStrings = bundledStrings
1012
- bundledStrings = null
1013
- encode(writeStrings[0])
1014
- encode(writeStrings[1])
1015
- }
1016
-
1017
- export function addExtension(extension) {
1018
- if (extension.Class) {
1019
- if (!extension.encode)
1020
- throw new Error('Extension has no encode function')
1021
- extensionClasses.unshift(extension.Class)
1022
- extensions.unshift(extension)
1023
- }
1024
- decodeAddExtension(extension)
1025
- }
1026
- let defaultEncoder = new Encoder({ useRecords: false })
1027
- export const encode = defaultEncoder.encode
1028
- export { FLOAT32_OPTIONS } from './decode.js'
1029
- import { FLOAT32_OPTIONS } from './decode.js'
1030
- export const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS
1031
- export const REUSE_BUFFER_MODE = 512
1032
- export const RESET_BUFFER_MODE = 1024
1033
-
1
+ import { Decoder, mult10, Tag, typedArrays, addExtension as decodeAddExtension } from './decode.js'
2
+ let textEncoder
3
+ try {
4
+ textEncoder = new TextEncoder()
5
+ } catch (error) {}
6
+ let extensions, extensionClasses
7
+ const hasNodeBuffer = typeof Buffer !== 'undefined'
8
+ const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array
9
+ const ByteArray = hasNodeBuffer ? Buffer : Uint8Array
10
+ const RECORD_INLINE_ID = 0xdfff // temporary first-come first-serve tag // proposed tag: 0x7265 // 're'
11
+ const MAX_STRUCTURES = 0x100
12
+ const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000
13
+ let serializationId = 1
14
+ let target
15
+ let targetView
16
+ let position = 0
17
+ let safeEnd
18
+ let bundledStrings = null
19
+ const MAX_BUNDLE_SIZE = 0xf000
20
+ const hasNonLatin = /[\u0080-\uFFFF]/
21
+ const RECORD_SYMBOL = Symbol('record-id')
22
+ export class Encoder extends Decoder {
23
+ constructor(options) {
24
+ super(options)
25
+ this.offset = 0
26
+ let typeBuffer
27
+ let start
28
+ let sharedStructures
29
+ let hasSharedUpdate
30
+ let structures
31
+ let referenceMap
32
+ options = options || {}
33
+ let encodeUtf8 = ByteArray.prototype.utf8Write ? function(string, position, maxBytes) {
34
+ return target.utf8Write(string, position, maxBytes)
35
+ } : (textEncoder && textEncoder.encodeInto) ?
36
+ function(string, position) {
37
+ return textEncoder.encodeInto(string, target.subarray(position)).written
38
+ } : false
39
+
40
+ let encoder = this
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')
47
+ let isSequential = options.sequential
48
+ if (isSequential) {
49
+ maxSharedStructures = 0
50
+ }
51
+ if (!this.structures)
52
+ this.structures = []
53
+ if (this.saveStructures)
54
+ this.saveShared = this.saveStructures
55
+ let samplingPackedValues, packedObjectMap, sharedValues = options.sharedValues
56
+ let sharedPackedObjectMap
57
+ if (sharedValues) {
58
+ sharedPackedObjectMap = Object.create(null)
59
+ for (let i = 0, l = sharedValues.length; i < l; i++) {
60
+ sharedPackedObjectMap[sharedValues[i]] = i
61
+ }
62
+ }
63
+ let recordIdsToRemove = []
64
+ let transitionsCount = 0
65
+ let serializationsSinceTransitionRebuild = 0
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) {
85
+ if (!target) {
86
+ target = new ByteArrayAllocate(8192)
87
+ targetView = new DataView(target.buffer, 0, 8192)
88
+ position = 0
89
+ }
90
+ safeEnd = target.length - 10
91
+ if (safeEnd - position < 0x800) {
92
+ // don't start too close to the end,
93
+ target = new ByteArrayAllocate(target.length)
94
+ targetView = new DataView(target.buffer, 0, target.length)
95
+ safeEnd = target.length - 10
96
+ position = 0
97
+ } else if (encodeOptions === REUSE_BUFFER_MODE)
98
+ position = (position + 7) & 0x7ffffff8 // Word align to make any future copying of this buffer faster
99
+ start = position
100
+ if (encoder.useSelfDescribedHeader) {
101
+ targetView.setUint32(position, 0xd9d9f700) // tag two byte, then self-descriptive tag
102
+ position += 3
103
+ }
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
+
111
+ sharedStructures = encoder.structures
112
+ if (sharedStructures) {
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
+ }
124
+ let sharedStructuresLength = sharedStructures.length
125
+ if (sharedStructuresLength > maxSharedStructures && !isSequential)
126
+ sharedStructuresLength = maxSharedStructures
127
+ if (!sharedStructures.transitions) {
128
+ // rebuild our structure transitions
129
+ sharedStructures.transitions = Object.create(null)
130
+ for (let i = 0; i < sharedStructuresLength; i++) {
131
+ let keys = sharedStructures[i]
132
+ //console.log('shared struct keys:', keys)
133
+ if (!keys)
134
+ continue
135
+ let nextTransition, transition = sharedStructures.transitions
136
+ for (let j = 0, l = keys.length; j < l; j++) {
137
+ if (transition[RECORD_SYMBOL] === undefined)
138
+ transition[RECORD_SYMBOL] = i
139
+ let key = keys[j]
140
+ nextTransition = transition[key]
141
+ if (!nextTransition) {
142
+ nextTransition = transition[key] = Object.create(null)
143
+ }
144
+ transition = nextTransition
145
+ }
146
+ transition[RECORD_SYMBOL] = i | 0x100000
147
+ }
148
+ }
149
+ if (!isSequential)
150
+ sharedStructures.nextId = sharedStructuresLength
151
+ }
152
+ if (hasSharedUpdate)
153
+ hasSharedUpdate = false
154
+ structures = sharedStructures || []
155
+ packedObjectMap = sharedPackedObjectMap
156
+ if (options.pack) {
157
+ let packedValues = new Map()
158
+ packedValues.values = []
159
+ packedValues.encoder = encoder
160
+ packedValues.maxValues = options.maxPrivatePackedValues || (sharedPackedObjectMap ? 16 : Infinity)
161
+ packedValues.objectMap = sharedPackedObjectMap || false
162
+ packedValues.samplingPackedValues = samplingPackedValues
163
+ findRepetitiveStrings(value, packedValues)
164
+ if (packedValues.values.length > 0) {
165
+ target[position++] = 0xd8 // one-byte tag
166
+ target[position++] = 51 // tag 51 for packed shared structures https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
167
+ writeArrayHeader(4)
168
+ let valuesArray = packedValues.values
169
+ encode(valuesArray)
170
+ writeArrayHeader(0) // prefixes
171
+ writeArrayHeader(0) // suffixes
172
+ packedObjectMap = Object.create(sharedPackedObjectMap || null)
173
+ for (let i = 0, l = valuesArray.length; i < l; i++) {
174
+ packedObjectMap[valuesArray[i]] = i
175
+ }
176
+ }
177
+ }
178
+ try {
179
+ encode(value)
180
+ if (bundledStrings) {
181
+ writeBundles(start, encode)
182
+ }
183
+ encoder.offset = position // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
184
+ if (referenceMap && referenceMap.idsToInsert) {
185
+ position += referenceMap.idsToInsert.length * 2
186
+ if (position > safeEnd)
187
+ makeRoom(position)
188
+ encoder.offset = position
189
+ let serialized = insertIds(target.subarray(start, position), referenceMap.idsToInsert)
190
+ referenceMap = null
191
+ return serialized
192
+ }
193
+ if (encodeOptions & REUSE_BUFFER_MODE) {
194
+ target.start = start
195
+ target.end = position
196
+ return target
197
+ }
198
+ return target.subarray(start, position) // position can change if we call encode again in saveShared, so we get the buffer now
199
+ } finally {
200
+ if (sharedStructures) {
201
+ if (serializationsSinceTransitionRebuild < 10)
202
+ serializationsSinceTransitionRebuild++
203
+ if (sharedStructures.length > maxSharedStructures)
204
+ sharedStructures.length = maxSharedStructures
205
+ if (transitionsCount > 10000) {
206
+ // force a rebuild occasionally after a lot of transitions so it can get cleaned up
207
+ sharedStructures.transitions = null
208
+ serializationsSinceTransitionRebuild = 0
209
+ transitionsCount = 0
210
+ if (recordIdsToRemove.length > 0)
211
+ recordIdsToRemove = []
212
+ } else if (recordIdsToRemove.length > 0 && !isSequential) {
213
+ for (let i = 0, l = recordIdsToRemove.length; i < l; i++) {
214
+ recordIdsToRemove[i][RECORD_SYMBOL] = undefined
215
+ }
216
+ recordIdsToRemove = []
217
+ //sharedStructures.nextId = maxSharedStructures
218
+ }
219
+ }
220
+ if (hasSharedUpdate && encoder.saveShared) {
221
+ if (encoder.structures.length > maxSharedStructures) {
222
+ encoder.structures = encoder.structures.slice(0, maxSharedStructures)
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
229
+ }
230
+ if (encodeOptions & RESET_BUFFER_MODE)
231
+ position = start
232
+ }
233
+ }
234
+ this.findCommonStringsToPack = () => {
235
+ samplingPackedValues = new Map()
236
+ if (!sharedPackedObjectMap)
237
+ sharedPackedObjectMap = Object.create(null)
238
+ return (options) => {
239
+ let threshold = options && options.threshold || 4
240
+ let position = this.pack ? options.maxPrivatePackedValues || 16 : 0
241
+ if (!sharedValues)
242
+ sharedValues = this.sharedValues = []
243
+ for (let [ key, status ] of samplingPackedValues) {
244
+ if (status.count > threshold) {
245
+ sharedPackedObjectMap[key] = position++
246
+ sharedValues.push(key)
247
+ hasSharedUpdate = true
248
+ }
249
+ }
250
+ while (this.saveShared && this.updateSharedData() === false) {}
251
+ samplingPackedValues = null
252
+ }
253
+ }
254
+ const encode = (value) => {
255
+ if (position > safeEnd)
256
+ target = makeRoom(position)
257
+
258
+ var type = typeof value
259
+ var length
260
+ if (type === 'string') {
261
+ if (packedObjectMap) {
262
+ let packedPosition = packedObjectMap[value]
263
+ if (packedPosition >= 0) {
264
+ if (packedPosition < 16)
265
+ target[position++] = packedPosition + 0xe0 // simple values, defined in https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
266
+ else {
267
+ target[position++] = 0xc6 // tag 6 defined in https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
268
+ if (packedPosition & 1)
269
+ encode((15 - packedPosition) >> 1)
270
+ else
271
+ encode((packedPosition - 16) >> 1)
272
+ }
273
+ return
274
+ /* } else if (packedStatus.serializationId != serializationId) {
275
+ packedStatus.serializationId = serializationId
276
+ packedStatus.count = 1
277
+ if (options.sharedPack) {
278
+ let sharedCount = packedStatus.sharedCount = (packedStatus.sharedCount || 0) + 1
279
+ if (shareCount > (options.sharedPack.threshold || 5)) {
280
+ let sharedPosition = packedStatus.position = packedStatus.nextSharedPosition
281
+ hasSharedUpdate = true
282
+ if (sharedPosition < 16)
283
+ target[position++] = sharedPosition + 0xc0
284
+
285
+ }
286
+ }
287
+ } // else any in-doc incrementation?*/
288
+ } else if (samplingPackedValues && !options.pack) {
289
+ let status = samplingPackedValues.get(value)
290
+ if (status)
291
+ status.count++
292
+ else
293
+ samplingPackedValues.set(value, {
294
+ count: 1,
295
+ })
296
+ }
297
+ }
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
+ }
326
+ let headerSize
327
+ // first we estimate the header size, so we can write to the correct location
328
+ if (strLength < 0x20) {
329
+ headerSize = 1
330
+ } else if (strLength < 0x100) {
331
+ headerSize = 2
332
+ } else if (strLength < 0x10000) {
333
+ headerSize = 3
334
+ } else {
335
+ headerSize = 5
336
+ }
337
+ let maxBytes = strLength * 3
338
+ if (position + maxBytes > safeEnd)
339
+ target = makeRoom(position + maxBytes)
340
+
341
+ if (strLength < 0x40 || !encodeUtf8) {
342
+ let i, c1, c2, strPosition = position + headerSize
343
+ for (i = 0; i < strLength; i++) {
344
+ c1 = value.charCodeAt(i)
345
+ if (c1 < 0x80) {
346
+ target[strPosition++] = c1
347
+ } else if (c1 < 0x800) {
348
+ target[strPosition++] = c1 >> 6 | 0xc0
349
+ target[strPosition++] = c1 & 0x3f | 0x80
350
+ } else if (
351
+ (c1 & 0xfc00) === 0xd800 &&
352
+ ((c2 = value.charCodeAt(i + 1)) & 0xfc00) === 0xdc00
353
+ ) {
354
+ c1 = 0x10000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff)
355
+ i++
356
+ target[strPosition++] = c1 >> 18 | 0xf0
357
+ target[strPosition++] = c1 >> 12 & 0x3f | 0x80
358
+ target[strPosition++] = c1 >> 6 & 0x3f | 0x80
359
+ target[strPosition++] = c1 & 0x3f | 0x80
360
+ } else {
361
+ target[strPosition++] = c1 >> 12 | 0xe0
362
+ target[strPosition++] = c1 >> 6 & 0x3f | 0x80
363
+ target[strPosition++] = c1 & 0x3f | 0x80
364
+ }
365
+ }
366
+ length = strPosition - position - headerSize
367
+ } else {
368
+ length = encodeUtf8(value, position + headerSize, maxBytes)
369
+ }
370
+
371
+ if (length < 0x18) {
372
+ target[position++] = 0x60 | length
373
+ } else if (length < 0x100) {
374
+ if (headerSize < 2) {
375
+ target.copyWithin(position + 2, position + 1, position + 1 + length)
376
+ }
377
+ target[position++] = 0x78
378
+ target[position++] = length
379
+ } else if (length < 0x10000) {
380
+ if (headerSize < 3) {
381
+ target.copyWithin(position + 3, position + 2, position + 2 + length)
382
+ }
383
+ target[position++] = 0x79
384
+ target[position++] = length >> 8
385
+ target[position++] = length & 0xff
386
+ } else {
387
+ if (headerSize < 5) {
388
+ target.copyWithin(position + 5, position + 3, position + 3 + length)
389
+ }
390
+ target[position++] = 0x7a
391
+ targetView.setUint32(position, length)
392
+ position += 4
393
+ }
394
+ position += length
395
+ } else if (type === 'number') {
396
+ if (value >>> 0 === value) {// positive integer, 32-bit or less
397
+ // positive uint
398
+ if (value < 0x18) {
399
+ target[position++] = value
400
+ } else if (value < 0x100) {
401
+ target[position++] = 0x18
402
+ target[position++] = value
403
+ } else if (value < 0x10000) {
404
+ target[position++] = 0x19
405
+ target[position++] = value >> 8
406
+ target[position++] = value & 0xff
407
+ } else {
408
+ target[position++] = 0x1a
409
+ targetView.setUint32(position, value)
410
+ position += 4
411
+ }
412
+ } else if (value >> 0 === value) { // negative integer
413
+ if (value >= -0x18) {
414
+ target[position++] = 0x1f - value
415
+ } else if (value >= -0x100) {
416
+ target[position++] = 0x38
417
+ target[position++] = ~value
418
+ } else if (value >= -0x10000) {
419
+ target[position++] = 0x39
420
+ targetView.setUint16(position, ~value)
421
+ position += 2
422
+ } else {
423
+ target[position++] = 0x3a
424
+ targetView.setUint32(position, ~value)
425
+ position += 4
426
+ }
427
+ } else {
428
+ let useFloat32
429
+ if ((useFloat32 = this.useFloat32) > 0 && value < 0x100000000 && value >= -0x80000000) {
430
+ target[position++] = 0xfa
431
+ targetView.setFloat32(position, value)
432
+ let xShifted
433
+ if (useFloat32 < 4 ||
434
+ // this checks for rounding of numbers that were encoded in 32-bit float to nearest significant decimal digit that could be preserved
435
+ ((xShifted = value * mult10[((target[position] & 0x7f) << 1) | (target[position + 1] >> 7)]) >> 0) === xShifted) {
436
+ position += 4
437
+ return
438
+ } else
439
+ position-- // move back into position for writing a double
440
+ }
441
+ target[position++] = 0xfb
442
+ targetView.setFloat64(position, value)
443
+ position += 8
444
+ }
445
+ } else if (type === 'object') {
446
+ if (!value)
447
+ target[position++] = 0xf6
448
+ else {
449
+ if (referenceMap) {
450
+ let referee = referenceMap.get(value)
451
+ if (referee) {
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) {
456
+ let idsToInsert = referenceMap.idsToInsert || (referenceMap.idsToInsert = [])
457
+ referee.references = []
458
+ idsToInsert.push(referee)
459
+ }
460
+ referee.references.push(position - start)
461
+ position += 2 // TODO: also support 32-bit
462
+ return
463
+ } else
464
+ referenceMap.set(value, { offset: position - start })
465
+ }
466
+ let constructor = value.constructor
467
+ if (constructor === Object) {
468
+ writeObject(value, true)
469
+ } else if (constructor === Array) {
470
+ length = value.length
471
+ if (length < 0x18) {
472
+ target[position++] = 0x80 | length
473
+ } else {
474
+ writeArrayHeader(length)
475
+ }
476
+ for (let i = 0; i < length; i++) {
477
+ encode(value[i])
478
+ }
479
+ } else if (constructor === Map) {
480
+ if (this.mapsAsObjects ? this.useTag259ForMaps !== false : this.useTag259ForMaps) {
481
+ // use Tag 259 (https://github.com/shanewholloway/js-cbor-codec/blob/master/docs/CBOR-259-spec--explicit-maps.md) for maps if the user wants it that way
482
+ target[position++] = 0xd9
483
+ target[position++] = 1
484
+ target[position++] = 3
485
+ }
486
+ length = value.size
487
+ if (length < 0x18) {
488
+ target[position++] = 0xa0 | length
489
+ } else if (length < 0x100) {
490
+ target[position++] = 0xb8
491
+ target[position++] = length
492
+ } else if (length < 0x10000) {
493
+ target[position++] = 0xb9
494
+ target[position++] = length >> 8
495
+ target[position++] = length & 0xff
496
+ } else {
497
+ target[position++] = 0xba
498
+ targetView.setUint32(position, length)
499
+ position += 4
500
+ }
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
+ }
511
+ }
512
+ } else {
513
+ for (let i = 0, l = extensions.length; i < l; i++) {
514
+ let extensionClass = extensionClasses[i]
515
+ if (value instanceof extensionClass) {
516
+ let extension = extensions[i]
517
+ let tag = extension.tag
518
+ if (tag == undefined)
519
+ tag = extension.getTag && extension.getTag.call(this, value)
520
+ if (tag < 0x18) {
521
+ target[position++] = 0xc0 | tag
522
+ } else if (tag < 0x100) {
523
+ target[position++] = 0xd8
524
+ target[position++] = tag
525
+ } else if (tag < 0x10000) {
526
+ target[position++] = 0xd9
527
+ target[position++] = tag >> 8
528
+ target[position++] = tag & 0xff
529
+ } else if (tag > -1) {
530
+ target[position++] = 0xda
531
+ targetView.setUint32(position, tag)
532
+ position += 4
533
+ } // else undefined, don't write tag
534
+ extension.encode.call(this, value, encode, makeRoom)
535
+ return
536
+ }
537
+ }
538
+ if (value[Symbol.iterator]) {
539
+ target[position++] = 0x9f // indefinite length array
540
+ for (let entry of value) {
541
+ encode(entry)
542
+ }
543
+ target[position++] = 0xff // stop-code
544
+ return
545
+ }
546
+ // no extension found, write as object
547
+ writeObject(value, !value.hasOwnProperty) // if it doesn't have hasOwnProperty, don't do hasOwnProperty checks
548
+ }
549
+ }
550
+ } else if (type === 'boolean') {
551
+ target[position++] = value ? 0xf5 : 0xf4
552
+ } else if (type === 'bigint') {
553
+ if (value < (BigInt(1)<<BigInt(64)) && value >= 0) {
554
+ // use an unsigned int as long as it fits
555
+ target[position++] = 0x1b
556
+ targetView.setBigUint64(position, value)
557
+ } else if (value > -(BigInt(1)<<BigInt(64)) && value < 0) {
558
+ // if we can fit an unsigned int, use that
559
+ target[position++] = 0x3b
560
+ targetView.setBigUint64(position, -value - BigInt(1))
561
+ } else {
562
+ // overflow
563
+ if (this.largeBigIntToFloat) {
564
+ target[position++] = 0xfb
565
+ targetView.setFloat64(position, Number(value))
566
+ } else {
567
+ throw new RangeError(value + ' was too large to fit in CBOR 64-bit integer format, set largeBigIntToFloat to convert to float-64')
568
+ }
569
+ }
570
+ position += 8
571
+ } else if (type === 'undefined') {
572
+ target[position++] = 0xf7
573
+ } else {
574
+ throw new Error('Unknown type: ' + type)
575
+ }
576
+ }
577
+
578
+ const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => {
579
+ // this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
580
+ let keys = Object.keys(object)
581
+ let vals = Object.values(object)
582
+ let length = keys.length
583
+ if (length < 0x18) {
584
+ target[position++] = 0xa0 | length
585
+ } else if (length < 0x100) {
586
+ target[position++] = 0xb8
587
+ target[position++] = length
588
+ } else if (length < 0x10000) {
589
+ target[position++] = 0xb9
590
+ target[position++] = length >> 8
591
+ target[position++] = length & 0xff
592
+ } else {
593
+ target[position++] = 0xba
594
+ targetView.setUint32(position, length)
595
+ position += 4
596
+ }
597
+ let key
598
+ if (encoder.keyMap) {
599
+ for (let i = 0; i < length; i++) {
600
+ encode(encodeKey(keys[i]))
601
+ encode(vals[i])
602
+ }
603
+ } else {
604
+ for (let i = 0; i < length; i++) {
605
+ encode(keys[i])
606
+ encode(vals[i])
607
+ }
608
+ }
609
+ } :
610
+ (object, safePrototype) => {
611
+ target[position++] = 0xb9 // always use map 16, so we can preallocate and set the length afterwards
612
+ let objectOffset = position - start
613
+ position += 2
614
+ let size = 0
615
+ if (encoder.keyMap) {
616
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
617
+ encode(encoder.encodeKey(key))
618
+ encode(object[key])
619
+ size++
620
+ }
621
+ } else {
622
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
623
+ encode(key)
624
+ encode(object[key])
625
+ size++
626
+ }
627
+ }
628
+ target[objectOffset++ + start] = size >> 8
629
+ target[objectOffset + start] = size & 0xff
630
+ } :
631
+ (object, safePrototype) => {
632
+ let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
633
+ let newTransitions = 0
634
+ let length = 0
635
+ let parentRecordId
636
+ let keys
637
+ if (this.keyMap) {
638
+ keys = Object.keys(object).map(k => this.encodeKey(k))
639
+ length = keys.length
640
+ for (let i = 0; i < length; i++) {
641
+ let key = keys[i]
642
+ nextTransition = transition[key]
643
+ if (!nextTransition) {
644
+ nextTransition = transition[key] = Object.create(null)
645
+ newTransitions++
646
+ }
647
+ transition = nextTransition
648
+ }
649
+ } else {
650
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
651
+ nextTransition = transition[key]
652
+ if (!nextTransition) {
653
+ if (transition[RECORD_SYMBOL] & 0x100000) {// this indicates it is a brancheable/extendable terminal node, so we will use this record id and extend it
654
+ parentRecordId = transition[RECORD_SYMBOL] & 0xffff
655
+ }
656
+ nextTransition = transition[key] = Object.create(null)
657
+ newTransitions++
658
+ }
659
+ transition = nextTransition
660
+ length++
661
+ }
662
+ }
663
+ let recordId = transition[RECORD_SYMBOL]
664
+ if (recordId !== undefined) {
665
+ recordId &= 0xffff
666
+ target[position++] = 0xd9
667
+ target[position++] = (recordId >> 8) | 0xe0
668
+ target[position++] = recordId & 0xff
669
+ } else {
670
+ if (!keys)
671
+ keys = transition.__keys__ || (transition.__keys__ = Object.keys(object))
672
+ if (parentRecordId === undefined) {
673
+ recordId = structures.nextId++
674
+ if (!recordId) {
675
+ recordId = 0
676
+ structures.nextId = 1
677
+ }
678
+ if (recordId >= MAX_STRUCTURES) {// cycle back around
679
+ structures.nextId = (recordId = maxSharedStructures) + 1
680
+ }
681
+ } else {
682
+ recordId = parentRecordId
683
+ }
684
+ structures[recordId] = keys
685
+ if (recordId < maxSharedStructures) {
686
+ target[position++] = 0xd9
687
+ target[position++] = (recordId >> 8) | 0xe0
688
+ target[position++] = recordId & 0xff
689
+ transition = structures.transitions
690
+ for (let i = 0; i < length; i++) {
691
+ if (transition[RECORD_SYMBOL] === undefined || (transition[RECORD_SYMBOL] & 0x100000))
692
+ transition[RECORD_SYMBOL] = recordId
693
+ transition = transition[keys[i]]
694
+ }
695
+ transition[RECORD_SYMBOL] = recordId | 0x100000 // indicates it is a extendable terminal
696
+ hasSharedUpdate = true
697
+ } else {
698
+ transition[RECORD_SYMBOL] = recordId
699
+ targetView.setUint32(position, 0xd9dfff00) // tag two byte, then record definition id
700
+ position += 3
701
+ if (newTransitions)
702
+ transitionsCount += serializationsSinceTransitionRebuild * newTransitions
703
+ // record the removal of the id, we can maintain our shared structure
704
+ if (recordIdsToRemove.length >= MAX_STRUCTURES - maxSharedStructures)
705
+ recordIdsToRemove.shift()[RECORD_SYMBOL] = undefined // we are cycling back through, and have to remove old ones
706
+ recordIdsToRemove.push(transition)
707
+ writeArrayHeader(length + 2)
708
+ encode(0xe000 + recordId)
709
+ encode(keys)
710
+ for (let v of Object.values(object)) encode(v)
711
+ return
712
+ }
713
+ }
714
+ if (length < 0x18) { // write the array header
715
+ target[position++] = 0x80 | length
716
+ } else {
717
+ writeArrayHeader(length)
718
+ }
719
+ for (let key in object)
720
+ if (safePrototype || object.hasOwnProperty(key))
721
+ encode(object[key])
722
+ }
723
+ const makeRoom = (end) => {
724
+ let newSize
725
+ if (end > 0x1000000) {
726
+ // special handling for really large buffers
727
+ if ((end - start) > MAX_BUFFER_SIZE)
728
+ throw new Error('Encoded buffer would be larger than maximum buffer size')
729
+ newSize = Math.min(MAX_BUFFER_SIZE,
730
+ Math.round(Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2), 0x400000) / 0x1000) * 0x1000)
731
+ } else // faster handling for smaller buffers
732
+ newSize = ((Math.max((end - start) << 2, target.length - 1) >> 12) + 1) << 12
733
+ let newBuffer = new ByteArrayAllocate(newSize)
734
+ targetView = new DataView(newBuffer.buffer, 0, newSize)
735
+ if (target.copy)
736
+ target.copy(newBuffer, 0, start, end)
737
+ else
738
+ newBuffer.set(target.slice(start, end))
739
+ position -= start
740
+ start = 0
741
+ safeEnd = newBuffer.length - 10
742
+ return target = newBuffer
743
+ }
744
+ }
745
+ useBuffer(buffer) {
746
+ // this means we are finished using our own buffer and we can write over it safely
747
+ target = buffer
748
+ targetView = new DataView(target.buffer, target.byteOffset, target.byteLength)
749
+ position = 0
750
+ }
751
+ clearSharedData() {
752
+ if (this.structures)
753
+ this.structures = []
754
+ if (this.sharedValues)
755
+ this.sharedValues = undefined
756
+ }
757
+ updateSharedData() {
758
+ let lastVersion = this.sharedVersion || 0
759
+ this.sharedVersion = lastVersion + 1
760
+ let structuresCopy = this.structures.slice(0)
761
+ let sharedData = new SharedData(structuresCopy, this.sharedValues, this.sharedVersion)
762
+ let saveResults = this.saveShared(sharedData,
763
+ existingShared => (existingShared && existingShared.version || 0) == lastVersion)
764
+ if (saveResults === false) {
765
+ // get updated structures and try again if the update failed
766
+ sharedData = this.getShared() || {}
767
+ this.structures = sharedData.structures || []
768
+ this.sharedValues = sharedData.packedValues
769
+ this.sharedVersion = sharedData.version
770
+ this.structures.nextId = this.structures.length
771
+ } else {
772
+ // restore structures
773
+ structuresCopy.forEach((structure, i) => this.structures[i] = structure)
774
+ }
775
+ // 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
776
+ return saveResults
777
+ }
778
+ }
779
+ class SharedData {
780
+ constructor(structures, values, version) {
781
+ this.structures = structures
782
+ this.packedValues = values
783
+ this.version = version
784
+ }
785
+ }
786
+
787
+ function writeArrayHeader(length) {
788
+ if (length < 0x18)
789
+ target[position++] = 0x80 | length
790
+ else if (length < 0x100) {
791
+ target[position++] = 0x98
792
+ target[position++] = length
793
+ } else if (length < 0x10000) {
794
+ target[position++] = 0x99
795
+ target[position++] = length >> 8
796
+ target[position++] = length & 0xff
797
+ } else {
798
+ target[position++] = 0x9a
799
+ targetView.setUint32(position, length)
800
+ position += 4
801
+ }
802
+ }
803
+
804
+ function findRepetitiveStrings(value, packedValues) {
805
+ switch(typeof value) {
806
+ case 'string':
807
+ if (value.length > 3) {
808
+ if (packedValues.objectMap[value] > -1 || packedValues.values.length >= packedValues.maxValues)
809
+ return
810
+ let packedStatus = packedValues.get(value)
811
+ if (packedStatus) {
812
+ if (++packedStatus.count == 2) {
813
+ packedValues.values.push(value)
814
+ }
815
+ } else {
816
+ packedValues.set(value, {
817
+ count: 1,
818
+ })
819
+ if (packedValues.samplingPackedValues) {
820
+ let status = packedValues.samplingPackedValues.get(value)
821
+ if (status)
822
+ status.count++
823
+ else
824
+ packedValues.samplingPackedValues.set(value, {
825
+ count: 1,
826
+ })
827
+ }
828
+ }
829
+ }
830
+ break
831
+ case 'object':
832
+ if (value) {
833
+ if (value instanceof Array) {
834
+ for (let i = 0, l = value.length; i < l; i++) {
835
+ findRepetitiveStrings(value[i], packedValues)
836
+ }
837
+
838
+ } else {
839
+ let includeKeys = !packedValues.encoder.useRecords
840
+ for (var key in value) {
841
+ if (value.hasOwnProperty(key)) {
842
+ if (includeKeys)
843
+ findRepetitiveStrings(key, packedValues)
844
+ findRepetitiveStrings(value[key], packedValues)
845
+ }
846
+ }
847
+ }
848
+ }
849
+ break
850
+ case 'function': console.log(value)
851
+ }
852
+ }
853
+ const isLittleEndianMachine = new Uint8Array(new Uint16Array([1]).buffer)[0] == 1
854
+ extensionClasses = [ Date, Set, Error, RegExp, Tag, ArrayBuffer,
855
+ Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array,
856
+ typeof BigUint64Array == 'undefined' ? function() {} : BigUint64Array, Int8Array, Int16Array, Int32Array,
857
+ typeof BigInt64Array == 'undefined' ? function() {} : BigInt64Array,
858
+ Float32Array, Float64Array, SharedData ]
859
+
860
+ //Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/
861
+ extensions = [{ // Date
862
+ tag: 1,
863
+ encode(date, encode) {
864
+ let seconds = date.getTime() / 1000
865
+ if ((this.useTimestamp32 || date.getMilliseconds() === 0) && seconds >= 0 && seconds < 0x100000000) {
866
+ // Timestamp 32
867
+ target[position++] = 0x1a
868
+ targetView.setUint32(position, seconds)
869
+ position += 4
870
+ } else {
871
+ // Timestamp float64
872
+ target[position++] = 0xfb
873
+ targetView.setFloat64(position, seconds)
874
+ position += 8
875
+ }
876
+ }
877
+ }, { // Set
878
+ tag: 258, // https://github.com/input-output-hk/cbor-sets-spec/blob/master/CBOR_SETS.md
879
+ encode(set, encode) {
880
+ let array = Array.from(set)
881
+ encode(array)
882
+ }
883
+ }, { // Error
884
+ tag: 27, // http://cbor.schmorp.de/generic-object
885
+ encode(error, encode) {
886
+ encode([ error.name, error.message ])
887
+ }
888
+ }, { // RegExp
889
+ tag: 27, // http://cbor.schmorp.de/generic-object
890
+ encode(regex, encode) {
891
+ encode([ 'RegExp', regex.source, regex.flags ])
892
+ }
893
+ }, { // Tag
894
+ getTag(tag) {
895
+ return tag.tag
896
+ },
897
+ encode(tag, encode) {
898
+ encode(tag.value)
899
+ }
900
+ }, { // ArrayBuffer
901
+ encode(arrayBuffer, encode, makeRoom) {
902
+ writeBuffer(arrayBuffer, makeRoom)
903
+ }
904
+ }, { // Uint8Array
905
+ getTag(typedArray) {
906
+ if (typedArray.constructor === Uint8Array) {
907
+ if (this.tagUint8Array || hasNodeBuffer && this.tagUint8Array !== false)
908
+ return 64;
909
+ } // else no tag
910
+ },
911
+ encode(typedArray, encode, makeRoom) {
912
+ writeBuffer(typedArray, makeRoom)
913
+ }
914
+ },
915
+ typedArrayEncoder(68, 1),
916
+ typedArrayEncoder(69, 2),
917
+ typedArrayEncoder(70, 4),
918
+ typedArrayEncoder(71, 8),
919
+ typedArrayEncoder(72, 1),
920
+ typedArrayEncoder(77, 2),
921
+ typedArrayEncoder(78, 4),
922
+ typedArrayEncoder(79, 8),
923
+ typedArrayEncoder(85, 4),
924
+ typedArrayEncoder(86, 8),
925
+ {
926
+ encode(sharedData, encode) { // write SharedData
927
+ let packedValues = sharedData.packedValues || []
928
+ let sharedStructures = sharedData.structures || []
929
+ if (packedValues.values.length > 0) {
930
+ target[position++] = 0xd8 // one-byte tag
931
+ target[position++] = 51 // tag 51 for packed shared structures https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
932
+ writeArrayHeader(4)
933
+ let valuesArray = packedValues.values
934
+ encode(valuesArray)
935
+ writeArrayHeader(0) // prefixes
936
+ writeArrayHeader(0) // suffixes
937
+ packedObjectMap = Object.create(sharedPackedObjectMap || null)
938
+ for (let i = 0, l = valuesArray.length; i < l; i++) {
939
+ packedObjectMap[valuesArray[i]] = i
940
+ }
941
+ }
942
+ if (sharedStructures) {
943
+ targetView.setUint32(position, 0xd9dffe00)
944
+ position += 3
945
+ let definitions = sharedStructures.slice(0)
946
+ definitions.unshift(0xe000)
947
+ definitions.push(new Tag(sharedData.version, 0x53687264))
948
+ encode(definitions)
949
+ } else
950
+ encode(new Tag(sharedData.version, 0x53687264))
951
+ }
952
+ }]
953
+ function typedArrayEncoder(tag, size) {
954
+ if (!isLittleEndianMachine && size > 1)
955
+ tag -= 4 // the big endian equivalents are 4 less
956
+ return {
957
+ tag: tag,
958
+ encode: function writeExtBuffer(typedArray, encode) {
959
+ let length = typedArray.byteLength
960
+ let offset = typedArray.byteOffset || 0
961
+ let buffer = typedArray.buffer || typedArray
962
+ encode(hasNodeBuffer ? Buffer.from(buffer, offset, length) :
963
+ new Uint8Array(buffer, offset, length))
964
+ }
965
+ }
966
+ }
967
+ function writeBuffer(buffer, makeRoom) {
968
+ let length = buffer.byteLength
969
+ if (length < 0x18) {
970
+ target[position++] = 0x40 + length
971
+ } else if (length < 0x100) {
972
+ target[position++] = 0x58
973
+ target[position++] = length
974
+ } else if (length < 0x10000) {
975
+ target[position++] = 0x59
976
+ target[position++] = length >> 8
977
+ target[position++] = length & 0xff
978
+ } else {
979
+ target[position++] = 0x5a
980
+ targetView.setUint32(position, length)
981
+ position += 4
982
+ }
983
+ if (position + length >= target.length) {
984
+ makeRoom(position + length)
985
+ }
986
+ // if it is already a typed array (has an ArrayBuffer), use that, but if it is an ArrayBuffer itself,
987
+ // must wrap it to set it.
988
+ target.set(buffer.buffer ? buffer : new Uint8Array(buffer), position)
989
+ position += length
990
+ }
991
+
992
+ function insertIds(serialized, idsToInsert) {
993
+ // insert the ids that need to be referenced for structured clones
994
+ let nextId
995
+ let distanceToMove = idsToInsert.length * 2
996
+ let lastEnd = serialized.length - distanceToMove
997
+ idsToInsert.sort((a, b) => a.offset > b.offset ? 1 : -1)
998
+ for (let id = 0; id < idsToInsert.length; id++) {
999
+ let referee = idsToInsert[id]
1000
+ referee.id = id
1001
+ for (let position of referee.references) {
1002
+ serialized[position++] = id >> 8
1003
+ serialized[position] = id & 0xff
1004
+ }
1005
+ }
1006
+ while (nextId = idsToInsert.pop()) {
1007
+ let offset = nextId.offset
1008
+ serialized.copyWithin(offset + distanceToMove, offset, lastEnd)
1009
+ distanceToMove -= 2
1010
+ let position = offset + distanceToMove
1011
+ serialized[position++] = 0xd8
1012
+ serialized[position++] = 28 // http://cbor.schmorp.de/value-sharing
1013
+ lastEnd = offset
1014
+ }
1015
+ return serialized
1016
+ }
1017
+ function writeBundles(start, encode) {
1018
+ targetView.setUint32(bundledStrings.position + start, position - bundledStrings.position - start + 1) // the offset to bundle
1019
+ let writeStrings = bundledStrings
1020
+ bundledStrings = null
1021
+ encode(writeStrings[0])
1022
+ encode(writeStrings[1])
1023
+ }
1024
+
1025
+ export function addExtension(extension) {
1026
+ if (extension.Class) {
1027
+ if (!extension.encode)
1028
+ throw new Error('Extension has no encode function')
1029
+ extensionClasses.unshift(extension.Class)
1030
+ extensions.unshift(extension)
1031
+ }
1032
+ decodeAddExtension(extension)
1033
+ }
1034
+ let defaultEncoder = new Encoder({ useRecords: false })
1035
+ export const encode = defaultEncoder.encode
1036
+ export { FLOAT32_OPTIONS } from './decode.js'
1037
+ import { FLOAT32_OPTIONS } from './decode.js'
1038
+ export const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS
1039
+ export const REUSE_BUFFER_MODE = 512
1040
+ export const RESET_BUFFER_MODE = 1024
1041
+