msgpackr 1.6.1 → 1.6.2

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,935 @@
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
+ 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 || (value < 0x80 && this.useRecords === false)) {
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
+ if (bundledStrings.length > 0) {
906
+ targetView.setUint32(bundledStrings.position + start, position - bundledStrings.position - start)
907
+ let writeStrings = bundledStrings
908
+ bundledStrings = null
909
+ let startPosition = position
910
+ pack(writeStrings[0])
911
+ pack(writeStrings[1])
912
+ }
913
+ }
914
+
915
+ export function addExtension(extension) {
916
+ if (extension.Class) {
917
+ if (!extension.pack && !extension.write)
918
+ throw new Error('Extension has no pack or write function')
919
+ if (extension.pack && !extension.type)
920
+ throw new Error('Extension has no type (numeric code to identify the extension)')
921
+ extensionClasses.unshift(extension.Class)
922
+ extensions.unshift(extension)
923
+ }
924
+ unpackAddExtension(extension)
925
+ }
926
+
927
+ let defaultPackr = new Packr({ useRecords: false })
928
+ export const pack = defaultPackr.pack
929
+ export const encode = defaultPackr.pack
930
+ export const Encoder = Packr
931
+ export { FLOAT32_OPTIONS } from './unpack.js'
932
+ import { FLOAT32_OPTIONS } from './unpack.js'
933
+ export const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS
934
+ export const REUSE_BUFFER_MODE = 512
935
+ export const RESET_BUFFER_MODE = 1024