libshv-js 3.16.1 → 3.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/chainpack.ts DELETED
@@ -1,660 +0,0 @@
1
- /* eslint-disable no-bitwise */
2
- import {utf8ToString} from './cpon';
3
- import {type RpcValue, type RpcValueType, type DateTime, Decimal, Double, type IMap, type Int, type MetaMap, RpcValueWithMetaData, type ShvMap, UInt, withOffset, shvMapType} from './rpcvalue';
4
- import {UnpackContext, PackContext} from './cpcontext';
5
-
6
- enum PackingSchema {
7
- IntThreshold = 64,
8
- Null = 128,
9
- UInt = 129,
10
- Int = 130,
11
- Double = 131,
12
- Bool = 132,
13
- Blob = 133,
14
- String = 134,
15
- List = 136,
16
- Map = 137,
17
- IMap = 138,
18
- MetaMap = 139,
19
- Decimal = 140,
20
- DateTime = 141,
21
- CString = 142,
22
- False = 253,
23
- True = 254,
24
- Term = 255,
25
- }
26
-
27
- const SHV_EPOCH_MSEC = 1_517_529_600_000;
28
- const INVALID_MIN_OFFSET_FROM_UTC = (-64 * 15);
29
-
30
- const divInt = (n: bigint, d: bigint) => {
31
- const r = n % d;
32
- return [(n - r) / d, r] as const;
33
- };
34
-
35
- const uint8ArrayBoBigint = (bytes: Uint8Array, type: 'Int' | 'UInt') => {
36
- let isNeg = false;
37
-
38
- if (type === 'Int') {
39
- if (bytes.length < 5) {
40
- const signMask = 0x80 >> bytes.length;
41
- isNeg = Boolean(bytes[0] & signMask);
42
- bytes[0] &= ~signMask;
43
- } else {
44
- isNeg = Boolean(bytes[1] & 128);
45
- bytes[1] &= ~128;
46
- }
47
- }
48
-
49
- let ret = 0n;
50
- for (const byte of bytes) {
51
- ret = (ret * BigInt(256)) + BigInt(byte);
52
- }
53
-
54
- if (isNeg) {
55
- ret = -ret;
56
- }
57
-
58
- return ret;
59
- };
60
-
61
- const numberToUint8Array = (num: bigint) => {
62
- let bytes = new Uint8Array(8);
63
- let len = 0;
64
- do {
65
- const res = divInt(num, 256n);
66
- num = res[0];
67
- bytes[len++] = Number(res[1]);
68
- } while (num !== 0n);
69
-
70
- bytes = bytes.subarray(0, len);
71
- bytes.reverse();
72
- return bytes;
73
- };
74
-
75
- const significantBitsCount = (num: bigint) => {
76
- let ret = 0;
77
- while (num > 0) {
78
- ret++;
79
- num >>= 1n;
80
- }
81
-
82
- return ret;
83
- };
84
-
85
- class ChainPackReader {
86
- ctx: UnpackContext;
87
-
88
- constructor(data: ArrayBuffer | UnpackContext) {
89
- this.ctx = data instanceof UnpackContext ? data : new UnpackContext(data);
90
- }
91
-
92
- /* eslint-disable complexity */
93
- read(): RpcValue {
94
- let meta: MetaMap | undefined;
95
- let codePointAt = this.ctx.getByte() as PackingSchema;
96
-
97
- if (codePointAt === PackingSchema.MetaMap) {
98
- meta = this.readMetaMap();
99
- codePointAt = this.ctx.getByte();
100
- }
101
-
102
- const implReturn = (x: RpcValueType) => {
103
- const ret = meta !== undefined ? new RpcValueWithMetaData(meta, x) : x;
104
- return ret;
105
- };
106
-
107
- if (codePointAt < PackingSchema.Null) {
108
- if (codePointAt >= PackingSchema.IntThreshold) {
109
- return implReturn(codePointAt - 64);
110
- }
111
-
112
- return implReturn(new UInt(codePointAt));
113
- }
114
-
115
- switch (codePointAt) {
116
- case PackingSchema.Null: {
117
- return implReturn(undefined);
118
- }
119
-
120
- case PackingSchema.True: {
121
- return implReturn(true);
122
- }
123
-
124
- case PackingSchema.False: {
125
- return implReturn(false);
126
- }
127
-
128
- case PackingSchema.Int: {
129
- return implReturn(this.readIntData());
130
- }
131
-
132
- case PackingSchema.UInt: {
133
- return implReturn(new UInt(this.readUIntData()));
134
- }
135
-
136
- case PackingSchema.Double: {
137
- const data = new Uint8Array(8);
138
- for (let i = 0; i < 8; i++) {
139
- data[i] = this.ctx.getByte();
140
- }
141
-
142
- return implReturn(new Double(new DataView(data.buffer).getFloat64(0, true))); // little endian
143
- }
144
-
145
- case PackingSchema.Decimal: {
146
- const mant = this.readIntData();
147
- const exp = this.readIntData();
148
- return implReturn(new Decimal(mant, exp));
149
- }
150
-
151
- case PackingSchema.DateTime: {
152
- let bi = this.readUIntDataHelper('UInt');
153
- const hasTzOffset = bi & 1n;
154
- const hasNotMsec = bi & 2n;
155
- bi >>= 2n;
156
-
157
- let offset = 0;
158
- if (hasTzOffset !== 0n) {
159
- offset = Number(bi & 0x7Fn);
160
- if ((offset & 0x40) !== 0) {
161
- // sign extension
162
- offset -= 128;
163
- }
164
-
165
- bi >>= 7n;
166
- }
167
-
168
- offset *= 15;
169
-
170
- if (offset === INVALID_MIN_OFFSET_FROM_UTC) {
171
- return implReturn(undefined);
172
- }
173
-
174
- let msec = Number(bi);
175
- if (hasNotMsec !== 0n) {
176
- msec *= 1000;
177
- }
178
-
179
- msec += SHV_EPOCH_MSEC;
180
- msec -= offset * 60_000;
181
- return withOffset(new Date(msec), offset ?? undefined);
182
- }
183
-
184
- case PackingSchema.Map: {
185
- return implReturn(this.readMap());
186
- }
187
-
188
- case PackingSchema.IMap: {
189
- return implReturn(this.readIMap());
190
- }
191
-
192
- case PackingSchema.List: {
193
- return implReturn(this.readList());
194
- }
195
-
196
- case PackingSchema.Blob: {
197
- const strLen = this.readUIntData();
198
- const arr = new Uint8Array(strLen);
199
- for (let i = 0; i < strLen; i++) {
200
- arr[i] = this.ctx.getByte();
201
- }
202
-
203
- return implReturn(arr.buffer);
204
- }
205
-
206
- case PackingSchema.String: {
207
- const strLen = this.readUIntData();
208
- const arr = new Uint8Array(strLen);
209
- for (let i = 0; i < strLen; i++) {
210
- arr[i] = this.ctx.getByte();
211
- }
212
-
213
- return implReturn(utf8ToString(arr.buffer));
214
- }
215
-
216
- case PackingSchema.CString: {
217
- // variation of CponReader.readCString()
218
- const pctx = new PackContext();
219
- while (true) {
220
- let b = this.ctx.getByte();
221
- if (b === '\\'.codePointAt(0)) {
222
- b = this.ctx.getByte();
223
- switch (b) {
224
- case '\\'.codePointAt(0):
225
- pctx.putByte('\\'.codePointAt(0)!);
226
- break;
227
- case '0'.codePointAt(0):
228
- pctx.putByte(0);
229
- break;
230
- default:
231
- pctx.putByte(b);
232
- break;
233
- }
234
- } else if (b === 0) {
235
- // end of string
236
- break;
237
- } else {
238
- pctx.putByte(b);
239
- }
240
- }
241
-
242
- return implReturn(utf8ToString(pctx.buffer()));
243
- }
244
-
245
- default:
246
- throw new TypeError('ChainPack - Invalid type info: ' + codePointAt);
247
- }
248
- }
249
- /* eslint-enable complexity */
250
-
251
- readUIntDataHelper(type: 'Int' | 'UInt') {
252
- let num = 0;
253
- const head = this.ctx.getByte();
254
- let bytesToReadCnt;
255
- switch (0) {
256
- case head & 128: {
257
- bytesToReadCnt = 0;
258
- num = head & 127;
259
- break;
260
- }
261
-
262
- case head & 64: {
263
- bytesToReadCnt = 1;
264
- num = head & 63;
265
- break;
266
- }
267
-
268
- case head & 32: {
269
- bytesToReadCnt = 2;
270
- num = head & 31;
271
- break;
272
- }
273
-
274
- case head & 16: {
275
- bytesToReadCnt = 3;
276
- num = head & 15;
277
- break;
278
- }
279
-
280
- default: {
281
- bytesToReadCnt = (head & 0xF) + 4;
282
- }
283
- }
284
-
285
- const bytes = new Uint8Array(bytesToReadCnt + 1);
286
- bytes[0] = num;
287
- for (let i = 0; i < bytesToReadCnt; i++) {
288
- const r = this.ctx.getByte();
289
- bytes[i + 1] = r;
290
- }
291
-
292
- return uint8ArrayBoBigint(bytes, type);
293
- }
294
-
295
- readIntData() {
296
- const val = this.readUIntDataHelper('Int');
297
- if (val <= Number.MIN_SAFE_INTEGER) {
298
- return Number.MIN_SAFE_INTEGER;
299
- }
300
-
301
- if (val >= Number.MAX_SAFE_INTEGER) {
302
- return Number.MAX_SAFE_INTEGER;
303
- }
304
-
305
- return Number(val);
306
- }
307
-
308
- readUIntData() {
309
- const val = this.readUIntDataHelper('UInt');
310
- if (val >= Number.MAX_SAFE_INTEGER) {
311
- return Number.MAX_SAFE_INTEGER;
312
- }
313
-
314
- return Number(val);
315
- }
316
-
317
- readList() {
318
- const lst = [];
319
- while (true) {
320
- const b = this.ctx.peekByte();
321
- if (b as PackingSchema === PackingSchema.Term) {
322
- this.ctx.getByte();
323
- break;
324
- }
325
-
326
- lst.push(this.read());
327
- }
328
-
329
- return lst;
330
- }
331
-
332
- readMetaMap() {
333
- return this.implReadMap('metamap');
334
- }
335
-
336
- readMap() {
337
- return this.implReadMap('map');
338
- }
339
-
340
- readIMap() {
341
- return this.implReadMap('imap');
342
- }
343
-
344
- private implReadMap(mapType: 'map'): ShvMap;
345
- private implReadMap(mapType: 'imap'): IMap;
346
- private implReadMap(mapType: 'metamap'): MetaMap;
347
- private implReadMap(mapType: 'map' | 'imap' | 'metamap') {
348
- const map: ShvMap | IMap | MetaMap = {
349
- [shvMapType]: mapType,
350
- };
351
- while (true) {
352
- const b = this.ctx.peekByte();
353
- if (b as PackingSchema === PackingSchema.Term) {
354
- this.ctx.getByte();
355
- break;
356
- }
357
-
358
- const key = this.read();
359
- const val = this.read();
360
- if (map[shvMapType] === 'metamap' && typeof key === 'string') {
361
- map[key] = val;
362
- } else if (map[shvMapType] === 'metamap' && typeof key === 'number') {
363
- map[Number(key)] = val;
364
- } else if (map[shvMapType] === 'map' && typeof key === 'string') {
365
- map[key] = val;
366
- } else if (map[shvMapType] === 'imap' && typeof key === 'number') {
367
- map[Number(key)] = val;
368
- } else {
369
- throw new TypeError('Malformed map, invalid key');
370
- }
371
- }
372
-
373
- return map;
374
- }
375
- }
376
-
377
- class ChainPackWriter {
378
- ctx: PackContext;
379
-
380
- constructor() {
381
- this.ctx = new PackContext();
382
- }
383
-
384
- write(rpcVal: RpcValue) {
385
- if (rpcVal instanceof RpcValueWithMetaData) {
386
- this.writeMeta(rpcVal.meta);
387
- rpcVal = rpcVal.value;
388
- }
389
-
390
- switch (true) {
391
- case rpcVal === undefined:
392
- this.ctx.putByte(PackingSchema.Null);
393
- break;
394
- case typeof rpcVal === 'boolean':
395
- this.ctx.putByte(rpcVal ? PackingSchema.True : PackingSchema.False);
396
- break;
397
- case typeof rpcVal === 'string':
398
- this.writeJSString(rpcVal);
399
- break;
400
- case rpcVal instanceof ArrayBuffer:
401
- this.writeBlob(rpcVal);
402
- break;
403
- case rpcVal instanceof UInt:
404
- this.writeUInt(rpcVal);
405
- break;
406
- case typeof rpcVal === 'number':
407
- this.writeInt(rpcVal);
408
- break;
409
- case rpcVal instanceof Decimal:
410
- this.writeDecimal(rpcVal);
411
- break;
412
- case Array.isArray(rpcVal):
413
- this.writeList(rpcVal);
414
- break;
415
- case rpcVal instanceof Date:
416
- this.writeDateTime(rpcVal);
417
- break;
418
- case rpcVal instanceof Double:
419
- this.writeDouble(rpcVal);
420
- break;
421
- case typeof rpcVal === 'object':
422
- switch (rpcVal[shvMapType]) {
423
- case 'imap':
424
- this.writeIMap(rpcVal);
425
- break;
426
- case 'map':
427
- this.writeMap(rpcVal);
428
- break;
429
- }
430
-
431
- break;
432
- default:
433
- console.log('Can\'t serialize', rpcVal);
434
- throw new Error('Can\'t serialize');
435
- }
436
- }
437
-
438
- writeUIntDataHelper(num: bigint, type: 'Int' | 'UInt') {
439
- const isNegative = num < 0;
440
- if (isNegative) {
441
- num = -num;
442
- }
443
-
444
- const significantBits = significantBitsCount(num) + (/* one more needed for sign bit */ type === 'Int' ? 1 : 0);
445
- const bytesNeeded = significantBits <= 28 ? Math.trunc((significantBits - 1) / 7) + 1 : Math.trunc((significantBits - 1) / 8) + 2;
446
-
447
- switch (bytesNeeded) {
448
- case 0:
449
- throw new Error(`Failed to count bytes needed for ${num}`);
450
- case 1:
451
- if (isNegative) {
452
- num |= 0b0100_0000n;
453
- }
454
-
455
- break;
456
- case 2:
457
- num |= 0b1000_0000n << 8n;
458
- if (isNegative) {
459
- num |= 0b0010_0000n << 8n;
460
- }
461
-
462
- break;
463
- case 3:
464
- num |= 0b1100_0000n << 16n;
465
- if (isNegative) {
466
- num |= 0b0001_0000n << 16n;
467
- }
468
-
469
- break;
470
- case 4:
471
- num |= 0b1110_0000n << 24n;
472
- if (isNegative) {
473
- num |= 0b0000_1000n << 24n;
474
- }
475
-
476
- break;
477
- default:
478
- num |= 0b1111_0000n << BigInt((bytesNeeded - 1) * 8);
479
- num |= BigInt(bytesNeeded - 4 /* n is offset by 4 */ - 1 /* for the control byte */) << BigInt((bytesNeeded - 1) * 8);
480
- if (isNegative) {
481
- num |= 0b1000_0000n << BigInt((bytesNeeded - 2) * 8);
482
- }
483
-
484
- break;
485
- }
486
-
487
- const bytes = numberToUint8Array(num);
488
- for (const byte of bytes) {
489
- this.ctx.putByte(byte);
490
- }
491
- }
492
-
493
- writeUIntData(num: number) {
494
- this.writeUIntDataHelper(BigInt(num), 'UInt');
495
- }
496
-
497
- writeIntData(snum: number) {
498
- this.writeUIntDataHelper(BigInt(snum), 'Int');
499
- }
500
-
501
- writeInt(n: Int) {
502
- if (Number(n) >= 0 && Number(n) < 64) {
503
- this.ctx.putByte(Number(n) + 64);
504
- return;
505
- }
506
-
507
- this.ctx.putByte(PackingSchema.Int);
508
- this.writeIntData(Number(n));
509
- }
510
-
511
- writeUInt(n: UInt) {
512
- if (Number(n) < 64) {
513
- this.ctx.putByte(Number(n));
514
- return;
515
- }
516
-
517
- this.ctx.putByte(PackingSchema.UInt);
518
- this.writeUIntData(Number(n));
519
- }
520
-
521
- writeJSString(str: string) {
522
- this.ctx.putByte(PackingSchema.String);
523
- const pctx = new PackContext();
524
- pctx.writeStringUtf8(str);
525
- this.writeUIntData(pctx.length);
526
- for (let i = 0; i < pctx.length; i++) {
527
- this.ctx.putByte(pctx.data[i]);
528
- }
529
- }
530
-
531
- writeDecimal(val: Decimal) {
532
- this.ctx.putByte(PackingSchema.Decimal);
533
- this.writeIntData(val.mantisa);
534
- this.writeIntData(val.exponent);
535
- }
536
-
537
- writeBlob(blob: ArrayBuffer) {
538
- this.ctx.putByte(PackingSchema.Blob);
539
- const arr = new Uint8Array(blob);
540
- this.writeUIntData(arr.length);
541
- for (const element of arr) {
542
- this.ctx.putByte(element);
543
- }
544
- }
545
-
546
- writeDateTime(dt: DateTime) {
547
- this.ctx.putByte(PackingSchema.DateTime);
548
-
549
- let msecs = (dt.getTime() + (60_000 * (dt.utc_offset ?? 0))) - SHV_EPOCH_MSEC;
550
- if (msecs < 0) {
551
- throw new RangeError('DateTime prior to 2018-02-02 are not supported in current ChainPack implementation.');
552
- }
553
-
554
- const ms = msecs % 1000;
555
- if (ms === 0) {
556
- msecs /= 1000;
557
- }
558
-
559
- let bi = BigInt(msecs);
560
- if (dt.utc_offset !== undefined && dt.utc_offset !== 0) {
561
- bi <<= 7n;
562
- bi |= BigInt((dt.utc_offset / 15) & 0x7F);
563
- }
564
-
565
- bi <<= 2n;
566
-
567
- if (dt.utc_offset !== undefined && dt.utc_offset !== 0) {
568
- bi |= 1n;
569
- }
570
-
571
- if (ms === 0) {
572
- bi |= 2n;
573
- }
574
-
575
- // save as signed int
576
- this.writeUIntDataHelper(bi, 'UInt');
577
- }
578
-
579
- writeDouble(double: Double) {
580
- this.ctx.putByte(PackingSchema.Double);
581
- const buffer = new ArrayBuffer(8);
582
- const view = new DataView(buffer);
583
-
584
- view.setFloat64(0, double.value, true);
585
-
586
- const data = new Uint8Array(buffer);
587
- for (let i = 0; i < 8; i++) {
588
- this.ctx.putByte(data[i]);
589
- }
590
- }
591
-
592
- writeList(lst: RpcValue[]) {
593
- this.ctx.putByte(PackingSchema.List);
594
- for (const element of lst) {
595
- this.write(element);
596
- }
597
-
598
- this.ctx.putByte(PackingSchema.Term);
599
- }
600
-
601
- writeMeta(map: MetaMap) {
602
- this.ctx.putByte(PackingSchema.MetaMap);
603
- this.writeMapContent(map);
604
- }
605
-
606
- writeIMap(map: IMap) {
607
- this.ctx.putByte(PackingSchema.IMap);
608
- this.writeMapContent(map);
609
- }
610
-
611
- writeMap(map: ShvMap) {
612
- this.ctx.putByte(PackingSchema.Map);
613
- this.writeMapContent(map);
614
- }
615
-
616
- writeMapContent(map: MetaMap | ShvMap | IMap) {
617
- for (const [key, value] of Object.entries<RpcValue>(map)) {
618
- if (value === undefined) {
619
- continue;
620
- }
621
-
622
- if (map[shvMapType] === 'imap') {
623
- const intKey = Number(key);
624
- if (Number.isNaN(intKey)) {
625
- throw new TypeError('Invalid NaN IMap key');
626
- }
627
-
628
- this.writeInt(intKey);
629
- } else if (map[shvMapType] === 'metamap') {
630
- const intKey = Number(key);
631
- if (Number.isNaN(intKey)) {
632
- this.writeJSString(key.toString());
633
- } else {
634
- this.writeInt(intKey);
635
- }
636
- } else {
637
- this.writeJSString(key.toString());
638
- }
639
-
640
- this.write(value);
641
- }
642
-
643
- this.ctx.putByte(PackingSchema.Term);
644
- }
645
- }
646
-
647
- const toChainPack = (value: RpcValue) => {
648
- const wr = new ChainPackWriter();
649
- wr.write(value);
650
- return wr.ctx.buffer();
651
- };
652
-
653
- const fromChainPack = (buffer: ArrayBuffer) => {
654
- const reader = new ChainPackReader(buffer);
655
- return reader.read();
656
- };
657
-
658
- const CHAINPACK_PROTOCOL_TYPE = 1;
659
-
660
- export {toChainPack, fromChainPack, ChainPackReader, ChainPackWriter, CHAINPACK_PROTOCOL_TYPE};