edinburgh 0.1.3 → 0.4.1

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.
@@ -0,0 +1,684 @@
1
+ const encoder = new TextEncoder();
2
+ const decoder = new TextDecoder('utf-8', { fatal: true });
3
+ // EOD marker for container parsing
4
+ const EOD = Symbol('EOD');
5
+ const BASE64_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$';
6
+ const BASE64_LOOKUP = new Uint8Array(128).fill(255); // Use 255 as invalid marker;
7
+ for (let i = 0; i < BASE64_CHARS.length; ++i)
8
+ BASE64_LOOKUP[BASE64_CHARS.charCodeAt(i)] = i;
9
+ const USE_COLORS = typeof process !== 'undefined' && process?.stdout?.isTTY;
10
+ const COLORS = USE_COLORS ? ['\x1b[32m', '\x1b[33m', '\x1b[34m', '\x1b[35m'] : ['']; // green, yellow, blue, magenta
11
+ const RESET_COLOR = USE_COLORS ? '\x1b[0m' : '';
12
+ const ERROR_COLOR = USE_COLORS ? '\x1b[31m' : ''; // red
13
+ let toStringTermCount = 0;
14
+ let useExtendedLogging = typeof process !== 'undefined' ? !!process.env?.DATAPACK_EXTENDED_LOGGING : false;
15
+ /**
16
+ * A byte buffer for efficient reading and writing of primitive values and bit sequences.
17
+ *
18
+ * The DataPack class provides methods for serializing and deserializing various data types
19
+ * including numbers, strings, bit sequences, and other primitive values to/from byte buffers.
20
+ * It supports both reading and writing operations with automatic buffer management.
21
+ */
22
+ export default class DataPack {
23
+ buffer;
24
+ dataView;
25
+ readPos = 0;
26
+ writePos = 0;
27
+ /**
28
+ * Backward compatibility: Access to the internal buffer
29
+ */
30
+ get _buffer() {
31
+ return this.buffer;
32
+ }
33
+ /**
34
+ * Create a new DataPack instance.
35
+ * @param data - Optional initial data as Uint8Array or buffer size as number.
36
+ */
37
+ constructor(data = 1900) {
38
+ if (data instanceof Uint8Array) {
39
+ this.buffer = data;
40
+ this.writePos = data.length;
41
+ }
42
+ else {
43
+ this.buffer = new Uint8Array(data);
44
+ }
45
+ }
46
+ /**
47
+ * Helper function to write a multi-byte integer with length prefix
48
+ * @param value - The value to write
49
+ * @param headerType - The type bits (0-7) for the header
50
+ * @param invertBytes - Whether to invert bytes (for negative numbers)
51
+ * @param invertByteCount - Whether to invert the byte count (for type 0)
52
+ */
53
+ writeMultiByteNumber(value, headerType, invertBytes = false, invertByteCount = false) {
54
+ let byteCount = 0;
55
+ let temp = value;
56
+ while (temp > 0) {
57
+ byteCount++;
58
+ temp = Math.floor(temp / 256);
59
+ }
60
+ const encodedByteCount = invertByteCount ? ((~byteCount) & 0x1F) : byteCount;
61
+ this.buffer[this.writePos++] = (headerType << 5) | encodedByteCount;
62
+ let max = 1;
63
+ for (let j = 0; j < byteCount; j++)
64
+ max *= 256;
65
+ for (let i = 0; i < byteCount; i++) {
66
+ max = Math.floor(max / 256);
67
+ let byte = Math.floor(value / max) % 256;
68
+ if (invertBytes)
69
+ byte ^= 0xFF;
70
+ this.buffer[this.writePos++] = byte;
71
+ }
72
+ }
73
+ /**
74
+ * Helper function to read a multi-byte integer with length prefix
75
+ * @param byteCount - Number of bytes to read
76
+ * @param invertBytes - Whether to invert bytes (for negative numbers)
77
+ * @returns The read value
78
+ */
79
+ readMultiByteNumber(byteCount, invertBytes = false) {
80
+ if (this.readPos + byteCount > this.writePos)
81
+ this.notEnoughData('number');
82
+ let value = 0;
83
+ let multiplier = 1;
84
+ for (let i = byteCount - 1; i >= 0; i--) {
85
+ let byte = this.buffer[this.readPos + i];
86
+ if (invertBytes)
87
+ byte ^= 0xFF;
88
+ value += byte * multiplier;
89
+ multiplier *= 256;
90
+ }
91
+ this.readPos += byteCount;
92
+ return value;
93
+ }
94
+ notEnoughData(type) {
95
+ throw new Error(`Not enough data to read a ${type} at position ${this.readPos} in\n${this.toString(true)}`);
96
+ }
97
+ /**
98
+ * Each data item starts with a single byte. The high 3 bits indicate the type.
99
+ * The low 5 bits indicate a size or a subtype, depending on the type.
100
+ *
101
+ * 0: negative integer (byte count encoded as bitwise NOT in lower bits)
102
+ * 1: small integer 0..31 (encoded in lower bits)
103
+ * 2: small integer 32...63 (encoded in lower bits)
104
+ * 3: integer (byte count encoded in lower bits)
105
+ * 4:
106
+ * 0: float64 (8 bytes follow)
107
+ * 1: undefined
108
+ * 2: null
109
+ * 3: true
110
+ * 4: false
111
+ * 5: array start (followed by a items until EOD)
112
+ * 6: object start (followed by key+value pairs until EOD)
113
+ * 7: map start (followed by key+value pairs until EOD)
114
+ * 8: set start (followed by items until EOD)
115
+ * 9: EOD
116
+ * 10: identifier (6 byte positive int follows, represented as a base64 string of length 8)
117
+ * 11: null-terminated string
118
+ * 12: Date/Time (varint with whole seconds since epoch follows)
119
+ * 13: custom type, followed by name string and data value
120
+ * 5: short string (length in lower bits, 0-31)
121
+ * 6: string (byte count of length in lower bits)
122
+ * 7: blob (byte count of length in lower bits)
123
+ */
124
+ write(data) {
125
+ // Pessimistic capacity allocation - covers most cases
126
+ this.ensureCapacity(33); // 1 byte header + up to 32 bytes for large integers/length encoding
127
+ if (typeof data === 'number') {
128
+ if (Number.isInteger(data) && data <= Number.MAX_SAFE_INTEGER && data >= Number.MIN_SAFE_INTEGER) {
129
+ if (data >= 0) {
130
+ if (data < 32) { // Type 1: small positive integer 0...31
131
+ this.buffer[this.writePos++] = (1 << 5) | data;
132
+ }
133
+ else if (data < 64) { // Type 2: small positive integer 32...63
134
+ this.buffer[this.writePos++] = (2 << 5) | (data - 32);
135
+ }
136
+ else { // Type 3: positive integer (byte count in lower bits)
137
+ this.writeMultiByteNumber(data - 64, 3);
138
+ }
139
+ }
140
+ else {
141
+ // Type 0: negative integer (byte count as bitwise NOT in lower bits)
142
+ this.writeMultiByteNumber(-data, 0, true, true);
143
+ }
144
+ }
145
+ else {
146
+ // Type 4, subtype 0: float64
147
+ this.buffer[this.writePos++] = (4 << 5) | 0;
148
+ this.dataView ||= new DataView(this.buffer.buffer, this.buffer.byteOffset, this.buffer.byteLength);
149
+ this.dataView.setFloat64(this.writePos, data);
150
+ this.writePos += 8;
151
+ }
152
+ }
153
+ else if (data === undefined) {
154
+ this.buffer[this.writePos++] = (4 << 5) | 1;
155
+ }
156
+ else if (data === null) {
157
+ this.buffer[this.writePos++] = (4 << 5) | 2;
158
+ }
159
+ else if (data === true) {
160
+ this.buffer[this.writePos++] = (4 << 5) | 3;
161
+ }
162
+ else if (data === false) {
163
+ this.buffer[this.writePos++] = (4 << 5) | 4;
164
+ }
165
+ else if (typeof data === 'string') {
166
+ const encoded = encoder.encode(data);
167
+ if (encoded.length < 32) {
168
+ // Type 5: short string (length in lower bits, 0-31)
169
+ this.buffer[this.writePos++] = (5 << 5) | encoded.length;
170
+ }
171
+ else {
172
+ // Type 6: string (byte count of length in lower bits)
173
+ this.writeMultiByteNumber(encoded.length, 6);
174
+ }
175
+ this.ensureCapacity(encoded.length);
176
+ this.buffer.set(encoded, this.writePos);
177
+ this.writePos += encoded.length;
178
+ }
179
+ else if (data instanceof Uint8Array) {
180
+ // Type 7: blob (byte count of length in lower bits)
181
+ this.writeMultiByteNumber(data.length, 7);
182
+ this.ensureCapacity(data.length);
183
+ this.buffer.set(data, this.writePos);
184
+ this.writePos += data.length;
185
+ }
186
+ else if (Array.isArray(data)) {
187
+ // Type 4, subtype 5: array start
188
+ this.buffer[this.writePos++] = (4 << 5) | 5;
189
+ for (const item of data) {
190
+ this.write(item);
191
+ }
192
+ this.buffer[this.writePos++] = (4 << 5) | 9; // EOD
193
+ }
194
+ else if (data instanceof Map) {
195
+ // Type 4, subtype 7: map start
196
+ this.buffer[this.writePos++] = (4 << 5) | 7;
197
+ for (const [key, value] of data) {
198
+ this.write(key);
199
+ this.write(value);
200
+ }
201
+ this.buffer[this.writePos++] = (4 << 5) | 9; // EOD
202
+ }
203
+ else if (data instanceof Set) {
204
+ // Type 4, subtype 8: set start
205
+ this.buffer[this.writePos++] = (4 << 5) | 8;
206
+ for (const item of data) {
207
+ this.write(item);
208
+ }
209
+ this.buffer[this.writePos++] = (4 << 5) | 9; // EOD
210
+ }
211
+ else if (data instanceof Date) {
212
+ // Type 4, subtype 12: Date/Time
213
+ this.buffer[this.writePos++] = (4 << 5) | 12;
214
+ // Write a varint -- whole seconds should be plenty of resolution
215
+ this.write(Math.floor(data.getTime() / 1000));
216
+ }
217
+ else if (typeof data === 'object') {
218
+ if (data instanceof CustomData) {
219
+ this.writeCustom(data.name, data.data);
220
+ }
221
+ else if (typeof data.toDataPack === 'function') {
222
+ data.toDataPack(this);
223
+ }
224
+ else {
225
+ this.writeAsObject(data);
226
+ }
227
+ }
228
+ else {
229
+ throw new Error(`Unsupported data type: ${typeof data}`);
230
+ }
231
+ return this;
232
+ }
233
+ read(customConverters) {
234
+ if (this.readPos > this.writePos) {
235
+ throw new Error('Not enough data');
236
+ }
237
+ const header = this.buffer[this.readPos++];
238
+ const type = (header >> 5) & 0x07;
239
+ const subtype = header & 0x1F;
240
+ switch (type) {
241
+ case 0: {
242
+ // Negative integer (byte count as bitwise NOT in lower bits)
243
+ const byteCount = (~subtype) & 0x1F;
244
+ return -this.readMultiByteNumber(byteCount, true);
245
+ }
246
+ case 1: {
247
+ // Small positive integer 0...31
248
+ return subtype;
249
+ }
250
+ case 2: {
251
+ // Small positive integer 32..63
252
+ return subtype + 32;
253
+ }
254
+ case 3: {
255
+ // Positive integer (byte count in lower bits)
256
+ return this.readMultiByteNumber(subtype) + 64;
257
+ }
258
+ case 4: {
259
+ // Floats, special values and collections
260
+ switch (subtype) {
261
+ case 0: {
262
+ // float64
263
+ if (this.readPos + 8 > this.writePos)
264
+ this.notEnoughData('float64');
265
+ this.dataView ||= new DataView(this.buffer.buffer, this.buffer.byteOffset, this.buffer.byteLength);
266
+ const result = this.dataView.getFloat64(this.readPos);
267
+ this.readPos += 8;
268
+ return result;
269
+ }
270
+ case 1: return undefined;
271
+ case 2: return null;
272
+ case 3: return true;
273
+ case 4: return false;
274
+ case 5: {
275
+ // Array start
276
+ const result = [];
277
+ while (true) {
278
+ const nextValue = this.read(customConverters);
279
+ if (nextValue === EOD)
280
+ break;
281
+ result.push(nextValue);
282
+ }
283
+ return result;
284
+ }
285
+ case 6: {
286
+ // Plain object or custom class instance
287
+ let result = {};
288
+ while (true) {
289
+ const key = this.read(customConverters);
290
+ if (key === EOD)
291
+ break;
292
+ const value = this.read(customConverters);
293
+ result[key] = value;
294
+ }
295
+ return result;
296
+ }
297
+ case 7: {
298
+ // Map start
299
+ const result = new Map();
300
+ while (true) {
301
+ const key = this.read(customConverters);
302
+ if (key === EOD)
303
+ break;
304
+ const value = this.read(customConverters);
305
+ result.set(key, value);
306
+ }
307
+ return result;
308
+ }
309
+ case 8: {
310
+ // Set start
311
+ const result = new Set();
312
+ while (true) {
313
+ const value = this.read(customConverters);
314
+ if (value === EOD)
315
+ break;
316
+ result.add(value);
317
+ }
318
+ return result;
319
+ }
320
+ case 9: return EOD;
321
+ case 10: {
322
+ // Identifier (6 byte positive int follows, represented as a base64 string of length 8)
323
+ --this.readPos; // readIdentifier() will read the header byte again
324
+ return this.readIdentifier();
325
+ }
326
+ case 11: {
327
+ // Null-terminated string
328
+ const start = this.readPos;
329
+ let end = start;
330
+ while (true) {
331
+ if (end >= this.writePos)
332
+ this.notEnoughData('null-terminated string');
333
+ if (this.buffer[end] === 0)
334
+ break;
335
+ end++;
336
+ }
337
+ this.readPos = end + 1; // Skip the null terminator
338
+ return decoder.decode(this.buffer.subarray(start, end));
339
+ }
340
+ case 12: {
341
+ // Date/Time (varint with whole seconds since epoch follows)
342
+ const seconds = this.readPositiveInt();
343
+ return new Date(seconds * 1000);
344
+ }
345
+ case 13: {
346
+ // Custom type, followed by name string and data value
347
+ const name = this.readString();
348
+ const data = this.read();
349
+ return (customConverters && name in customConverters) ? customConverters[name](data) : new CustomData(name, data);
350
+ }
351
+ default: throw new Error(`Unknown type 4 subtype: ${subtype}`);
352
+ }
353
+ }
354
+ case 5: {
355
+ // Short string (length in lower bits, 0-31)
356
+ const length = subtype;
357
+ if (this.readPos + length > this.writePos)
358
+ this.notEnoughData('short string');
359
+ const bytes = this.buffer.subarray(this.readPos, this.readPos + length);
360
+ this.readPos += length;
361
+ return decoder.decode(bytes);
362
+ }
363
+ case 6: {
364
+ // String (byte count of length in lower bits)
365
+ const length = this.readMultiByteNumber(subtype);
366
+ if (this.readPos + length > this.writePos)
367
+ this.notEnoughData('string');
368
+ const bytes = this.buffer.subarray(this.readPos, this.readPos + length);
369
+ this.readPos += length;
370
+ return decoder.decode(bytes);
371
+ }
372
+ case 7: {
373
+ // Blob (byte count of length in lower bits)
374
+ const length = this.readMultiByteNumber(subtype);
375
+ if (this.readPos + length > this.writePos)
376
+ this.notEnoughData('blob');
377
+ // This makes an actual copy of the underlying buffer data
378
+ const result = this.buffer.slice(this.readPos, this.readPos + length);
379
+ this.readPos += length;
380
+ return new Uint8Array(result); // Return a copy
381
+ }
382
+ default: throw new Error(`Unknown type: ${type}`);
383
+ }
384
+ }
385
+ /**
386
+ * Ensure the buffer has capacity for additional bytes.
387
+ * @param bytesNeeded - Number of additional bytes needed.
388
+ */
389
+ ensureCapacity(bytesNeeded) {
390
+ const needed = this.writePos + bytesNeeded;
391
+ if (needed <= this.buffer.length)
392
+ return;
393
+ // Grow by 1.5x or the needed amount, whichever is larger
394
+ const newCapacity = Math.max(needed, Math.floor(this.buffer.length * 1.5));
395
+ const newBuffer = new Uint8Array(newCapacity);
396
+ newBuffer.set(this.buffer.subarray(0, this.writePos));
397
+ this.buffer = newBuffer;
398
+ delete this.dataView;
399
+ }
400
+ readNumber() {
401
+ const result = this.read();
402
+ if (typeof result !== 'number') {
403
+ throw new Error('Expected number but got ' + typeof result);
404
+ }
405
+ return result;
406
+ }
407
+ readDate() {
408
+ const result = this.read();
409
+ if (!(result instanceof Date)) {
410
+ throw new Error('Expected Date but got ' + typeof result);
411
+ }
412
+ return result;
413
+ }
414
+ readPositiveInt(limit) {
415
+ const result = this.read();
416
+ if (typeof result !== 'number' || !Number.isInteger(result) || result < 0 || (limit !== undefined && result >= limit)) {
417
+ throw new Error(`Expected positive integer < ${limit} but got ${result}`);
418
+ }
419
+ return result;
420
+ }
421
+ readBoolean() {
422
+ const result = this.read();
423
+ if (typeof result !== 'boolean') {
424
+ throw new Error('Expected boolean but got ' + typeof result);
425
+ }
426
+ return result;
427
+ }
428
+ readUint8Array() {
429
+ const result = this.read();
430
+ if (!(result instanceof Uint8Array)) {
431
+ throw new Error('Expected Uint8Array but got ' + typeof result);
432
+ }
433
+ return result;
434
+ }
435
+ readString() {
436
+ const result = this.read();
437
+ if (typeof result !== 'string') {
438
+ throw new Error('Expected string but got ' + typeof result);
439
+ }
440
+ return result;
441
+ }
442
+ writeIdentifier(id) {
443
+ let num = 0;
444
+ if (typeof id === 'number') {
445
+ num = id;
446
+ }
447
+ else {
448
+ if (id.length !== 8) {
449
+ throw new Error(`Identifier must be exactly 8 characters, got ${id.length}`);
450
+ }
451
+ // Convert base64 string to 48-bit number
452
+ let value;
453
+ for (let i = 0; i < 8; i++) {
454
+ const char = id.charCodeAt(i);
455
+ if (char > 127 || (value = BASE64_LOOKUP[char]) === 255) {
456
+ throw new Error(`Invalid base64 character: ${id[i]}`);
457
+ }
458
+ num = num * 64 + value;
459
+ }
460
+ }
461
+ // Write type 4, subtype 10 header
462
+ this.ensureCapacity(7); // 1 byte header + 6 bytes for the number
463
+ this.buffer[this.writePos++] = (4 << 5) | 10;
464
+ // Write the 6-byte number in big-endian format
465
+ for (let i = 5; i >= 0; i--) {
466
+ this.buffer[this.writePos + i] = num % 256;
467
+ num = Math.floor(num / 256);
468
+ }
469
+ this.writePos += 6;
470
+ return this;
471
+ }
472
+ readIdentifier() {
473
+ let num = this.readIdentifierNumber();
474
+ // Convert 48-bit number back to 8-character base64 string
475
+ let id = '';
476
+ for (let i = 0; i < 8; i++) {
477
+ id = BASE64_CHARS[num % 64] + id;
478
+ num = Math.floor(num / 64);
479
+ }
480
+ return id;
481
+ }
482
+ readIdentifierNumber() {
483
+ // Read the 6-byte number in big-endian format
484
+ if (this.readPos + 7 > this.writePos)
485
+ this.notEnoughData('identifier');
486
+ const header = this.buffer[this.readPos++];
487
+ if (header !== ((4 << 5) | 10)) {
488
+ throw new Error('Invalid identifier header');
489
+ }
490
+ let num = 0;
491
+ for (let i = 0; i < 6; i++) {
492
+ num = (num * 256) + this.buffer[this.readPos++];
493
+ }
494
+ return num;
495
+ }
496
+ /**
497
+ * Like writeString but writes without a length prefix and with a null terminator, for ordered storage.
498
+ * Can be read with {@link read} or {@link readString} just like any other string.
499
+ * @param str - The string to write. May not contain null characters.
500
+ */
501
+ writeOrderedString(str) {
502
+ const utf8Bytes = new TextEncoder().encode(str);
503
+ if (utf8Bytes.includes(0)) {
504
+ throw new Error('String contains null character');
505
+ }
506
+ this.ensureCapacity(utf8Bytes.length + 2);
507
+ this.buffer[this.writePos++] = (4 << 5) | 11; // Null-terminated string
508
+ this.buffer.set(utf8Bytes, this.writePos);
509
+ this.writePos += utf8Bytes.length;
510
+ this.buffer[this.writePos++] = 0; // Null terminator
511
+ return this;
512
+ }
513
+ toUint8Array(copyBuffer = true, startPos = 0, endPos = this.writePos) {
514
+ return copyBuffer ? this.buffer.slice(startPos, endPos) : this.buffer.subarray(startPos, endPos);
515
+ }
516
+ toBuffer() {
517
+ return this.buffer.buffer.slice(0, this.writePos);
518
+ }
519
+ clone(copyBuffer, readPos = 0, writePos = this.writePos) {
520
+ if (copyBuffer) {
521
+ return new DataPack(this.buffer.slice(readPos, writePos));
522
+ }
523
+ else {
524
+ const pack = new DataPack(this.buffer);
525
+ pack.readPos = readPos;
526
+ pack.writePos = writePos;
527
+ return pack;
528
+ }
529
+ }
530
+ writeCustom(name, data) {
531
+ this.buffer[this.writePos++] = (4 << 5) | 13;
532
+ this.write(name);
533
+ this.write(data);
534
+ }
535
+ writeAsObject(obj) {
536
+ // Type 4, subtype 6: object start
537
+ this.buffer[this.writePos++] = (4 << 5) | 6;
538
+ for (const key of Object.keys(obj)) {
539
+ this.write(key);
540
+ this.write(obj[key]);
541
+ }
542
+ this.buffer[this.writePos++] = (4 << 5) | 9; // EOD
543
+ return this;
544
+ }
545
+ writeCollection(type, bodyFunc) {
546
+ let subType = { 'array': 5, 'object': 6, 'map': 7, 'set': 8 }[type];
547
+ if (!subType)
548
+ throw new Error(`Invalid collection type: ${type}`);
549
+ this.buffer[this.writePos++] = (4 << 5) | subType; // Collection start
550
+ bodyFunc((type === 'array' || type === 'set') ? (value) => {
551
+ this.write(value);
552
+ } : (name, value) => {
553
+ this.write(name);
554
+ this.write(value);
555
+ });
556
+ this.buffer[this.writePos++] = (4 << 5) | 9; // EOD
557
+ return this;
558
+ }
559
+ /**
560
+ * Increment the last byte of the buffer. If it was already 255 set it to 0 and
561
+ * increment the previous byte, and so on. If all bytes were 255, return undefined.
562
+ * This is useful for creating an exclusive end key for range scans.
563
+ * This may result in a DataPack instance that cannot be parsed (or represented by
564
+ * {@link toString}).
565
+ */
566
+ increment() {
567
+ for (let byte = this.writePos - 1; byte >= 0; byte--) {
568
+ if (this.buffer[byte] === 255) {
569
+ // Byte is all 1s, set to 0 and continue
570
+ this.buffer[byte] = 0;
571
+ }
572
+ else {
573
+ this.buffer[byte]++;
574
+ return this;
575
+ }
576
+ }
577
+ return undefined; // All bytes were 255 (and are now 0)
578
+ }
579
+ toString(extended = undefined, startPos = 0, endPos = this.writePos) {
580
+ if (extended === undefined)
581
+ extended = useExtendedLogging;
582
+ let oldReadPos = this.readPos;
583
+ this.readPos = startPos;
584
+ let lastPos = 0;
585
+ let vals = '';
586
+ let hexs = '';
587
+ const orgTermCount = toStringTermCount;
588
+ const resetColor = orgTermCount > 0 ? COLORS[(orgTermCount - 1) % COLORS.length] : RESET_COLOR;
589
+ try {
590
+ while (this.readPos < endPos) {
591
+ const color = COLORS[toStringTermCount++ % COLORS.length];
592
+ vals += color + toText(this.read()) + ' ';
593
+ if (extended) {
594
+ hexs += color;
595
+ while (lastPos < this.readPos) {
596
+ hexs += this.buffer[lastPos].toString(16).padStart(2, '0') + ' ';
597
+ lastPos++;
598
+ }
599
+ }
600
+ }
601
+ }
602
+ catch (e) {
603
+ if (!extended) {
604
+ this.readPos = oldReadPos;
605
+ toStringTermCount = orgTermCount;
606
+ return this.toString(true, startPos, endPos);
607
+ }
608
+ vals += ERROR_COLOR + "ERROR ";
609
+ hexs += ERROR_COLOR;
610
+ while (lastPos < this.writePos) {
611
+ hexs += this.buffer[lastPos].toString(16).padStart(2, '0') + ' ';
612
+ lastPos++;
613
+ }
614
+ }
615
+ this.readPos = oldReadPos;
616
+ if (!orgTermCount)
617
+ toStringTermCount = 0;
618
+ if (extended) {
619
+ return "DataPack{" + hexs + resetColor + "→ " + vals.trimEnd() + resetColor + "}";
620
+ }
621
+ else {
622
+ return "DataPack{" + vals.trimEnd() + resetColor + "}";
623
+ }
624
+ }
625
+ [Symbol.for('nodejs.util.inspect.custom')]() {
626
+ return this.toString();
627
+ }
628
+ readAvailable() {
629
+ return this.readPos < this.writePos;
630
+ }
631
+ static generateIdentifier() {
632
+ // Combine a timestamp with randomness, to create locality of reference as well as a high chance of uniqueness.
633
+ // Bits 9...48 are the date in ms (wrapping about every 34 years), providing some `locality of reference
634
+ // Bit 0...14 are random bits (partly overlapping with the date, adding up to 62ms of jitter)
635
+ let num = Math.floor(+new Date() * (1 << 8) + Math.random() * (1 << 14));
636
+ let id = '';
637
+ for (let i = 0; i < 8; i++) {
638
+ id = BASE64_CHARS[num & 0x3f] + id;
639
+ num = Math.floor(num / 64);
640
+ }
641
+ return id;
642
+ }
643
+ static createBuffer(...args) {
644
+ const pack = new DataPack();
645
+ for (const arg of args) {
646
+ pack.write(arg);
647
+ }
648
+ return pack.toBuffer();
649
+ }
650
+ static createUint8Array(...args) {
651
+ const pack = new DataPack();
652
+ for (const arg of args) {
653
+ pack.write(arg);
654
+ }
655
+ return pack.toUint8Array(false);
656
+ }
657
+ }
658
+ function toText(v) {
659
+ if (v === undefined)
660
+ return 'undefined';
661
+ if (typeof v === 'object' && v !== null) {
662
+ if (v instanceof Uint8Array)
663
+ return new DataPack(v).toString();
664
+ if (v instanceof Array)
665
+ return '[' + v.map(vv => toText(vv)).join(', ') + ']';
666
+ if (v instanceof Map)
667
+ return 'Map{' + Array.from(v.entries()).map(([k, val]) => `${toText(k)}=>${toText(val)}`).join(', ') + '}';
668
+ if (v instanceof Set)
669
+ return 'Set{' + Array.from(v.values()).map(vv => toText(vv)).join(', ') + '}';
670
+ if (typeof v === 'object' && v !== null)
671
+ return '{' + Object.entries(v).map(([k, val]) => `${k}:${toText(val)}`).join(', ') + '}';
672
+ }
673
+ return JSON.stringify(v);
674
+ }
675
+ class CustomData {
676
+ name;
677
+ data;
678
+ constructor(name, data) {
679
+ this.name = name;
680
+ this.data = data;
681
+ }
682
+ }
683
+ DataPack.prototype.CustomData = CustomData;
684
+ //# sourceMappingURL=datapack.js.map