msgpackr 1.6.1 → 1.7.0-alpha2

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/pack.js CHANGED
@@ -1,933 +1,957 @@
1
- "use strict"
2
- import { Unpackr, mult10, C1Type, typedArrays, addExtension as unpackAddExtension } from './unpack.js'
3
- let textEncoder
4
- try {
5
- textEncoder = new TextEncoder()
6
- } catch (error) {}
7
- let extensions, extensionClasses
8
- const hasNodeBuffer = typeof Buffer !== 'undefined'
9
- const ByteArrayAllocate = hasNodeBuffer ?
10
- function(length) { return Buffer.allocUnsafeSlow(length) } : Uint8Array
11
- const ByteArray = hasNodeBuffer ? Buffer : Uint8Array
12
- const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000
13
- let target, keysTarget
14
- let targetView
15
- let position = 0
16
- let safeEnd
17
- let bundledStrings = null
18
- const MAX_BUNDLE_SIZE = 0xf000
19
- const hasNonLatin = /[\u0080-\uFFFF]/
20
- const RECORD_SYMBOL = Symbol('record-id')
21
- export class Packr extends Unpackr {
22
- constructor(options) {
23
- super(options)
24
- this.offset = 0
25
- let typeBuffer
26
- let start
27
- let hasSharedUpdate
28
- let structures
29
- let referenceMap
30
- let lastSharedStructuresLength = 0
31
- let encodeUtf8 = ByteArray.prototype.utf8Write ? function(string, position) {
32
- return target.utf8Write(string, position, 0xffffffff)
33
- } : (textEncoder && textEncoder.encodeInto) ?
34
- function(string, position) {
35
- return textEncoder.encodeInto(string, target.subarray(position)).written
36
- } : false
37
-
38
- let packr = this
39
- if (!options)
40
- options = {}
41
- let isSequential = options && options.sequential
42
- let hasSharedStructures = options.structures || options.saveStructures
43
- let maxSharedStructures = options.maxSharedStructures
44
- if (maxSharedStructures == null)
45
- maxSharedStructures = hasSharedStructures ? 32 : 0
46
- if (maxSharedStructures > 8160)
47
- throw new Error('Maximum maxSharedStructure is 8160')
48
- if (options.structuredClone && options.moreTypes == undefined) {
49
- options.moreTypes = true
50
- }
51
- let maxOwnStructures = options.maxOwnStructures
52
- if (maxOwnStructures == null)
53
- maxOwnStructures = hasSharedStructures ? 32 : 64
54
- if (!this.structures && options.useRecords != false)
55
- this.structures = []
56
- // two byte record ids for shared structures
57
- let useTwoByteRecords = maxSharedStructures > 32 || (maxOwnStructures + maxSharedStructures > 64)
58
- let sharedLimitId = maxSharedStructures + 0x40
59
- let maxStructureId = maxSharedStructures + maxOwnStructures + 0x40
60
- if (maxStructureId > 8256) {
61
- throw new Error('Maximum maxSharedStructure + maxOwnStructure is 8192')
62
- }
63
- let recordIdsToRemove = []
64
- let transitionsCount = 0
65
- let serializationsSinceTransitionRebuild = 0
66
-
67
- this.pack = this.encode = function(value, encodeOptions) {
68
- if (!target) {
69
- target = new ByteArrayAllocate(8192)
70
- targetView = new DataView(target.buffer, 0, 8192)
71
- position = 0
72
- }
73
- safeEnd = target.length - 10
74
- if (safeEnd - position < 0x800) {
75
- // don't start too close to the end,
76
- target = new ByteArrayAllocate(target.length)
77
- targetView = new DataView(target.buffer, 0, target.length)
78
- safeEnd = target.length - 10
79
- position = 0
80
- } else
81
- position = (position + 7) & 0x7ffffff8 // Word align to make any future copying of this buffer faster
82
- start = position
83
- referenceMap = packr.structuredClone ? new Map() : null
84
- if (packr.bundleStrings && typeof value !== 'string') {
85
- bundledStrings = []
86
- bundledStrings.size = Infinity // force a new bundle start on first string
87
- } else
88
- bundledStrings = null
89
- structures = packr.structures
90
- if (structures) {
91
- if (structures.uninitialized)
92
- structures = packr._mergeStructures(packr.getStructures())
93
- let sharedLength = structures.sharedLength || 0
94
- if (sharedLength > maxSharedStructures) {
95
- //if (maxSharedStructures <= 32 && structures.sharedLength > 32) // TODO: could support this, but would need to update the limit ids
96
- throw new Error('Shared structures is larger than maximum shared structures, try increasing maxSharedStructures to ' + structures.sharedLength)
97
- }
98
- if (!structures.transitions) {
99
- // rebuild our structure transitions
100
- structures.transitions = Object.create(null)
101
- for (let i = 0; i < sharedLength; i++) {
102
- let keys = structures[i]
103
- if (!keys)
104
- continue
105
- let nextTransition, transition = structures.transitions
106
- for (let j = 0, l = keys.length; j < l; j++) {
107
- let key = keys[j]
108
- nextTransition = transition[key]
109
- if (!nextTransition) {
110
- nextTransition = transition[key] = Object.create(null)
111
- }
112
- transition = nextTransition
113
- }
114
- transition[RECORD_SYMBOL] = i + 0x40
115
- }
116
- lastSharedStructuresLength = sharedLength
117
- }
118
- if (!isSequential) {
119
- structures.nextId = sharedLength + 0x40
120
- }
121
- }
122
- if (hasSharedUpdate)
123
- hasSharedUpdate = false
124
- try {
125
- pack(value)
126
- if (bundledStrings) {
127
- writeBundles(start, pack)
128
- }
129
- packr.offset = position // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
130
- if (referenceMap && referenceMap.idsToInsert) {
131
- position += referenceMap.idsToInsert.length * 6
132
- if (position > safeEnd)
133
- makeRoom(position)
134
- packr.offset = position
135
- let serialized = insertIds(target.subarray(start, position), referenceMap.idsToInsert)
136
- referenceMap = null
137
- return serialized
138
- }
139
- if (encodeOptions & REUSE_BUFFER_MODE) {
140
- target.start = start
141
- target.end = position
142
- return target
143
- }
144
- return target.subarray(start, position) // position can change if we call pack again in saveStructures, so we get the buffer now
145
- } finally {
146
- if (structures) {
147
- if (serializationsSinceTransitionRebuild < 10)
148
- serializationsSinceTransitionRebuild++
149
- let sharedLength = structures.sharedLength || maxSharedStructures
150
- if (structures.length > sharedLength)
151
- structures.length = sharedLength
152
- if (transitionsCount > 10000) {
153
- // force a rebuild occasionally after a lot of transitions so it can get cleaned up
154
- structures.transitions = null
155
- serializationsSinceTransitionRebuild = 0
156
- transitionsCount = 0
157
- if (recordIdsToRemove.length > 0)
158
- recordIdsToRemove = []
159
- } else if (recordIdsToRemove.length > 0 && !isSequential) {
160
- for (let i = 0, l = recordIdsToRemove.length; i < l; i++) {
161
- recordIdsToRemove[i][RECORD_SYMBOL] = 0
162
- }
163
- recordIdsToRemove = []
164
- }
165
- if (hasSharedUpdate && packr.saveStructures) {
166
- // we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
167
- let returnBuffer = target.subarray(start, position)
168
- if (packr.saveStructures(structures, lastSharedStructuresLength) === false) {
169
- // get updated structures and try again if the update failed
170
- packr._mergeStructures(packr.getStructures())
171
- return packr.pack(value)
172
- }
173
- lastSharedStructuresLength = sharedLength
174
- return returnBuffer
175
- }
176
- }
177
- if (encodeOptions & RESET_BUFFER_MODE)
178
- position = start
179
- }
180
- }
181
- const pack = (value) => {
182
- if (position > safeEnd)
183
- target = makeRoom(position)
184
-
185
- var type = typeof value
186
- var length
187
- if (type === 'string') {
188
- let strLength = value.length
189
- if (bundledStrings && strLength >= 4 && strLength < 0x1000) {
190
- if ((bundledStrings.size += strLength) > MAX_BUNDLE_SIZE) {
191
- let extStart
192
- let maxBytes = (bundledStrings[0] ? bundledStrings[0].length * 3 + bundledStrings[1].length : 0) + 10
193
- if (position + maxBytes > safeEnd)
194
- target = makeRoom(position + maxBytes)
195
- if (bundledStrings.position) { // here we use the 0x62 extension to write the last bundle and reserve sapce for the reference pointer to the next/current bundle
196
- target[position] = 0xc8 // ext 16
197
- position += 3 // reserve for the writing bundle size
198
- target[position++] = 0x62 // 'b'
199
- extStart = position - start
200
- position += 4 // reserve for writing bundle reference
201
- writeBundles(start, pack) // write the last bundles
202
- targetView.setUint16(extStart + start - 3, position - start - extStart)
203
- } else { // here we use the 0x62 extension just to reserve the space for the reference pointer to the bundle (will be updated once the bundle is written)
204
- target[position++] = 0xd6 // fixext 4
205
- target[position++] = 0x62 // 'b'
206
- extStart = position - start
207
- position += 4 // reserve for writing bundle reference
208
- }
209
- bundledStrings = ['', ''] // create new ones
210
- bundledStrings.size = 0
211
- bundledStrings.position = extStart
212
- }
213
- let twoByte = hasNonLatin.test(value)
214
- bundledStrings[twoByte ? 0 : 1] += value
215
- target[position++] = 0xc1
216
- pack(twoByte ? -strLength : strLength);
217
- return
218
- }
219
- let headerSize
220
- // first we estimate the header size, so we can write to the correct location
221
- if (strLength < 0x20) {
222
- headerSize = 1
223
- } else if (strLength < 0x100) {
224
- headerSize = 2
225
- } else if (strLength < 0x10000) {
226
- headerSize = 3
227
- } else {
228
- headerSize = 5
229
- }
230
- let maxBytes = strLength * 3
231
- if (position + maxBytes > safeEnd)
232
- target = makeRoom(position + maxBytes)
233
-
234
- if (strLength < 0x40 || !encodeUtf8) {
235
- let i, c1, c2, strPosition = position + headerSize
236
- for (i = 0; i < strLength; i++) {
237
- c1 = value.charCodeAt(i)
238
- if (c1 < 0x80) {
239
- target[strPosition++] = c1
240
- } else if (c1 < 0x800) {
241
- target[strPosition++] = c1 >> 6 | 0xc0
242
- target[strPosition++] = c1 & 0x3f | 0x80
243
- } else if (
244
- (c1 & 0xfc00) === 0xd800 &&
245
- ((c2 = value.charCodeAt(i + 1)) & 0xfc00) === 0xdc00
246
- ) {
247
- c1 = 0x10000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff)
248
- i++
249
- target[strPosition++] = c1 >> 18 | 0xf0
250
- target[strPosition++] = c1 >> 12 & 0x3f | 0x80
251
- target[strPosition++] = c1 >> 6 & 0x3f | 0x80
252
- target[strPosition++] = c1 & 0x3f | 0x80
253
- } else {
254
- target[strPosition++] = c1 >> 12 | 0xe0
255
- target[strPosition++] = c1 >> 6 & 0x3f | 0x80
256
- target[strPosition++] = c1 & 0x3f | 0x80
257
- }
258
- }
259
- length = strPosition - position - headerSize
260
- } else {
261
- length = encodeUtf8(value, position + headerSize)
262
- }
263
-
264
- if (length < 0x20) {
265
- target[position++] = 0xa0 | length
266
- } else if (length < 0x100) {
267
- if (headerSize < 2) {
268
- target.copyWithin(position + 2, position + 1, position + 1 + length)
269
- }
270
- target[position++] = 0xd9
271
- target[position++] = length
272
- } else if (length < 0x10000) {
273
- if (headerSize < 3) {
274
- target.copyWithin(position + 3, position + 2, position + 2 + length)
275
- }
276
- target[position++] = 0xda
277
- target[position++] = length >> 8
278
- target[position++] = length & 0xff
279
- } else {
280
- if (headerSize < 5) {
281
- target.copyWithin(position + 5, position + 3, position + 3 + length)
282
- }
283
- target[position++] = 0xdb
284
- targetView.setUint32(position, length)
285
- position += 4
286
- }
287
- position += length
288
- } else if (type === 'number') {
289
- if (value >>> 0 === value) {// positive integer, 32-bit or less
290
- // positive uint
291
- if (value < 0x40) {
292
- target[position++] = value
293
- } else if (value < 0x100) {
294
- target[position++] = 0xcc
295
- target[position++] = value
296
- } else if (value < 0x10000) {
297
- target[position++] = 0xcd
298
- target[position++] = value >> 8
299
- target[position++] = value & 0xff
300
- } else {
301
- target[position++] = 0xce
302
- targetView.setUint32(position, value)
303
- position += 4
304
- }
305
- } else if (value >> 0 === value) { // negative integer
306
- if (value >= -0x20) {
307
- target[position++] = 0x100 + value
308
- } else if (value >= -0x80) {
309
- target[position++] = 0xd0
310
- target[position++] = value + 0x100
311
- } else if (value >= -0x8000) {
312
- target[position++] = 0xd1
313
- targetView.setInt16(position, value)
314
- position += 2
315
- } else {
316
- target[position++] = 0xd2
317
- targetView.setInt32(position, value)
318
- position += 4
319
- }
320
- } else {
321
- let useFloat32
322
- if ((useFloat32 = this.useFloat32) > 0 && value < 0x100000000 && value >= -0x80000000) {
323
- target[position++] = 0xca
324
- targetView.setFloat32(position, value)
325
- let xShifted
326
- if (useFloat32 < 4 ||
327
- // this checks for rounding of numbers that were encoded in 32-bit float to nearest significant decimal digit that could be preserved
328
- ((xShifted = value * mult10[((target[position] & 0x7f) << 1) | (target[position + 1] >> 7)]) >> 0) === xShifted) {
329
- position += 4
330
- return
331
- } else
332
- position-- // move back into position for writing a double
333
- }
334
- target[position++] = 0xcb
335
- targetView.setFloat64(position, value)
336
- position += 8
337
- }
338
- } else if (type === 'object') {
339
- if (!value)
340
- target[position++] = 0xc0
341
- else {
342
- if (referenceMap) {
343
- let referee = referenceMap.get(value)
344
- if (referee) {
345
- if (!referee.id) {
346
- let idsToInsert = referenceMap.idsToInsert || (referenceMap.idsToInsert = [])
347
- referee.id = idsToInsert.push(referee)
348
- }
349
- target[position++] = 0xd6 // fixext 4
350
- target[position++] = 0x70 // "p" for pointer
351
- targetView.setUint32(position, referee.id)
352
- position += 4
353
- return
354
- } else
355
- referenceMap.set(value, { offset: position - start })
356
- }
357
- let constructor = value.constructor
358
- if (constructor === Object) {
359
- writeObject(value, true)
360
- } else if (constructor === Array) {
361
- length = value.length
362
- if (length < 0x10) {
363
- target[position++] = 0x90 | length
364
- } else if (length < 0x10000) {
365
- target[position++] = 0xdc
366
- target[position++] = length >> 8
367
- target[position++] = length & 0xff
368
- } else {
369
- target[position++] = 0xdd
370
- targetView.setUint32(position, length)
371
- position += 4
372
- }
373
- for (let i = 0; i < length; i++) {
374
- pack(value[i])
375
- }
376
- } else if (constructor === Map) {
377
- length = value.size
378
- if (length < 0x10) {
379
- target[position++] = 0x80 | length
380
- } else if (length < 0x10000) {
381
- target[position++] = 0xde
382
- target[position++] = length >> 8
383
- target[position++] = length & 0xff
384
- } else {
385
- target[position++] = 0xdf
386
- targetView.setUint32(position, length)
387
- position += 4
388
- }
389
- for (let [ key, entryValue ] of value) {
390
- pack(key)
391
- pack(entryValue)
392
- }
393
- } else {
394
- for (let i = 0, l = extensions.length; i < l; i++) {
395
- let extensionClass = extensionClasses[i]
396
- if (value instanceof extensionClass) {
397
- let extension = extensions[i]
398
- if (extension.write) {
399
- if (extension.type) {
400
- target[position++] = 0xd4 // one byte "tag" extension
401
- target[position++] = extension.type
402
- target[position++] = 0
403
- }
404
- pack(extension.write.call(this, value))
405
- return
406
- }
407
- let currentTarget = target
408
- let currentTargetView = targetView
409
- let currentPosition = position
410
- target = null
411
- let result
412
- try {
413
- result = extension.pack.call(this, value, (size) => {
414
- // restore target and use it
415
- target = currentTarget
416
- currentTarget = null
417
- position += size
418
- if (position > safeEnd)
419
- makeRoom(position)
420
- return {
421
- target, targetView, position: position - size
422
- }
423
- }, pack)
424
- } finally {
425
- // restore current target information (unless already restored)
426
- if (currentTarget) {
427
- target = currentTarget
428
- targetView = currentTargetView
429
- position = currentPosition
430
- safeEnd = target.length - 10
431
- }
432
- }
433
- if (result) {
434
- if (result.length + position > safeEnd)
435
- makeRoom(result.length + position)
436
- position = writeExtensionData(result, target, position, extension.type)
437
- }
438
- return
439
- }
440
- }
441
- // no extension found, write as object
442
- writeObject(value, !value.hasOwnProperty) // if it doesn't have hasOwnProperty, don't do hasOwnProperty checks
443
- }
444
- }
445
- } else if (type === 'boolean') {
446
- target[position++] = value ? 0xc3 : 0xc2
447
- } else if (type === 'bigint') {
448
- if (value < (BigInt(1)<<BigInt(63)) && value >= -(BigInt(1)<<BigInt(63))) {
449
- // use a signed int as long as it fits
450
- target[position++] = 0xd3
451
- targetView.setBigInt64(position, value)
452
- } else if (value < (BigInt(1)<<BigInt(64)) && value > 0) {
453
- // if we can fit an unsigned int, use that
454
- target[position++] = 0xcf
455
- targetView.setBigUint64(position, value)
456
- } else {
457
- // overflow
458
- if (this.largeBigIntToFloat) {
459
- target[position++] = 0xcb
460
- targetView.setFloat64(position, Number(value))
461
- } else {
462
- throw new RangeError(value + ' was too large to fit in MessagePack 64-bit integer format, set largeBigIntToFloat to convert to float-64')
463
- }
464
- }
465
- position += 8
466
- } else if (type === 'undefined') {
467
- if (this.encodeUndefinedAsNil)
468
- target[position++] = 0xc0
469
- else {
470
- target[position++] = 0xd4 // a number of implementations use fixext1 with type 0, data 0 to denote undefined, so we follow suite
471
- target[position++] = 0
472
- target[position++] = 0
473
- }
474
- } else if (type === 'function') {
475
- pack(this.writeFunction && this.writeFunction()) // if there is a writeFunction, use it, otherwise just encode as undefined
476
- } else {
477
- throw new Error('Unknown type: ' + type)
478
- }
479
- }
480
-
481
- const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => {
482
- // this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
483
- let keys = Object.keys(object)
484
- let length = keys.length
485
- if (length < 0x10) {
486
- target[position++] = 0x80 | length
487
- } else if (length < 0x10000) {
488
- target[position++] = 0xde
489
- target[position++] = length >> 8
490
- target[position++] = length & 0xff
491
- } else {
492
- target[position++] = 0xdf
493
- targetView.setUint32(position, length)
494
- position += 4
495
- }
496
- let key
497
- for (let i = 0; i < length; i++) {
498
- pack(key = keys[i])
499
- pack(object[key])
500
- }
501
- } :
502
- (object, safePrototype) => {
503
- target[position++] = 0xde // always using map 16, so we can preallocate and set the length afterwards
504
- let objectOffset = position - start
505
- position += 2
506
- let size = 0
507
- for (let key in object) {
508
- if (safePrototype || object.hasOwnProperty(key)) {
509
- pack(key)
510
- pack(object[key])
511
- size++
512
- }
513
- }
514
- target[objectOffset++ + start] = size >> 8
515
- target[objectOffset + start] = size & 0xff
516
- } :
517
- (options.progressiveRecords && !useTwoByteRecords) ? // this is about 2% faster for highly stable structures, since it only requires one for-in loop (but much more expensive when new structure needs to be written)
518
- (object, safePrototype) => {
519
- let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
520
- let objectOffset = position++ - start
521
- let wroteKeys
522
- for (let key in object) {
523
- if (safePrototype || object.hasOwnProperty(key)) {
524
- nextTransition = transition[key]
525
- if (nextTransition)
526
- transition = nextTransition
527
- else {
528
- // record doesn't exist, create full new record and insert it
529
- let keys = Object.keys(object)
530
- let lastTransition = transition
531
- transition = structures.transitions
532
- let newTransitions = 0
533
- for (let i = 0, l = keys.length; i < l; i++) {
534
- let key = keys[i]
535
- nextTransition = transition[key]
536
- if (!nextTransition) {
537
- nextTransition = transition[key] = Object.create(null)
538
- newTransitions++
539
- }
540
- transition = nextTransition
541
- }
542
- if (objectOffset + start + 1 == position) {
543
- // first key, so we don't need to insert, we can just write record directly
544
- position--
545
- newRecord(transition, keys, newTransitions)
546
- } else // otherwise we need to insert the record, moving existing data after the record
547
- insertNewRecord(transition, keys, objectOffset, newTransitions)
548
- wroteKeys = true
549
- transition = lastTransition[key]
550
- }
551
- pack(object[key])
552
- }
553
- }
554
- if (!wroteKeys) {
555
- let recordId = transition[RECORD_SYMBOL]
556
- if (recordId)
557
- target[objectOffset + start] = recordId
558
- else
559
- insertNewRecord(transition, Object.keys(object), objectOffset, 0)
560
- }
561
- } :
562
- (object, safePrototype) => {
563
- let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
564
- let newTransitions = 0
565
- for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
566
- nextTransition = transition[key]
567
- if (!nextTransition) {
568
- nextTransition = transition[key] = Object.create(null)
569
- newTransitions++
570
- }
571
- transition = nextTransition
572
- }
573
- let recordId = transition[RECORD_SYMBOL]
574
- if (recordId) {
575
- if (recordId >= 0x60 && useTwoByteRecords) {
576
- target[position++] = ((recordId -= 0x60) & 0x1f) + 0x60
577
- target[position++] = recordId >> 5
578
- } else
579
- target[position++] = recordId
580
- } else {
581
- newRecord(transition, transition.__keys__ || Object.keys(object), newTransitions)
582
- }
583
- // now write the values
584
- for (let key in object)
585
- if (safePrototype || object.hasOwnProperty(key))
586
- pack(object[key])
587
- }
588
- const makeRoom = (end) => {
589
- let newSize
590
- if (end > 0x1000000) {
591
- // special handling for really large buffers
592
- if ((end - start) > MAX_BUFFER_SIZE)
593
- throw new Error('Packed buffer would be larger than maximum buffer size')
594
- newSize = Math.min(MAX_BUFFER_SIZE,
595
- Math.round(Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2), 0x400000) / 0x1000) * 0x1000)
596
- } else // faster handling for smaller buffers
597
- newSize = ((Math.max((end - start) << 2, target.length - 1) >> 12) + 1) << 12
598
- let newBuffer = new ByteArrayAllocate(newSize)
599
- targetView = new DataView(newBuffer.buffer, 0, newSize)
600
- end = Math.min(end, target.length)
601
- if (target.copy)
602
- target.copy(newBuffer, 0, start, end)
603
- else
604
- newBuffer.set(target.slice(start, end))
605
- position -= start
606
- start = 0
607
- safeEnd = newBuffer.length - 10
608
- return target = newBuffer
609
- }
610
- const newRecord = (transition, keys, newTransitions) => {
611
- let recordId = structures.nextId
612
- if (!recordId)
613
- recordId = 0x40
614
- if (recordId < sharedLimitId && this.shouldShareStructure && !this.shouldShareStructure(keys)) {
615
- recordId = structures.nextOwnId
616
- if (!(recordId < maxStructureId))
617
- recordId = sharedLimitId
618
- structures.nextOwnId = recordId + 1
619
- } else {
620
- if (recordId >= maxStructureId)// cycle back around
621
- recordId = sharedLimitId
622
- structures.nextId = recordId + 1
623
- }
624
- let highByte = keys.highByte = recordId >= 0x60 && useTwoByteRecords ? (recordId - 0x60) >> 5 : -1
625
- transition[RECORD_SYMBOL] = recordId
626
- transition.__keys__ = keys
627
- structures[recordId - 0x40] = keys
628
-
629
- if (recordId < sharedLimitId) {
630
- keys.isShared = true
631
- structures.sharedLength = recordId - 0x3f
632
- hasSharedUpdate = true
633
- if (highByte >= 0) {
634
- target[position++] = (recordId & 0x1f) + 0x60
635
- target[position++] = highByte
636
- } else {
637
- target[position++] = recordId
638
- }
639
- } else {
640
- if (highByte >= 0) {
641
- target[position++] = 0xd5 // fixext 2
642
- target[position++] = 0x72 // "r" record defintion extension type
643
- target[position++] = (recordId & 0x1f) + 0x60
644
- target[position++] = highByte
645
- } else {
646
- target[position++] = 0xd4 // fixext 1
647
- target[position++] = 0x72 // "r" record defintion extension type
648
- target[position++] = recordId
649
- }
650
-
651
- if (newTransitions)
652
- transitionsCount += serializationsSinceTransitionRebuild * newTransitions
653
- // record the removal of the id, we can maintain our shared structure
654
- if (recordIdsToRemove.length >= maxOwnStructures)
655
- recordIdsToRemove.shift()[RECORD_SYMBOL] = 0 // we are cycling back through, and have to remove old ones
656
- recordIdsToRemove.push(transition)
657
- pack(keys)
658
- }
659
- }
660
- const insertNewRecord = (transition, keys, insertionOffset, newTransitions) => {
661
- let mainTarget = target
662
- let mainPosition = position
663
- let mainSafeEnd = safeEnd
664
- let mainStart = start
665
- target = keysTarget
666
- position = 0
667
- start = 0
668
- if (!target)
669
- keysTarget = target = new ByteArrayAllocate(8192)
670
- safeEnd = target.length - 10
671
- newRecord(transition, keys, newTransitions)
672
- keysTarget = target
673
- let keysPosition = position
674
- target = mainTarget
675
- position = mainPosition
676
- safeEnd = mainSafeEnd
677
- start = mainStart
678
- if (keysPosition > 1) {
679
- let newEnd = position + keysPosition - 1
680
- if (newEnd > safeEnd)
681
- makeRoom(newEnd)
682
- let insertionPosition = insertionOffset + start
683
- target.copyWithin(insertionPosition + keysPosition, insertionPosition + 1, position)
684
- target.set(keysTarget.slice(0, keysPosition), insertionPosition)
685
- position = newEnd
686
- } else {
687
- target[insertionOffset + start] = keysTarget[0]
688
- }
689
- }
690
- }
691
- useBuffer(buffer) {
692
- // this means we are finished using our own buffer and we can write over it safely
693
- target = buffer
694
- targetView = new DataView(target.buffer, target.byteOffset, target.byteLength)
695
- position = 0
696
- }
697
- clearSharedData() {
698
- if (this.structures)
699
- this.structures = []
700
- }
701
- }
702
-
703
- function copyBinary(source, target, targetOffset, offset, endOffset) {
704
- while (offset < endOffset) {
705
- target[targetOffset++] = source[offset++]
706
- }
707
- }
708
-
709
- extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/, C1Type ]
710
- extensions = [{
711
- pack(date, allocateForWrite, pack) {
712
- let seconds = date.getTime() / 1000
713
- if ((this.useTimestamp32 || date.getMilliseconds() === 0) && seconds >= 0 && seconds < 0x100000000) {
714
- // Timestamp 32
715
- let { target, targetView, position} = allocateForWrite(6)
716
- target[position++] = 0xd6
717
- target[position++] = 0xff
718
- targetView.setUint32(position, seconds)
719
- } else if (seconds > 0 && seconds < 0x100000000) {
720
- // Timestamp 64
721
- let { target, targetView, position} = allocateForWrite(10)
722
- target[position++] = 0xd7
723
- target[position++] = 0xff
724
- targetView.setUint32(position, date.getMilliseconds() * 4000000 + ((seconds / 1000 / 0x100000000) >> 0))
725
- targetView.setUint32(position + 4, seconds)
726
- } else if (isNaN(seconds)) {
727
- if (this.onInvalidDate) {
728
- allocateForWrite(0)
729
- return pack(this.onInvalidDate())
730
- }
731
- // Intentionally invalid timestamp
732
- let { target, targetView, position} = allocateForWrite(3)
733
- target[position++] = 0xd4
734
- target[position++] = 0xff
735
- target[position++] = 0xff
736
- } else {
737
- // Timestamp 96
738
- let { target, targetView, position} = allocateForWrite(15)
739
- target[position++] = 0xc7
740
- target[position++] = 12
741
- target[position++] = 0xff
742
- targetView.setUint32(position, date.getMilliseconds() * 1000000)
743
- targetView.setBigInt64(position + 4, BigInt(Math.floor(seconds)))
744
- }
745
- }
746
- }, {
747
- pack(set, allocateForWrite, pack) {
748
- let array = Array.from(set)
749
- let { target, position} = allocateForWrite(this.moreTypes ? 3 : 0)
750
- if (this.moreTypes) {
751
- target[position++] = 0xd4
752
- target[position++] = 0x73 // 's' for Set
753
- target[position++] = 0
754
- }
755
- pack(array)
756
- }
757
- }, {
758
- pack(error, allocateForWrite, pack) {
759
- let { target, position} = allocateForWrite(this.moreTypes ? 3 : 0)
760
- if (this.moreTypes) {
761
- target[position++] = 0xd4
762
- target[position++] = 0x65 // 'e' for error
763
- target[position++] = 0
764
- }
765
- pack([ error.name, error.message ])
766
- }
767
- }, {
768
- pack(regex, allocateForWrite, pack) {
769
- let { target, position} = allocateForWrite(this.moreTypes ? 3 : 0)
770
- if (this.moreTypes) {
771
- target[position++] = 0xd4
772
- target[position++] = 0x78 // 'x' for regeXp
773
- target[position++] = 0
774
- }
775
- pack([ regex.source, regex.flags ])
776
- }
777
- }, {
778
- pack(arrayBuffer, allocateForWrite) {
779
- if (this.moreTypes)
780
- writeExtBuffer(arrayBuffer, 0x10, allocateForWrite)
781
- else
782
- writeBuffer(hasNodeBuffer ? Buffer.from(arrayBuffer) : new Uint8Array(arrayBuffer), allocateForWrite)
783
- }
784
- }, {
785
- pack(typedArray, allocateForWrite) {
786
- let constructor = typedArray.constructor
787
- if (constructor !== ByteArray && this.moreTypes)
788
- writeExtBuffer(typedArray, typedArrays.indexOf(constructor.name), allocateForWrite)
789
- else
790
- writeBuffer(typedArray, allocateForWrite)
791
- }
792
- }, {
793
- pack(c1, allocateForWrite) { // specific 0xC1 object
794
- let { target, position} = allocateForWrite(1)
795
- target[position] = 0xc1
796
- }
797
- }]
798
-
799
- function writeExtBuffer(typedArray, type, allocateForWrite, encode) {
800
- let length = typedArray.byteLength
801
- if (length + 1 < 0x100) {
802
- var { target, position } = allocateForWrite(4 + length)
803
- target[position++] = 0xc7
804
- target[position++] = length + 1
805
- } else if (length + 1 < 0x10000) {
806
- var { target, position } = allocateForWrite(5 + length)
807
- target[position++] = 0xc8
808
- target[position++] = (length + 1) >> 8
809
- target[position++] = (length + 1) & 0xff
810
- } else {
811
- var { target, position, targetView } = allocateForWrite(7 + length)
812
- target[position++] = 0xc9
813
- targetView.setUint32(position, length + 1) // plus one for the type byte
814
- position += 4
815
- }
816
- target[position++] = 0x74 // "t" for typed array
817
- target[position++] = type
818
- target.set(new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength), position)
819
- }
820
- function writeBuffer(buffer, allocateForWrite) {
821
- let length = buffer.byteLength
822
- var target, position
823
- if (length < 0x100) {
824
- var { target, position } = allocateForWrite(length + 2)
825
- target[position++] = 0xc4
826
- target[position++] = length
827
- } else if (length < 0x10000) {
828
- var { target, position } = allocateForWrite(length + 3)
829
- target[position++] = 0xc5
830
- target[position++] = length >> 8
831
- target[position++] = length & 0xff
832
- } else {
833
- var { target, position, targetView } = allocateForWrite(length + 5)
834
- target[position++] = 0xc6
835
- targetView.setUint32(position, length)
836
- position += 4
837
- }
838
- target.set(buffer, position)
839
- }
840
-
841
- function writeExtensionData(result, target, position, type) {
842
- let length = result.length
843
- switch (length) {
844
- case 1:
845
- target[position++] = 0xd4
846
- break
847
- case 2:
848
- target[position++] = 0xd5
849
- break
850
- case 4:
851
- target[position++] = 0xd6
852
- break
853
- case 8:
854
- target[position++] = 0xd7
855
- break
856
- case 16:
857
- target[position++] = 0xd8
858
- break
859
- default:
860
- if (length < 0x100) {
861
- target[position++] = 0xc7
862
- target[position++] = length
863
- } else if (length < 0x10000) {
864
- target[position++] = 0xc8
865
- target[position++] = length >> 8
866
- target[position++] = length & 0xff
867
- } else {
868
- target[position++] = 0xc9
869
- target[position++] = length >> 24
870
- target[position++] = (length >> 16) & 0xff
871
- target[position++] = (length >> 8) & 0xff
872
- target[position++] = length & 0xff
873
- }
874
- }
875
- target[position++] = type
876
- target.set(result, position)
877
- position += length
878
- return position
879
- }
880
-
881
- function insertIds(serialized, idsToInsert) {
882
- // insert the ids that need to be referenced for structured clones
883
- let nextId
884
- let distanceToMove = idsToInsert.length * 6
885
- let lastEnd = serialized.length - distanceToMove
886
- idsToInsert.sort((a, b) => a.offset > b.offset ? 1 : -1)
887
- while (nextId = idsToInsert.pop()) {
888
- let offset = nextId.offset
889
- let id = nextId.id
890
- serialized.copyWithin(offset + distanceToMove, offset, lastEnd)
891
- distanceToMove -= 6
892
- let position = offset + distanceToMove
893
- serialized[position++] = 0xd6
894
- serialized[position++] = 0x69 // 'i'
895
- serialized[position++] = id >> 24
896
- serialized[position++] = (id >> 16) & 0xff
897
- serialized[position++] = (id >> 8) & 0xff
898
- serialized[position++] = id & 0xff
899
- lastEnd = offset
900
- }
901
- return serialized
902
- }
903
-
904
- function writeBundles(start, pack) {
905
- targetView.setUint32(bundledStrings.position + start, position - bundledStrings.position - start)
906
- let writeStrings = bundledStrings
907
- bundledStrings = null
908
- let startPosition = position
909
- pack(writeStrings[0])
910
- pack(writeStrings[1])
911
- }
912
-
913
- export function addExtension(extension) {
914
- if (extension.Class) {
915
- if (!extension.pack && !extension.write)
916
- throw new Error('Extension has no pack or write function')
917
- if (extension.pack && !extension.type)
918
- throw new Error('Extension has no type (numeric code to identify the extension)')
919
- extensionClasses.unshift(extension.Class)
920
- extensions.unshift(extension)
921
- }
922
- unpackAddExtension(extension)
923
- }
924
-
925
- let defaultPackr = new Packr({ useRecords: false })
926
- export const pack = defaultPackr.pack
927
- export const encode = defaultPackr.pack
928
- export const Encoder = Packr
929
- export { FLOAT32_OPTIONS } from './unpack.js'
930
- import { FLOAT32_OPTIONS } from './unpack.js'
931
- export const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS
932
- export const REUSE_BUFFER_MODE = 512
933
- export const RESET_BUFFER_MODE = 1024
1
+ "use strict"
2
+ import { Unpackr, mult10, C1Type, typedArrays, addExtension as unpackAddExtension } from './unpack.js'
3
+ let textEncoder
4
+ try {
5
+ textEncoder = new TextEncoder()
6
+ } catch (error) {}
7
+ let extensions, extensionClasses
8
+ const hasNodeBuffer = typeof Buffer !== 'undefined'
9
+ const ByteArrayAllocate = hasNodeBuffer ?
10
+ function(length) { return Buffer.allocUnsafeSlow(length) } : Uint8Array
11
+ const ByteArray = hasNodeBuffer ? Buffer : Uint8Array
12
+ const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000
13
+ let target, keysTarget
14
+ let targetView
15
+ let position = 0
16
+ let safeEnd
17
+ let bundledStrings = null
18
+ let writeStructSlots
19
+ const MAX_BUNDLE_SIZE = 0xf000
20
+ const hasNonLatin = /[\u0080-\uFFFF]/
21
+ export const RECORD_SYMBOL = Symbol('record-id')
22
+ export class Packr extends Unpackr {
23
+ constructor(options) {
24
+ super(options)
25
+ this.offset = 0
26
+ let typeBuffer
27
+ let start
28
+ let hasSharedUpdate
29
+ let structures
30
+ let referenceMap
31
+ let lastSharedStructuresLength = 0
32
+ let encodeUtf8 = ByteArray.prototype.utf8Write ? function(string, position) {
33
+ return target.utf8Write(string, position, 0xffffffff)
34
+ } : (textEncoder && textEncoder.encodeInto) ?
35
+ function(string, position) {
36
+ return textEncoder.encodeInto(string, target.subarray(position)).written
37
+ } : false
38
+
39
+ let packr = this
40
+ if (!options)
41
+ options = {}
42
+ let isSequential = options && options.sequential
43
+ let hasSharedStructures = options.structures || options.saveStructures
44
+ let maxSharedStructures = options.maxSharedStructures
45
+ if (maxSharedStructures == null)
46
+ maxSharedStructures = hasSharedStructures ? 32 : 0
47
+ if (maxSharedStructures > 8160)
48
+ throw new Error('Maximum maxSharedStructure is 8160')
49
+ if (options.structuredClone && options.moreTypes == undefined) {
50
+ options.moreTypes = true
51
+ }
52
+ let maxOwnStructures = options.maxOwnStructures
53
+ if (maxOwnStructures == null)
54
+ maxOwnStructures = hasSharedStructures ? 32 : 64
55
+ if (!this.structures && options.useRecords != false)
56
+ this.structures = []
57
+ // two byte record ids for shared structures
58
+ let useTwoByteRecords = maxSharedStructures > 32 || (maxOwnStructures + maxSharedStructures > 64)
59
+ let sharedLimitId = maxSharedStructures + 0x40
60
+ let maxStructureId = maxSharedStructures + maxOwnStructures + 0x40
61
+ if (maxStructureId > 8256) {
62
+ throw new Error('Maximum maxSharedStructure + maxOwnStructure is 8192')
63
+ }
64
+ let recordIdsToRemove = []
65
+ let transitionsCount = 0
66
+ let serializationsSinceTransitionRebuild = 0
67
+
68
+ this.pack = this.encode = function(value, encodeOptions) {
69
+ if (!target) {
70
+ target = new ByteArrayAllocate(8192)
71
+ targetView = target.dataView = new DataView(target.buffer, 0, 8192)
72
+ position = 0
73
+ }
74
+ safeEnd = target.length - 10
75
+ if (safeEnd - position < 0x800) {
76
+ // don't start too close to the end,
77
+ target = new ByteArrayAllocate(target.length)
78
+ targetView = target.dataView = new DataView(target.buffer, 0, target.length)
79
+ safeEnd = target.length - 10
80
+ position = 0
81
+ } else
82
+ position = (position + 7) & 0x7ffffff8 // Word align to make any future copying of this buffer faster
83
+ start = position
84
+ referenceMap = packr.structuredClone ? new Map() : null
85
+ if (packr.bundleStrings && typeof value !== 'string') {
86
+ bundledStrings = []
87
+ bundledStrings.size = Infinity // force a new bundle start on first string
88
+ } else
89
+ bundledStrings = null
90
+ structures = packr.structures
91
+ if (structures) {
92
+ if (structures.uninitialized)
93
+ structures = packr._mergeStructures(packr.getStructures())
94
+ let sharedLength = structures.sharedLength || 0
95
+ if (sharedLength > maxSharedStructures) {
96
+ //if (maxSharedStructures <= 32 && structures.sharedLength > 32) // TODO: could support this, but would need to update the limit ids
97
+ throw new Error('Shared structures is larger than maximum shared structures, try increasing maxSharedStructures to ' + structures.sharedLength)
98
+ }
99
+ if (!structures.transitions) {
100
+ // rebuild our structure transitions
101
+ structures.transitions = Object.create(null)
102
+ for (let i = 0; i < sharedLength; i++) {
103
+ let keys = structures[i]
104
+ if (!keys)
105
+ continue
106
+ let nextTransition, transition = structures.transitions
107
+ for (let j = 0, l = keys.length; j < l; j++) {
108
+ let key = keys[j]
109
+ nextTransition = transition[key]
110
+ if (!nextTransition) {
111
+ nextTransition = transition[key] = Object.create(null)
112
+ }
113
+ transition = nextTransition
114
+ }
115
+ transition[RECORD_SYMBOL] = i + 0x40
116
+ }
117
+ lastSharedStructuresLength = sharedLength
118
+ }
119
+ if (!isSequential) {
120
+ structures.nextId = sharedLength + 0x40
121
+ }
122
+ }
123
+ if (hasSharedUpdate)
124
+ hasSharedUpdate = false
125
+ try {
126
+ if (packr.randomAccessStructure && value.constructor && value.constructor === Object)
127
+ writeStruct(value);
128
+ else
129
+ pack(value)
130
+ if (bundledStrings) {
131
+ writeBundles(start, pack)
132
+ }
133
+ packr.offset = position // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
134
+ if (referenceMap && referenceMap.idsToInsert) {
135
+ position += referenceMap.idsToInsert.length * 6
136
+ if (position > safeEnd)
137
+ makeRoom(position)
138
+ packr.offset = position
139
+ let serialized = insertIds(target.subarray(start, position), referenceMap.idsToInsert)
140
+ referenceMap = null
141
+ return serialized
142
+ }
143
+ if (encodeOptions & REUSE_BUFFER_MODE) {
144
+ target.start = start
145
+ target.end = position
146
+ return target
147
+ }
148
+ return target.subarray(start, position) // position can change if we call pack again in saveStructures, so we get the buffer now
149
+ } finally {
150
+ if (structures) {
151
+ if (serializationsSinceTransitionRebuild < 10)
152
+ serializationsSinceTransitionRebuild++
153
+ let sharedLength = structures.sharedLength || maxSharedStructures
154
+ if (structures.length > sharedLength)
155
+ structures.length = sharedLength
156
+ if (transitionsCount > 10000) {
157
+ // force a rebuild occasionally after a lot of transitions so it can get cleaned up
158
+ structures.transitions = null
159
+ serializationsSinceTransitionRebuild = 0
160
+ transitionsCount = 0
161
+ if (recordIdsToRemove.length > 0)
162
+ recordIdsToRemove = []
163
+ } else if (recordIdsToRemove.length > 0 && !isSequential) {
164
+ for (let i = 0, l = recordIdsToRemove.length; i < l; i++) {
165
+ recordIdsToRemove[i][RECORD_SYMBOL] = 0
166
+ }
167
+ recordIdsToRemove = []
168
+ }
169
+ if (hasSharedUpdate && packr.saveStructures) {
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
+ if (packr.saveStructures(structures, lastSharedStructuresLength) === false) {
173
+ // get updated structures and try again if the update failed
174
+ packr._mergeStructures(packr.getStructures())
175
+ return packr.pack(value)
176
+ }
177
+ lastSharedStructuresLength = sharedLength
178
+ return returnBuffer
179
+ }
180
+ }
181
+ if (encodeOptions & RESET_BUFFER_MODE)
182
+ position = start
183
+ }
184
+ }
185
+ const pack = (value) => {
186
+ if (position > safeEnd)
187
+ target = makeRoom(position)
188
+
189
+ var type = typeof value
190
+ var length
191
+ if (type === 'string') {
192
+ let strLength = value.length
193
+ if (bundledStrings && strLength >= 4 && strLength < 0x1000) {
194
+ if ((bundledStrings.size += strLength) > MAX_BUNDLE_SIZE) {
195
+ let extStart
196
+ let maxBytes = (bundledStrings[0] ? bundledStrings[0].length * 3 + bundledStrings[1].length : 0) + 10
197
+ if (position + maxBytes > safeEnd)
198
+ target = makeRoom(position + maxBytes)
199
+ if (bundledStrings.position) { // here we use the 0x62 extension to write the last bundle and reserve sapce for the reference pointer to the next/current bundle
200
+ target[position] = 0xc8 // ext 16
201
+ position += 3 // reserve for the writing bundle size
202
+ target[position++] = 0x62 // 'b'
203
+ extStart = position - start
204
+ position += 4 // reserve for writing bundle reference
205
+ writeBundles(start, pack) // write the last bundles
206
+ targetView.setUint16(extStart + start - 3, position - start - extStart)
207
+ } else { // here we use the 0x62 extension just to reserve the space for the reference pointer to the bundle (will be updated once the bundle is written)
208
+ target[position++] = 0xd6 // fixext 4
209
+ target[position++] = 0x62 // 'b'
210
+ extStart = position - start
211
+ position += 4 // reserve for writing bundle reference
212
+ }
213
+ bundledStrings = ['', ''] // create new ones
214
+ bundledStrings.size = 0
215
+ bundledStrings.position = extStart
216
+ }
217
+ let twoByte = hasNonLatin.test(value)
218
+ bundledStrings[twoByte ? 0 : 1] += value
219
+ target[position++] = 0xc1
220
+ pack(twoByte ? -strLength : strLength);
221
+ return
222
+ }
223
+ let headerSize
224
+ // first we estimate the header size, so we can write to the correct location
225
+ if (strLength < 0x20) {
226
+ headerSize = 1
227
+ } else if (strLength < 0x100) {
228
+ headerSize = 2
229
+ } else if (strLength < 0x10000) {
230
+ headerSize = 3
231
+ } else {
232
+ headerSize = 5
233
+ }
234
+ let maxBytes = strLength * 3
235
+ if (position + maxBytes > safeEnd)
236
+ target = makeRoom(position + maxBytes)
237
+
238
+ if (strLength < 0x40 || !encodeUtf8) {
239
+ let i, c1, c2, strPosition = position + headerSize
240
+ for (i = 0; i < strLength; i++) {
241
+ c1 = value.charCodeAt(i)
242
+ if (c1 < 0x80) {
243
+ target[strPosition++] = c1
244
+ } else if (c1 < 0x800) {
245
+ target[strPosition++] = c1 >> 6 | 0xc0
246
+ target[strPosition++] = c1 & 0x3f | 0x80
247
+ } else if (
248
+ (c1 & 0xfc00) === 0xd800 &&
249
+ ((c2 = value.charCodeAt(i + 1)) & 0xfc00) === 0xdc00
250
+ ) {
251
+ c1 = 0x10000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff)
252
+ i++
253
+ target[strPosition++] = c1 >> 18 | 0xf0
254
+ target[strPosition++] = c1 >> 12 & 0x3f | 0x80
255
+ target[strPosition++] = c1 >> 6 & 0x3f | 0x80
256
+ target[strPosition++] = c1 & 0x3f | 0x80
257
+ } else {
258
+ target[strPosition++] = c1 >> 12 | 0xe0
259
+ target[strPosition++] = c1 >> 6 & 0x3f | 0x80
260
+ target[strPosition++] = c1 & 0x3f | 0x80
261
+ }
262
+ }
263
+ length = strPosition - position - headerSize
264
+ } else {
265
+ length = encodeUtf8(value, position + headerSize)
266
+ }
267
+
268
+ if (length < 0x20) {
269
+ target[position++] = 0xa0 | length
270
+ } else if (length < 0x100) {
271
+ if (headerSize < 2) {
272
+ target.copyWithin(position + 2, position + 1, position + 1 + length)
273
+ }
274
+ target[position++] = 0xd9
275
+ target[position++] = length
276
+ } else if (length < 0x10000) {
277
+ if (headerSize < 3) {
278
+ target.copyWithin(position + 3, position + 2, position + 2 + length)
279
+ }
280
+ target[position++] = 0xda
281
+ target[position++] = length >> 8
282
+ target[position++] = length & 0xff
283
+ } else {
284
+ if (headerSize < 5) {
285
+ target.copyWithin(position + 5, position + 3, position + 3 + length)
286
+ }
287
+ target[position++] = 0xdb
288
+ targetView.setUint32(position, length)
289
+ position += 4
290
+ }
291
+ position += length
292
+ } else if (type === 'number') {
293
+ if (value >>> 0 === value) {// positive integer, 32-bit or less
294
+ // positive uint
295
+ if (value < 0x40 || (value < 0x80 && this.useRecords === false)) {
296
+ target[position++] = value
297
+ } else if (value < 0x100) {
298
+ target[position++] = 0xcc
299
+ target[position++] = value
300
+ } else if (value < 0x10000) {
301
+ target[position++] = 0xcd
302
+ target[position++] = value >> 8
303
+ target[position++] = value & 0xff
304
+ } else {
305
+ target[position++] = 0xce
306
+ targetView.setUint32(position, value)
307
+ position += 4
308
+ }
309
+ } else if (value >> 0 === value) { // negative integer
310
+ if (value >= -0x20) {
311
+ target[position++] = 0x100 + value
312
+ } else if (value >= -0x80) {
313
+ target[position++] = 0xd0
314
+ target[position++] = value + 0x100
315
+ } else if (value >= -0x8000) {
316
+ target[position++] = 0xd1
317
+ targetView.setInt16(position, value)
318
+ position += 2
319
+ } else {
320
+ target[position++] = 0xd2
321
+ targetView.setInt32(position, value)
322
+ position += 4
323
+ }
324
+ } else {
325
+ let useFloat32
326
+ if ((useFloat32 = this.useFloat32) > 0 && value < 0x100000000 && value >= -0x80000000) {
327
+ target[position++] = 0xca
328
+ targetView.setFloat32(position, value)
329
+ let xShifted
330
+ if (useFloat32 < 4 ||
331
+ // this checks for rounding of numbers that were encoded in 32-bit float to nearest significant decimal digit that could be preserved
332
+ ((xShifted = value * mult10[((target[position] & 0x7f) << 1) | (target[position + 1] >> 7)]) >> 0) === xShifted) {
333
+ position += 4
334
+ return
335
+ } else
336
+ position-- // move back into position for writing a double
337
+ }
338
+ target[position++] = 0xcb
339
+ targetView.setFloat64(position, value)
340
+ position += 8
341
+ }
342
+ } else if (type === 'object') {
343
+ if (!value)
344
+ target[position++] = 0xc0
345
+ else {
346
+ if (referenceMap) {
347
+ let referee = referenceMap.get(value)
348
+ if (referee) {
349
+ if (!referee.id) {
350
+ let idsToInsert = referenceMap.idsToInsert || (referenceMap.idsToInsert = [])
351
+ referee.id = idsToInsert.push(referee)
352
+ }
353
+ target[position++] = 0xd6 // fixext 4
354
+ target[position++] = 0x70 // "p" for pointer
355
+ targetView.setUint32(position, referee.id)
356
+ position += 4
357
+ return
358
+ } else
359
+ referenceMap.set(value, { offset: position - start })
360
+ }
361
+ let constructor = value.constructor
362
+ if (constructor === Object) {
363
+ writeObject(value, true)
364
+ } else if (constructor === Array) {
365
+ length = value.length
366
+ if (length < 0x10) {
367
+ target[position++] = 0x90 | length
368
+ } else if (length < 0x10000) {
369
+ target[position++] = 0xdc
370
+ target[position++] = length >> 8
371
+ target[position++] = length & 0xff
372
+ } else {
373
+ target[position++] = 0xdd
374
+ targetView.setUint32(position, length)
375
+ position += 4
376
+ }
377
+ for (let i = 0; i < length; i++) {
378
+ pack(value[i])
379
+ }
380
+ } else if (constructor === Map) {
381
+ length = value.size
382
+ if (length < 0x10) {
383
+ target[position++] = 0x80 | length
384
+ } else if (length < 0x10000) {
385
+ target[position++] = 0xde
386
+ target[position++] = length >> 8
387
+ target[position++] = length & 0xff
388
+ } else {
389
+ target[position++] = 0xdf
390
+ targetView.setUint32(position, length)
391
+ position += 4
392
+ }
393
+ for (let [ key, entryValue ] of value) {
394
+ pack(key)
395
+ pack(entryValue)
396
+ }
397
+ } else {
398
+ for (let i = 0, l = extensions.length; i < l; i++) {
399
+ let extensionClass = extensionClasses[i]
400
+ if (value instanceof extensionClass) {
401
+ let extension = extensions[i]
402
+ if (extension.write) {
403
+ if (extension.type) {
404
+ target[position++] = 0xd4 // one byte "tag" extension
405
+ target[position++] = extension.type
406
+ target[position++] = 0
407
+ }
408
+ pack(extension.write.call(this, value))
409
+ return
410
+ }
411
+ let currentTarget = target
412
+ let currentTargetView = targetView
413
+ let currentPosition = position
414
+ target = null
415
+ let result
416
+ try {
417
+ result = extension.pack.call(this, value, (size) => {
418
+ // restore target and use it
419
+ target = currentTarget
420
+ currentTarget = null
421
+ position += size
422
+ if (position > safeEnd)
423
+ makeRoom(position)
424
+ return {
425
+ target, targetView, position: position - size
426
+ }
427
+ }, pack)
428
+ } finally {
429
+ // restore current target information (unless already restored)
430
+ if (currentTarget) {
431
+ target = currentTarget
432
+ targetView = currentTargetView
433
+ position = currentPosition
434
+ safeEnd = target.length - 10
435
+ }
436
+ }
437
+ if (result) {
438
+ if (result.length + position > safeEnd)
439
+ makeRoom(result.length + position)
440
+ position = writeExtensionData(result, target, position, extension.type)
441
+ }
442
+ return
443
+ }
444
+ }
445
+ // no extension found, write as object
446
+ writeObject(value, !value.hasOwnProperty) // if it doesn't have hasOwnProperty, don't do hasOwnProperty checks
447
+ }
448
+ }
449
+ } else if (type === 'boolean') {
450
+ target[position++] = value ? 0xc3 : 0xc2
451
+ } else if (type === 'bigint') {
452
+ if (value < (BigInt(1)<<BigInt(63)) && value >= -(BigInt(1)<<BigInt(63))) {
453
+ // use a signed int as long as it fits
454
+ target[position++] = 0xd3
455
+ targetView.setBigInt64(position, value)
456
+ } else if (value < (BigInt(1)<<BigInt(64)) && value > 0) {
457
+ // if we can fit an unsigned int, use that
458
+ target[position++] = 0xcf
459
+ targetView.setBigUint64(position, value)
460
+ } else {
461
+ // overflow
462
+ if (this.largeBigIntToFloat) {
463
+ target[position++] = 0xcb
464
+ targetView.setFloat64(position, Number(value))
465
+ } else {
466
+ throw new RangeError(value + ' was too large to fit in MessagePack 64-bit integer format, set largeBigIntToFloat to convert to float-64')
467
+ }
468
+ }
469
+ position += 8
470
+ } else if (type === 'undefined') {
471
+ if (this.encodeUndefinedAsNil)
472
+ target[position++] = 0xc0
473
+ else {
474
+ target[position++] = 0xd4 // a number of implementations use fixext1 with type 0, data 0 to denote undefined, so we follow suite
475
+ target[position++] = 0
476
+ target[position++] = 0
477
+ }
478
+ } else if (type === 'function') {
479
+ pack(this.writeFunction && this.writeFunction()) // if there is a writeFunction, use it, otherwise just encode as undefined
480
+ } else {
481
+ throw new Error('Unknown type: ' + type)
482
+ }
483
+ }
484
+
485
+ const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => {
486
+ // this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
487
+ let keys = Object.keys(object)
488
+ let length = keys.length
489
+ if (length < 0x10) {
490
+ target[position++] = 0x80 | length
491
+ } else if (length < 0x10000) {
492
+ target[position++] = 0xde
493
+ target[position++] = length >> 8
494
+ target[position++] = length & 0xff
495
+ } else {
496
+ target[position++] = 0xdf
497
+ targetView.setUint32(position, length)
498
+ position += 4
499
+ }
500
+ let key
501
+ for (let i = 0; i < length; i++) {
502
+ pack(key = keys[i])
503
+ pack(object[key])
504
+ }
505
+ } :
506
+ (object, safePrototype) => {
507
+ target[position++] = 0xde // always using map 16, so we can preallocate and set the length afterwards
508
+ let objectOffset = position - start
509
+ position += 2
510
+ let size = 0
511
+ for (let key in object) {
512
+ if (safePrototype || object.hasOwnProperty(key)) {
513
+ pack(key)
514
+ pack(object[key])
515
+ size++
516
+ }
517
+ }
518
+ target[objectOffset++ + start] = size >> 8
519
+ target[objectOffset + start] = size & 0xff
520
+ } :
521
+ (options.progressiveRecords && !useTwoByteRecords) ? // this is about 2% faster for highly stable structures, since it only requires one for-in loop (but much more expensive when new structure needs to be written)
522
+ (object, safePrototype) => {
523
+ let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
524
+ let objectOffset = position++ - start
525
+ let wroteKeys
526
+ for (let key in object) {
527
+ if (safePrototype || object.hasOwnProperty(key)) {
528
+ nextTransition = transition[key]
529
+ if (nextTransition)
530
+ transition = nextTransition
531
+ else {
532
+ // record doesn't exist, create full new record and insert it
533
+ let keys = Object.keys(object)
534
+ let lastTransition = transition
535
+ transition = structures.transitions
536
+ let newTransitions = 0
537
+ for (let i = 0, l = keys.length; i < l; i++) {
538
+ let key = keys[i]
539
+ nextTransition = transition[key]
540
+ if (!nextTransition) {
541
+ nextTransition = transition[key] = Object.create(null)
542
+ newTransitions++
543
+ }
544
+ transition = nextTransition
545
+ }
546
+ if (objectOffset + start + 1 == position) {
547
+ // first key, so we don't need to insert, we can just write record directly
548
+ position--
549
+ newRecord(transition, keys, newTransitions)
550
+ } else // otherwise we need to insert the record, moving existing data after the record
551
+ insertNewRecord(transition, keys, objectOffset, newTransitions)
552
+ wroteKeys = true
553
+ transition = lastTransition[key]
554
+ }
555
+ pack(object[key])
556
+ }
557
+ }
558
+ if (!wroteKeys) {
559
+ let recordId = transition[RECORD_SYMBOL]
560
+ if (recordId)
561
+ target[objectOffset + start] = recordId
562
+ else
563
+ insertNewRecord(transition, Object.keys(object), objectOffset, 0)
564
+ }
565
+ } :
566
+ (object, safePrototype) => {
567
+ let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
568
+ let newTransitions = 0
569
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
570
+ nextTransition = transition[key]
571
+ if (!nextTransition) {
572
+ nextTransition = transition[key] = Object.create(null)
573
+ newTransitions++
574
+ }
575
+ transition = nextTransition
576
+ }
577
+ let recordId = transition[RECORD_SYMBOL]
578
+ if (recordId) {
579
+ if (recordId >= 0x60 && useTwoByteRecords) {
580
+ target[position++] = ((recordId -= 0x60) & 0x1f) + 0x60
581
+ target[position++] = recordId >> 5
582
+ } else
583
+ target[position++] = recordId
584
+ } else {
585
+ newRecord(transition, transition.__keys__ || Object.keys(object), newTransitions)
586
+ }
587
+ // now write the values
588
+ for (let key in object)
589
+ if (safePrototype || object.hasOwnProperty(key))
590
+ pack(object[key])
591
+ }
592
+ const makeRoom = (end) => {
593
+ let newSize
594
+ if (end > 0x1000000) {
595
+ // special handling for really large buffers
596
+ if ((end - start) > MAX_BUFFER_SIZE)
597
+ throw new Error('Packed buffer would be larger than maximum buffer size')
598
+ newSize = Math.min(MAX_BUFFER_SIZE,
599
+ Math.round(Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2), 0x400000) / 0x1000) * 0x1000)
600
+ } else // faster handling for smaller buffers
601
+ newSize = ((Math.max((end - start) << 2, target.length - 1) >> 12) + 1) << 12
602
+ let newBuffer = new ByteArrayAllocate(newSize)
603
+ targetView = newBuffer.dataView = new DataView(newBuffer.buffer, 0, newSize)
604
+ end = Math.min(end, target.length)
605
+ if (target.copy)
606
+ target.copy(newBuffer, 0, start, end)
607
+ else
608
+ newBuffer.set(target.slice(start, end))
609
+ position -= start
610
+ start = 0
611
+ safeEnd = newBuffer.length - 10
612
+ return target = newBuffer
613
+ }
614
+ const newRecord = (transition, keys, newTransitions) => {
615
+ let recordId = structures.nextId
616
+ if (!recordId)
617
+ recordId = 0x40
618
+ if (recordId < sharedLimitId && this.shouldShareStructure && !this.shouldShareStructure(keys)) {
619
+ recordId = structures.nextOwnId
620
+ if (!(recordId < maxStructureId))
621
+ recordId = sharedLimitId
622
+ structures.nextOwnId = recordId + 1
623
+ } else {
624
+ if (recordId >= maxStructureId)// cycle back around
625
+ recordId = sharedLimitId
626
+ structures.nextId = recordId + 1
627
+ }
628
+ let highByte = keys.highByte = recordId >= 0x60 && useTwoByteRecords ? (recordId - 0x60) >> 5 : -1
629
+ transition[RECORD_SYMBOL] = recordId
630
+ transition.__keys__ = keys
631
+ structures[recordId - 0x40] = keys
632
+
633
+ if (recordId < sharedLimitId) {
634
+ keys.isShared = true
635
+ structures.sharedLength = recordId - 0x3f
636
+ hasSharedUpdate = true
637
+ if (highByte >= 0) {
638
+ target[position++] = (recordId & 0x1f) + 0x60
639
+ target[position++] = highByte
640
+ } else {
641
+ target[position++] = recordId
642
+ }
643
+ } else {
644
+ if (highByte >= 0) {
645
+ target[position++] = 0xd5 // fixext 2
646
+ target[position++] = 0x72 // "r" record defintion extension type
647
+ target[position++] = (recordId & 0x1f) + 0x60
648
+ target[position++] = highByte
649
+ } else {
650
+ target[position++] = 0xd4 // fixext 1
651
+ target[position++] = 0x72 // "r" record defintion extension type
652
+ target[position++] = recordId
653
+ }
654
+
655
+ if (newTransitions)
656
+ transitionsCount += serializationsSinceTransitionRebuild * newTransitions
657
+ // record the removal of the id, we can maintain our shared structure
658
+ if (recordIdsToRemove.length >= maxOwnStructures)
659
+ recordIdsToRemove.shift()[RECORD_SYMBOL] = 0 // we are cycling back through, and have to remove old ones
660
+ recordIdsToRemove.push(transition)
661
+ pack(keys)
662
+ }
663
+ }
664
+ const insertNewRecord = (transition, keys, insertionOffset, newTransitions) => {
665
+ let mainTarget = target
666
+ let mainPosition = position
667
+ let mainSafeEnd = safeEnd
668
+ let mainStart = start
669
+ target = keysTarget
670
+ position = 0
671
+ start = 0
672
+ if (!target)
673
+ keysTarget = target = new ByteArrayAllocate(8192)
674
+ safeEnd = target.length - 10
675
+ newRecord(transition, keys, newTransitions)
676
+ keysTarget = target
677
+ let keysPosition = position
678
+ target = mainTarget
679
+ position = mainPosition
680
+ safeEnd = mainSafeEnd
681
+ start = mainStart
682
+ if (keysPosition > 1) {
683
+ let newEnd = position + keysPosition - 1
684
+ if (newEnd > safeEnd)
685
+ makeRoom(newEnd)
686
+ let insertionPosition = insertionOffset + start
687
+ target.copyWithin(insertionPosition + keysPosition, insertionPosition + 1, position)
688
+ target.set(keysTarget.slice(0, keysPosition), insertionPosition)
689
+ position = newEnd
690
+ } else {
691
+ target[insertionOffset + start] = keysTarget[0]
692
+ }
693
+ }
694
+ const writeStruct = (object, safePrototype) => {
695
+ let newPosition = writeStructSlots(object, target, position, structures, makeRoom, (value, newPosition) => {
696
+ position = newPosition;
697
+ if (start > 0) {
698
+ pack(value);
699
+ if (start == 0)
700
+ return { position, targetView }; // indicate the buffer was re-allocated
701
+ } else
702
+ pack(value);
703
+ return position;
704
+ })
705
+ if (newPosition === 0) // bail and go to a msgpack object
706
+ return writeObject(object, true);
707
+ position = newPosition;
708
+ }
709
+ }
710
+ useBuffer(buffer) {
711
+ // this means we are finished using our own buffer and we can write over it safely
712
+ target = buffer
713
+ targetView = new DataView(target.buffer, target.byteOffset, target.byteLength)
714
+ position = 0
715
+ }
716
+ clearSharedData() {
717
+ if (this.structures)
718
+ this.structures = []
719
+ }
720
+ }
721
+
722
+ function copyBinary(source, target, targetOffset, offset, endOffset) {
723
+ while (offset < endOffset) {
724
+ target[targetOffset++] = source[offset++]
725
+ }
726
+ }
727
+
728
+ extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/, C1Type ]
729
+ extensions = [{
730
+ pack(date, allocateForWrite, pack) {
731
+ let seconds = date.getTime() / 1000
732
+ if ((this.useTimestamp32 || date.getMilliseconds() === 0) && seconds >= 0 && seconds < 0x100000000) {
733
+ // Timestamp 32
734
+ let { target, targetView, position} = allocateForWrite(6)
735
+ target[position++] = 0xd6
736
+ target[position++] = 0xff
737
+ targetView.setUint32(position, seconds)
738
+ } else if (seconds > 0 && seconds < 0x100000000) {
739
+ // Timestamp 64
740
+ let { target, targetView, position} = allocateForWrite(10)
741
+ target[position++] = 0xd7
742
+ target[position++] = 0xff
743
+ targetView.setUint32(position, date.getMilliseconds() * 4000000 + ((seconds / 1000 / 0x100000000) >> 0))
744
+ targetView.setUint32(position + 4, seconds)
745
+ } else if (isNaN(seconds)) {
746
+ if (this.onInvalidDate) {
747
+ allocateForWrite(0)
748
+ return pack(this.onInvalidDate())
749
+ }
750
+ // Intentionally invalid timestamp
751
+ let { target, targetView, position} = allocateForWrite(3)
752
+ target[position++] = 0xd4
753
+ target[position++] = 0xff
754
+ target[position++] = 0xff
755
+ } else {
756
+ // Timestamp 96
757
+ let { target, targetView, position} = allocateForWrite(15)
758
+ target[position++] = 0xc7
759
+ target[position++] = 12
760
+ target[position++] = 0xff
761
+ targetView.setUint32(position, date.getMilliseconds() * 1000000)
762
+ targetView.setBigInt64(position + 4, BigInt(Math.floor(seconds)))
763
+ }
764
+ }
765
+ }, {
766
+ pack(set, allocateForWrite, pack) {
767
+ let array = Array.from(set)
768
+ let { target, position} = allocateForWrite(this.moreTypes ? 3 : 0)
769
+ if (this.moreTypes) {
770
+ target[position++] = 0xd4
771
+ target[position++] = 0x73 // 's' for Set
772
+ target[position++] = 0
773
+ }
774
+ pack(array)
775
+ }
776
+ }, {
777
+ pack(error, allocateForWrite, pack) {
778
+ let { target, position} = allocateForWrite(this.moreTypes ? 3 : 0)
779
+ if (this.moreTypes) {
780
+ target[position++] = 0xd4
781
+ target[position++] = 0x65 // 'e' for error
782
+ target[position++] = 0
783
+ }
784
+ pack([ error.name, error.message ])
785
+ }
786
+ }, {
787
+ pack(regex, allocateForWrite, pack) {
788
+ let { target, position} = allocateForWrite(this.moreTypes ? 3 : 0)
789
+ if (this.moreTypes) {
790
+ target[position++] = 0xd4
791
+ target[position++] = 0x78 // 'x' for regeXp
792
+ target[position++] = 0
793
+ }
794
+ pack([ regex.source, regex.flags ])
795
+ }
796
+ }, {
797
+ pack(arrayBuffer, allocateForWrite) {
798
+ if (this.moreTypes)
799
+ writeExtBuffer(arrayBuffer, 0x10, allocateForWrite)
800
+ else
801
+ writeBuffer(hasNodeBuffer ? Buffer.from(arrayBuffer) : new Uint8Array(arrayBuffer), allocateForWrite)
802
+ }
803
+ }, {
804
+ pack(typedArray, allocateForWrite) {
805
+ let constructor = typedArray.constructor
806
+ if (constructor !== ByteArray && this.moreTypes)
807
+ writeExtBuffer(typedArray, typedArrays.indexOf(constructor.name), allocateForWrite)
808
+ else
809
+ writeBuffer(typedArray, allocateForWrite)
810
+ }
811
+ }, {
812
+ pack(c1, allocateForWrite) { // specific 0xC1 object
813
+ let { target, position} = allocateForWrite(1)
814
+ target[position] = 0xc1
815
+ }
816
+ }]
817
+
818
+ function writeExtBuffer(typedArray, type, allocateForWrite, encode) {
819
+ let length = typedArray.byteLength
820
+ if (length + 1 < 0x100) {
821
+ var { target, position } = allocateForWrite(4 + length)
822
+ target[position++] = 0xc7
823
+ target[position++] = length + 1
824
+ } else if (length + 1 < 0x10000) {
825
+ var { target, position } = allocateForWrite(5 + length)
826
+ target[position++] = 0xc8
827
+ target[position++] = (length + 1) >> 8
828
+ target[position++] = (length + 1) & 0xff
829
+ } else {
830
+ var { target, position, targetView } = allocateForWrite(7 + length)
831
+ target[position++] = 0xc9
832
+ targetView.setUint32(position, length + 1) // plus one for the type byte
833
+ position += 4
834
+ }
835
+ target[position++] = 0x74 // "t" for typed array
836
+ target[position++] = type
837
+ target.set(new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength), position)
838
+ }
839
+ function writeBuffer(buffer, allocateForWrite) {
840
+ let length = buffer.byteLength
841
+ var target, position
842
+ if (length < 0x100) {
843
+ var { target, position } = allocateForWrite(length + 2)
844
+ target[position++] = 0xc4
845
+ target[position++] = length
846
+ } else if (length < 0x10000) {
847
+ var { target, position } = allocateForWrite(length + 3)
848
+ target[position++] = 0xc5
849
+ target[position++] = length >> 8
850
+ target[position++] = length & 0xff
851
+ } else {
852
+ var { target, position, targetView } = allocateForWrite(length + 5)
853
+ target[position++] = 0xc6
854
+ targetView.setUint32(position, length)
855
+ position += 4
856
+ }
857
+ target.set(buffer, position)
858
+ }
859
+
860
+ function writeExtensionData(result, target, position, type) {
861
+ let length = result.length
862
+ switch (length) {
863
+ case 1:
864
+ target[position++] = 0xd4
865
+ break
866
+ case 2:
867
+ target[position++] = 0xd5
868
+ break
869
+ case 4:
870
+ target[position++] = 0xd6
871
+ break
872
+ case 8:
873
+ target[position++] = 0xd7
874
+ break
875
+ case 16:
876
+ target[position++] = 0xd8
877
+ break
878
+ default:
879
+ if (length < 0x100) {
880
+ target[position++] = 0xc7
881
+ target[position++] = length
882
+ } else if (length < 0x10000) {
883
+ target[position++] = 0xc8
884
+ target[position++] = length >> 8
885
+ target[position++] = length & 0xff
886
+ } else {
887
+ target[position++] = 0xc9
888
+ target[position++] = length >> 24
889
+ target[position++] = (length >> 16) & 0xff
890
+ target[position++] = (length >> 8) & 0xff
891
+ target[position++] = length & 0xff
892
+ }
893
+ }
894
+ target[position++] = type
895
+ target.set(result, position)
896
+ position += length
897
+ return position
898
+ }
899
+
900
+ function insertIds(serialized, idsToInsert) {
901
+ // insert the ids that need to be referenced for structured clones
902
+ let nextId
903
+ let distanceToMove = idsToInsert.length * 6
904
+ let lastEnd = serialized.length - distanceToMove
905
+ idsToInsert.sort((a, b) => a.offset > b.offset ? 1 : -1)
906
+ while (nextId = idsToInsert.pop()) {
907
+ let offset = nextId.offset
908
+ let id = nextId.id
909
+ serialized.copyWithin(offset + distanceToMove, offset, lastEnd)
910
+ distanceToMove -= 6
911
+ let position = offset + distanceToMove
912
+ serialized[position++] = 0xd6
913
+ serialized[position++] = 0x69 // 'i'
914
+ serialized[position++] = id >> 24
915
+ serialized[position++] = (id >> 16) & 0xff
916
+ serialized[position++] = (id >> 8) & 0xff
917
+ serialized[position++] = id & 0xff
918
+ lastEnd = offset
919
+ }
920
+ return serialized
921
+ }
922
+
923
+ function writeBundles(start, pack) {
924
+ if (bundledStrings.length > 0) {
925
+ targetView.setUint32(bundledStrings.position + start, position - bundledStrings.position - start)
926
+ let writeStrings = bundledStrings
927
+ bundledStrings = null
928
+ let startPosition = position
929
+ pack(writeStrings[0])
930
+ pack(writeStrings[1])
931
+ }
932
+ }
933
+
934
+ export function addExtension(extension) {
935
+ if (extension.Class) {
936
+ if (!extension.pack && !extension.write)
937
+ throw new Error('Extension has no pack or write function')
938
+ if (extension.pack && !extension.type)
939
+ throw new Error('Extension has no type (numeric code to identify the extension)')
940
+ extensionClasses.unshift(extension.Class)
941
+ extensions.unshift(extension)
942
+ }
943
+ unpackAddExtension(extension)
944
+ }
945
+ export function setWriteStructSlots(func) {
946
+ writeStructSlots = func;
947
+ }
948
+
949
+ let defaultPackr = new Packr({ useRecords: false })
950
+ export const pack = defaultPackr.pack
951
+ export const encode = defaultPackr.pack
952
+ export const Encoder = Packr
953
+ export { FLOAT32_OPTIONS } from './unpack.js'
954
+ import { FLOAT32_OPTIONS } from './unpack.js'
955
+ export const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS
956
+ export const REUSE_BUFFER_MODE = 512
957
+ export const RESET_BUFFER_MODE = 1024