libshv-js 3.9.0 → 3.9.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/dist/access.d.ts +69 -0
- package/dist/access.js +94 -0
- package/dist/chainpack.d.ts +39 -0
- package/dist/chainpack.js +555 -0
- package/dist/cpcontext.d.ts +18 -0
- package/dist/cpcontext.js +74 -0
- package/dist/cpon.d.ts +55 -0
- package/dist/cpon.js +891 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/json.d.ts +3 -0
- package/dist/json.js +113 -0
- package/dist/rpcmessage.d.ts +73 -0
- package/dist/rpcmessage.js +37 -0
- package/dist/rpcvalue.d.ts +56 -0
- package/dist/rpcvalue.js +85 -0
- package/dist/shv-ri.d.ts +12 -0
- package/dist/shv-ri.js +51 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +6 -0
- package/dist/ws-client.d.ts +239 -0
- package/dist/ws-client.js +560 -0
- package/package.json +1 -1
- package/src/rpcmessage.ts +2 -0
- package/src/ws-client.ts +4 -3
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
/* eslint-disable no-bitwise */
|
|
2
|
+
import { utf8ToString } from './cpon';
|
|
3
|
+
import { Decimal, Double, RpcValueWithMetaData, UInt, withOffset, shvMapType } from './rpcvalue';
|
|
4
|
+
import { UnpackContext, PackContext } from './cpcontext';
|
|
5
|
+
var PackingSchema;
|
|
6
|
+
(function (PackingSchema) {
|
|
7
|
+
PackingSchema[PackingSchema["IntThreshold"] = 64] = "IntThreshold";
|
|
8
|
+
PackingSchema[PackingSchema["Null"] = 128] = "Null";
|
|
9
|
+
PackingSchema[PackingSchema["UInt"] = 129] = "UInt";
|
|
10
|
+
PackingSchema[PackingSchema["Int"] = 130] = "Int";
|
|
11
|
+
PackingSchema[PackingSchema["Double"] = 131] = "Double";
|
|
12
|
+
PackingSchema[PackingSchema["Bool"] = 132] = "Bool";
|
|
13
|
+
PackingSchema[PackingSchema["Blob"] = 133] = "Blob";
|
|
14
|
+
PackingSchema[PackingSchema["String"] = 134] = "String";
|
|
15
|
+
PackingSchema[PackingSchema["List"] = 136] = "List";
|
|
16
|
+
PackingSchema[PackingSchema["Map"] = 137] = "Map";
|
|
17
|
+
PackingSchema[PackingSchema["IMap"] = 138] = "IMap";
|
|
18
|
+
PackingSchema[PackingSchema["MetaMap"] = 139] = "MetaMap";
|
|
19
|
+
PackingSchema[PackingSchema["Decimal"] = 140] = "Decimal";
|
|
20
|
+
PackingSchema[PackingSchema["DateTime"] = 141] = "DateTime";
|
|
21
|
+
PackingSchema[PackingSchema["CString"] = 142] = "CString";
|
|
22
|
+
PackingSchema[PackingSchema["False"] = 253] = "False";
|
|
23
|
+
PackingSchema[PackingSchema["True"] = 254] = "True";
|
|
24
|
+
PackingSchema[PackingSchema["Term"] = 255] = "Term";
|
|
25
|
+
})(PackingSchema || (PackingSchema = {}));
|
|
26
|
+
const SHV_EPOCH_MSEC = 1_517_529_600_000;
|
|
27
|
+
const INVALID_MIN_OFFSET_FROM_UTC = (-64 * 15);
|
|
28
|
+
const divInt = (n, d) => {
|
|
29
|
+
const r = n % d;
|
|
30
|
+
return [(n - r) / d, r];
|
|
31
|
+
};
|
|
32
|
+
const uint8ArrayBoBigint = (bytes, type) => {
|
|
33
|
+
let isNeg = false;
|
|
34
|
+
if (type === 'Int') {
|
|
35
|
+
if (bytes.length < 5) {
|
|
36
|
+
const signMask = 0x80 >> bytes.length;
|
|
37
|
+
isNeg = Boolean(bytes[0] & signMask);
|
|
38
|
+
bytes[0] &= ~signMask;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
isNeg = Boolean(bytes[1] & 128);
|
|
42
|
+
bytes[1] &= ~128;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
let ret = 0n;
|
|
46
|
+
for (const byte of bytes) {
|
|
47
|
+
ret = (ret * 256n) + BigInt(byte);
|
|
48
|
+
}
|
|
49
|
+
if (isNeg) {
|
|
50
|
+
ret = -ret;
|
|
51
|
+
}
|
|
52
|
+
return ret;
|
|
53
|
+
};
|
|
54
|
+
const numberToUint8Array = (num) => {
|
|
55
|
+
let bytes = new Uint8Array(8);
|
|
56
|
+
let len = 0;
|
|
57
|
+
do {
|
|
58
|
+
const res = divInt(num, 256n);
|
|
59
|
+
num = res[0];
|
|
60
|
+
bytes[len++] = Number(res[1]);
|
|
61
|
+
} while (num !== 0n);
|
|
62
|
+
bytes = bytes.subarray(0, len);
|
|
63
|
+
bytes.reverse();
|
|
64
|
+
return bytes;
|
|
65
|
+
};
|
|
66
|
+
const significantBitsCount = (num) => {
|
|
67
|
+
let ret = 0;
|
|
68
|
+
while (num > 0) {
|
|
69
|
+
ret++;
|
|
70
|
+
num >>= 1n;
|
|
71
|
+
}
|
|
72
|
+
return ret;
|
|
73
|
+
};
|
|
74
|
+
class ChainPackReader {
|
|
75
|
+
ctx;
|
|
76
|
+
constructor(data) {
|
|
77
|
+
this.ctx = data instanceof UnpackContext ? data : new UnpackContext(data);
|
|
78
|
+
}
|
|
79
|
+
/* eslint-disable complexity */
|
|
80
|
+
read() {
|
|
81
|
+
let meta;
|
|
82
|
+
let codePointAt = this.ctx.getByte();
|
|
83
|
+
if (codePointAt === PackingSchema.MetaMap) {
|
|
84
|
+
meta = this.readMetaMap();
|
|
85
|
+
codePointAt = this.ctx.getByte();
|
|
86
|
+
}
|
|
87
|
+
const implReturn = (x) => {
|
|
88
|
+
const ret = meta !== undefined ? new RpcValueWithMetaData(meta, x) : x;
|
|
89
|
+
return ret;
|
|
90
|
+
};
|
|
91
|
+
if (codePointAt < PackingSchema.Null) {
|
|
92
|
+
if (codePointAt >= PackingSchema.IntThreshold) {
|
|
93
|
+
return implReturn(codePointAt - 64);
|
|
94
|
+
}
|
|
95
|
+
return implReturn(new UInt(codePointAt));
|
|
96
|
+
}
|
|
97
|
+
switch (codePointAt) {
|
|
98
|
+
case PackingSchema.Null: {
|
|
99
|
+
return implReturn(undefined);
|
|
100
|
+
}
|
|
101
|
+
case PackingSchema.True: {
|
|
102
|
+
return implReturn(true);
|
|
103
|
+
}
|
|
104
|
+
case PackingSchema.False: {
|
|
105
|
+
return implReturn(false);
|
|
106
|
+
}
|
|
107
|
+
case PackingSchema.Int: {
|
|
108
|
+
return implReturn(this.readIntData());
|
|
109
|
+
}
|
|
110
|
+
case PackingSchema.UInt: {
|
|
111
|
+
return implReturn(new UInt(this.readUIntData()));
|
|
112
|
+
}
|
|
113
|
+
case PackingSchema.Double: {
|
|
114
|
+
const data = new Uint8Array(8);
|
|
115
|
+
for (let i = 0; i < 8; i++) {
|
|
116
|
+
data[i] = this.ctx.getByte();
|
|
117
|
+
}
|
|
118
|
+
return implReturn(new Double(new DataView(data.buffer).getFloat64(0, true))); // little endian
|
|
119
|
+
}
|
|
120
|
+
case PackingSchema.Decimal: {
|
|
121
|
+
const mant = this.readIntData();
|
|
122
|
+
const exp = this.readIntData();
|
|
123
|
+
return implReturn(new Decimal(mant, exp));
|
|
124
|
+
}
|
|
125
|
+
case PackingSchema.DateTime: {
|
|
126
|
+
let bi = this.readUIntDataHelper('UInt');
|
|
127
|
+
const hasTzOffset = bi & 1n;
|
|
128
|
+
const hasNotMsec = bi & 2n;
|
|
129
|
+
bi >>= 2n;
|
|
130
|
+
let offset = 0;
|
|
131
|
+
if (hasTzOffset !== 0n) {
|
|
132
|
+
offset = Number(bi & 0x7fn);
|
|
133
|
+
if ((offset & 0x40) !== 0) {
|
|
134
|
+
// sign extension
|
|
135
|
+
offset -= 128;
|
|
136
|
+
}
|
|
137
|
+
bi >>= 7n;
|
|
138
|
+
}
|
|
139
|
+
offset *= 15;
|
|
140
|
+
if (offset === INVALID_MIN_OFFSET_FROM_UTC) {
|
|
141
|
+
return implReturn(undefined);
|
|
142
|
+
}
|
|
143
|
+
let msec = Number(bi);
|
|
144
|
+
if (hasNotMsec !== 0n) {
|
|
145
|
+
msec *= 1000;
|
|
146
|
+
}
|
|
147
|
+
msec += SHV_EPOCH_MSEC;
|
|
148
|
+
msec -= offset * 60_000;
|
|
149
|
+
return withOffset(new Date(msec), offset ?? undefined);
|
|
150
|
+
}
|
|
151
|
+
case PackingSchema.Map: {
|
|
152
|
+
return implReturn(this.readMap());
|
|
153
|
+
}
|
|
154
|
+
case PackingSchema.IMap: {
|
|
155
|
+
return implReturn(this.readIMap());
|
|
156
|
+
}
|
|
157
|
+
case PackingSchema.List: {
|
|
158
|
+
return implReturn(this.readList());
|
|
159
|
+
}
|
|
160
|
+
case PackingSchema.Blob: {
|
|
161
|
+
const strLen = this.readUIntData();
|
|
162
|
+
const arr = new Uint8Array(strLen);
|
|
163
|
+
for (let i = 0; i < strLen; i++) {
|
|
164
|
+
arr[i] = this.ctx.getByte();
|
|
165
|
+
}
|
|
166
|
+
return implReturn(arr.buffer);
|
|
167
|
+
}
|
|
168
|
+
case PackingSchema.String: {
|
|
169
|
+
const strLen = this.readUIntData();
|
|
170
|
+
const arr = new Uint8Array(strLen);
|
|
171
|
+
for (let i = 0; i < strLen; i++) {
|
|
172
|
+
arr[i] = this.ctx.getByte();
|
|
173
|
+
}
|
|
174
|
+
return implReturn(utf8ToString(arr.buffer));
|
|
175
|
+
}
|
|
176
|
+
case PackingSchema.CString: {
|
|
177
|
+
// variation of CponReader.readCString()
|
|
178
|
+
const pctx = new PackContext();
|
|
179
|
+
while (true) {
|
|
180
|
+
let b = this.ctx.getByte();
|
|
181
|
+
if (b === '\\'.codePointAt(0)) {
|
|
182
|
+
b = this.ctx.getByte();
|
|
183
|
+
switch (b) {
|
|
184
|
+
case '\\'.codePointAt(0):
|
|
185
|
+
pctx.putByte('\\'.codePointAt(0));
|
|
186
|
+
break;
|
|
187
|
+
case '0'.codePointAt(0):
|
|
188
|
+
pctx.putByte(0);
|
|
189
|
+
break;
|
|
190
|
+
default:
|
|
191
|
+
pctx.putByte(b);
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else if (b === 0) {
|
|
196
|
+
// end of string
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
pctx.putByte(b);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return implReturn(utf8ToString(pctx.buffer()));
|
|
204
|
+
}
|
|
205
|
+
default:
|
|
206
|
+
throw new TypeError('ChainPack - Invalid type info: ' + codePointAt);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/* eslint-enable complexity */
|
|
210
|
+
readUIntDataHelper(type) {
|
|
211
|
+
let num = 0;
|
|
212
|
+
const head = this.ctx.getByte();
|
|
213
|
+
let bytesToReadCnt;
|
|
214
|
+
switch (0) {
|
|
215
|
+
case head & 128: {
|
|
216
|
+
bytesToReadCnt = 0;
|
|
217
|
+
num = head & 127;
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
case head & 64: {
|
|
221
|
+
bytesToReadCnt = 1;
|
|
222
|
+
num = head & 63;
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
case head & 32: {
|
|
226
|
+
bytesToReadCnt = 2;
|
|
227
|
+
num = head & 31;
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
case head & 16: {
|
|
231
|
+
bytesToReadCnt = 3;
|
|
232
|
+
num = head & 15;
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
default: {
|
|
236
|
+
bytesToReadCnt = (head & 0xF) + 4;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const bytes = new Uint8Array(bytesToReadCnt + 1);
|
|
240
|
+
bytes[0] = num;
|
|
241
|
+
for (let i = 0; i < bytesToReadCnt; i++) {
|
|
242
|
+
const r = this.ctx.getByte();
|
|
243
|
+
bytes[i + 1] = r;
|
|
244
|
+
}
|
|
245
|
+
return uint8ArrayBoBigint(bytes, type);
|
|
246
|
+
}
|
|
247
|
+
readIntData() {
|
|
248
|
+
const val = this.readUIntDataHelper('Int');
|
|
249
|
+
if (val <= Number.MIN_SAFE_INTEGER) {
|
|
250
|
+
return Number.MIN_SAFE_INTEGER;
|
|
251
|
+
}
|
|
252
|
+
if (val >= Number.MAX_SAFE_INTEGER) {
|
|
253
|
+
return Number.MAX_SAFE_INTEGER;
|
|
254
|
+
}
|
|
255
|
+
return Number(val);
|
|
256
|
+
}
|
|
257
|
+
readUIntData() {
|
|
258
|
+
const val = this.readUIntDataHelper('UInt');
|
|
259
|
+
if (val >= Number.MAX_SAFE_INTEGER) {
|
|
260
|
+
return Number.MAX_SAFE_INTEGER;
|
|
261
|
+
}
|
|
262
|
+
return Number(val);
|
|
263
|
+
}
|
|
264
|
+
readList() {
|
|
265
|
+
const lst = [];
|
|
266
|
+
while (true) {
|
|
267
|
+
const b = this.ctx.peekByte();
|
|
268
|
+
if (b === PackingSchema.Term) {
|
|
269
|
+
this.ctx.getByte();
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
lst.push(this.read());
|
|
273
|
+
}
|
|
274
|
+
return lst;
|
|
275
|
+
}
|
|
276
|
+
readMetaMap() {
|
|
277
|
+
return this.implReadMap('metamap');
|
|
278
|
+
}
|
|
279
|
+
readMap() {
|
|
280
|
+
return this.implReadMap('map');
|
|
281
|
+
}
|
|
282
|
+
readIMap() {
|
|
283
|
+
return this.implReadMap('imap');
|
|
284
|
+
}
|
|
285
|
+
implReadMap(mapType) {
|
|
286
|
+
const map = {
|
|
287
|
+
[shvMapType]: mapType,
|
|
288
|
+
};
|
|
289
|
+
while (true) {
|
|
290
|
+
const b = this.ctx.peekByte();
|
|
291
|
+
if (b === PackingSchema.Term) {
|
|
292
|
+
this.ctx.getByte();
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
const key = this.read();
|
|
296
|
+
const val = this.read();
|
|
297
|
+
if (map[shvMapType] === 'metamap' && typeof key === 'string') {
|
|
298
|
+
map[key] = val;
|
|
299
|
+
}
|
|
300
|
+
else if (map[shvMapType] === 'metamap' && typeof key === 'number') {
|
|
301
|
+
map[Number(key)] = val;
|
|
302
|
+
}
|
|
303
|
+
else if (map[shvMapType] === 'map' && typeof key === 'string') {
|
|
304
|
+
map[key] = val;
|
|
305
|
+
}
|
|
306
|
+
else if (map[shvMapType] === 'imap' && typeof key === 'number') {
|
|
307
|
+
map[Number(key)] = val;
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
throw new TypeError('Malformed map, invalid key');
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return map;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
class ChainPackWriter {
|
|
317
|
+
ctx;
|
|
318
|
+
constructor() {
|
|
319
|
+
this.ctx = new PackContext();
|
|
320
|
+
}
|
|
321
|
+
write(rpcVal) {
|
|
322
|
+
if (rpcVal instanceof RpcValueWithMetaData) {
|
|
323
|
+
this.writeMeta(rpcVal.meta);
|
|
324
|
+
rpcVal = rpcVal.value;
|
|
325
|
+
}
|
|
326
|
+
switch (true) {
|
|
327
|
+
case rpcVal === undefined:
|
|
328
|
+
this.ctx.putByte(PackingSchema.Null);
|
|
329
|
+
break;
|
|
330
|
+
case typeof rpcVal === 'boolean':
|
|
331
|
+
this.ctx.putByte(rpcVal ? PackingSchema.True : PackingSchema.False);
|
|
332
|
+
break;
|
|
333
|
+
case typeof rpcVal === 'string':
|
|
334
|
+
this.writeJSString(rpcVal);
|
|
335
|
+
break;
|
|
336
|
+
case rpcVal instanceof ArrayBuffer:
|
|
337
|
+
this.writeBlob(rpcVal);
|
|
338
|
+
break;
|
|
339
|
+
case rpcVal instanceof UInt:
|
|
340
|
+
this.writeUInt(rpcVal);
|
|
341
|
+
break;
|
|
342
|
+
case typeof rpcVal === 'number':
|
|
343
|
+
this.writeInt(rpcVal);
|
|
344
|
+
break;
|
|
345
|
+
case rpcVal instanceof Decimal:
|
|
346
|
+
this.writeDecimal(rpcVal);
|
|
347
|
+
break;
|
|
348
|
+
case Array.isArray(rpcVal):
|
|
349
|
+
this.writeList(rpcVal);
|
|
350
|
+
break;
|
|
351
|
+
case rpcVal instanceof Date:
|
|
352
|
+
this.writeDateTime(rpcVal);
|
|
353
|
+
break;
|
|
354
|
+
case rpcVal instanceof Double:
|
|
355
|
+
this.writeDouble(rpcVal);
|
|
356
|
+
break;
|
|
357
|
+
case typeof rpcVal === 'object':
|
|
358
|
+
switch (rpcVal[shvMapType]) {
|
|
359
|
+
case 'imap':
|
|
360
|
+
this.writeIMap(rpcVal);
|
|
361
|
+
break;
|
|
362
|
+
case 'map':
|
|
363
|
+
this.writeMap(rpcVal);
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
break;
|
|
367
|
+
default:
|
|
368
|
+
console.log('Can\'t serialize', rpcVal);
|
|
369
|
+
throw new Error('Can\'t serialize');
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
writeUIntDataHelper(num, type) {
|
|
373
|
+
const isNegative = num < 0;
|
|
374
|
+
if (isNegative) {
|
|
375
|
+
num = -num;
|
|
376
|
+
}
|
|
377
|
+
const significantBits = significantBitsCount(num) + ( /* one more needed for sign bit */type === 'Int' ? 1 : 0);
|
|
378
|
+
const bytesNeeded = significantBits <= 28 ? Math.trunc((significantBits - 1) / 7) + 1 : Math.trunc((significantBits - 1) / 8) + 2;
|
|
379
|
+
switch (bytesNeeded) {
|
|
380
|
+
case 0:
|
|
381
|
+
throw new Error(`Failed to count bytes needed for ${num}`);
|
|
382
|
+
case 1:
|
|
383
|
+
if (isNegative) {
|
|
384
|
+
num |= 64n;
|
|
385
|
+
}
|
|
386
|
+
break;
|
|
387
|
+
case 2:
|
|
388
|
+
num |= 128n << 8n;
|
|
389
|
+
if (isNegative) {
|
|
390
|
+
num |= 32n << 8n;
|
|
391
|
+
}
|
|
392
|
+
break;
|
|
393
|
+
case 3:
|
|
394
|
+
num |= 192n << 16n;
|
|
395
|
+
if (isNegative) {
|
|
396
|
+
num |= 16n << 16n;
|
|
397
|
+
}
|
|
398
|
+
break;
|
|
399
|
+
case 4:
|
|
400
|
+
num |= 224n << 24n;
|
|
401
|
+
if (isNegative) {
|
|
402
|
+
num |= 8n << 24n;
|
|
403
|
+
}
|
|
404
|
+
break;
|
|
405
|
+
default:
|
|
406
|
+
num |= 240n << BigInt((bytesNeeded - 1) * 8);
|
|
407
|
+
num |= BigInt(bytesNeeded - 4 /* n is offset by 4 */ - 1 /* for the control byte */) << BigInt((bytesNeeded - 1) * 8);
|
|
408
|
+
if (isNegative) {
|
|
409
|
+
num |= 128n << BigInt((bytesNeeded - 2) * 8);
|
|
410
|
+
}
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
const bytes = numberToUint8Array(num);
|
|
414
|
+
for (const byte of bytes) {
|
|
415
|
+
this.ctx.putByte(byte);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
writeUIntData(num) {
|
|
419
|
+
this.writeUIntDataHelper(BigInt(num), 'UInt');
|
|
420
|
+
}
|
|
421
|
+
writeIntData(snum) {
|
|
422
|
+
this.writeUIntDataHelper(BigInt(snum), 'Int');
|
|
423
|
+
}
|
|
424
|
+
writeInt(n) {
|
|
425
|
+
if (Number(n) >= 0 && Number(n) < 64) {
|
|
426
|
+
this.ctx.putByte(Number(n) + 64);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
this.ctx.putByte(PackingSchema.Int);
|
|
430
|
+
this.writeIntData(Number(n));
|
|
431
|
+
}
|
|
432
|
+
writeUInt(n) {
|
|
433
|
+
if (Number(n) < 64) {
|
|
434
|
+
this.ctx.putByte(Number(n));
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
this.ctx.putByte(PackingSchema.UInt);
|
|
438
|
+
this.writeUIntData(Number(n));
|
|
439
|
+
}
|
|
440
|
+
writeJSString(str) {
|
|
441
|
+
this.ctx.putByte(PackingSchema.String);
|
|
442
|
+
const pctx = new PackContext();
|
|
443
|
+
pctx.writeStringUtf8(str);
|
|
444
|
+
this.writeUIntData(pctx.length);
|
|
445
|
+
for (let i = 0; i < pctx.length; i++) {
|
|
446
|
+
this.ctx.putByte(pctx.data[i]);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
writeDecimal(val) {
|
|
450
|
+
this.ctx.putByte(PackingSchema.Decimal);
|
|
451
|
+
this.writeIntData(val.mantisa);
|
|
452
|
+
this.writeIntData(val.exponent);
|
|
453
|
+
}
|
|
454
|
+
writeBlob(blob) {
|
|
455
|
+
this.ctx.putByte(PackingSchema.Blob);
|
|
456
|
+
const arr = new Uint8Array(blob);
|
|
457
|
+
this.writeUIntData(arr.length);
|
|
458
|
+
for (const element of arr) {
|
|
459
|
+
this.ctx.putByte(element);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
writeDateTime(dt) {
|
|
463
|
+
this.ctx.putByte(PackingSchema.DateTime);
|
|
464
|
+
let msecs = (dt.getTime() + (60_000 * (dt.utc_offset ?? 0))) - SHV_EPOCH_MSEC;
|
|
465
|
+
if (msecs < 0) {
|
|
466
|
+
throw new RangeError('DateTime prior to 2018-02-02 are not supported in current ChainPack implementation.');
|
|
467
|
+
}
|
|
468
|
+
const ms = msecs % 1000;
|
|
469
|
+
if (ms === 0) {
|
|
470
|
+
msecs /= 1000;
|
|
471
|
+
}
|
|
472
|
+
let bi = BigInt(msecs);
|
|
473
|
+
if (dt.utc_offset !== undefined && dt.utc_offset !== 0) {
|
|
474
|
+
bi <<= 7n;
|
|
475
|
+
bi |= BigInt((dt.utc_offset / 15) & 0x7F);
|
|
476
|
+
}
|
|
477
|
+
bi <<= 2n;
|
|
478
|
+
if (dt.utc_offset !== undefined && dt.utc_offset !== 0) {
|
|
479
|
+
bi |= 1n;
|
|
480
|
+
}
|
|
481
|
+
if (ms === 0) {
|
|
482
|
+
bi |= 2n;
|
|
483
|
+
}
|
|
484
|
+
// save as signed int
|
|
485
|
+
this.writeUIntDataHelper(bi, 'UInt');
|
|
486
|
+
}
|
|
487
|
+
writeDouble(double) {
|
|
488
|
+
this.ctx.putByte(PackingSchema.Double);
|
|
489
|
+
const buffer = new ArrayBuffer(8);
|
|
490
|
+
const view = new DataView(buffer);
|
|
491
|
+
view.setFloat64(0, double.value, true);
|
|
492
|
+
const data = new Uint8Array(buffer);
|
|
493
|
+
for (let i = 0; i < 8; i++) {
|
|
494
|
+
this.ctx.putByte(data[i]);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
writeList(lst) {
|
|
498
|
+
this.ctx.putByte(PackingSchema.List);
|
|
499
|
+
for (const element of lst) {
|
|
500
|
+
this.write(element);
|
|
501
|
+
}
|
|
502
|
+
this.ctx.putByte(PackingSchema.Term);
|
|
503
|
+
}
|
|
504
|
+
writeMeta(map) {
|
|
505
|
+
this.ctx.putByte(PackingSchema.MetaMap);
|
|
506
|
+
this.writeMapContent(map);
|
|
507
|
+
}
|
|
508
|
+
writeIMap(map) {
|
|
509
|
+
this.ctx.putByte(PackingSchema.IMap);
|
|
510
|
+
this.writeMapContent(map);
|
|
511
|
+
}
|
|
512
|
+
writeMap(map) {
|
|
513
|
+
this.ctx.putByte(PackingSchema.Map);
|
|
514
|
+
this.writeMapContent(map);
|
|
515
|
+
}
|
|
516
|
+
writeMapContent(map) {
|
|
517
|
+
for (const [key, value] of Object.entries(map)) {
|
|
518
|
+
if (value === undefined) {
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
if (map[shvMapType] === 'imap') {
|
|
522
|
+
const intKey = Number(key);
|
|
523
|
+
if (Number.isNaN(intKey)) {
|
|
524
|
+
throw new TypeError('Invalid NaN IMap key');
|
|
525
|
+
}
|
|
526
|
+
this.writeInt(intKey);
|
|
527
|
+
}
|
|
528
|
+
else if (map[shvMapType] === 'metamap') {
|
|
529
|
+
const intKey = Number(key);
|
|
530
|
+
if (Number.isNaN(intKey)) {
|
|
531
|
+
this.writeJSString(key.toString());
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
this.writeInt(intKey);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
this.writeJSString(key.toString());
|
|
539
|
+
}
|
|
540
|
+
this.write(value);
|
|
541
|
+
}
|
|
542
|
+
this.ctx.putByte(PackingSchema.Term);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
const toChainPack = (value) => {
|
|
546
|
+
const wr = new ChainPackWriter();
|
|
547
|
+
wr.write(value);
|
|
548
|
+
return wr.ctx.buffer();
|
|
549
|
+
};
|
|
550
|
+
const fromChainPack = (buffer) => {
|
|
551
|
+
const reader = new ChainPackReader(buffer);
|
|
552
|
+
return reader.read();
|
|
553
|
+
};
|
|
554
|
+
const CHAINPACK_PROTOCOL_TYPE = 1;
|
|
555
|
+
export { toChainPack, fromChainPack, ChainPackReader, ChainPackWriter, CHAINPACK_PROTOCOL_TYPE };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
declare class UnpackContext {
|
|
2
|
+
index: number;
|
|
3
|
+
private readonly data;
|
|
4
|
+
constructor(buf: ArrayBufferLike);
|
|
5
|
+
getByte(): number;
|
|
6
|
+
peekByte(): number;
|
|
7
|
+
getBytes(str: string): void;
|
|
8
|
+
}
|
|
9
|
+
declare class PackContext {
|
|
10
|
+
static CHUNK_LEN: number;
|
|
11
|
+
data: Uint8Array<ArrayBuffer>;
|
|
12
|
+
length: number;
|
|
13
|
+
putByte(b: number): void;
|
|
14
|
+
writeStringUtf8(str: string): void;
|
|
15
|
+
writeCharCodeUtf8(charcode: number): void;
|
|
16
|
+
buffer(): ArrayBuffer;
|
|
17
|
+
}
|
|
18
|
+
export { PackContext, UnpackContext };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
class UnpackContext {
|
|
2
|
+
index;
|
|
3
|
+
data;
|
|
4
|
+
constructor(buf) {
|
|
5
|
+
this.data = new Uint8Array(buf);
|
|
6
|
+
this.index = 0;
|
|
7
|
+
}
|
|
8
|
+
getByte() {
|
|
9
|
+
if (this.index >= this.data.length) {
|
|
10
|
+
throw new RangeError('unexpected end of data');
|
|
11
|
+
}
|
|
12
|
+
return this.data[this.index++];
|
|
13
|
+
}
|
|
14
|
+
peekByte() {
|
|
15
|
+
if (this.index >= this.data.length) {
|
|
16
|
+
return -1;
|
|
17
|
+
}
|
|
18
|
+
return this.data[this.index];
|
|
19
|
+
}
|
|
20
|
+
getBytes(str) {
|
|
21
|
+
for (let i = 0; i < str.length; i++) {
|
|
22
|
+
if (str.codePointAt(i) !== this.getByte()) {
|
|
23
|
+
throw new TypeError(`'${str}' expected`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const transfer = (source, length) => {
|
|
29
|
+
if (length <= source.byteLength) {
|
|
30
|
+
return source.slice(0, length);
|
|
31
|
+
}
|
|
32
|
+
const sourceView = new Uint8Array(source);
|
|
33
|
+
const destView = new Uint8Array(new ArrayBuffer(length));
|
|
34
|
+
destView.set(sourceView);
|
|
35
|
+
return destView.buffer;
|
|
36
|
+
};
|
|
37
|
+
class PackContext {
|
|
38
|
+
static CHUNK_LEN = 1024;
|
|
39
|
+
data = new Uint8Array(0);
|
|
40
|
+
length = 0;
|
|
41
|
+
putByte(b) {
|
|
42
|
+
if (this.length >= this.data.length) {
|
|
43
|
+
const buffer = transfer(this.data.buffer, this.data.length + PackContext.CHUNK_LEN);
|
|
44
|
+
this.data = new Uint8Array(buffer);
|
|
45
|
+
}
|
|
46
|
+
this.data[this.length++] = b;
|
|
47
|
+
}
|
|
48
|
+
writeStringUtf8(str) {
|
|
49
|
+
for (let i = 0; i < str.length; i++) {
|
|
50
|
+
const charcode = str.codePointAt(i);
|
|
51
|
+
this.writeCharCodeUtf8(charcode);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
writeCharCodeUtf8(charcode) {
|
|
55
|
+
/* eslint-disable no-bitwise */
|
|
56
|
+
if (charcode < 0x80) {
|
|
57
|
+
this.putByte(charcode);
|
|
58
|
+
}
|
|
59
|
+
else if (charcode < 0x8_00) {
|
|
60
|
+
this.putByte(0xC0 | (charcode >> 6));
|
|
61
|
+
this.putByte(0x80 | (charcode & 0x3F));
|
|
62
|
+
}
|
|
63
|
+
else if (charcode < 0xD8_00 || charcode >= 0xE0_00) {
|
|
64
|
+
this.putByte(0xE0 | (charcode >> 12));
|
|
65
|
+
this.putByte(0x80 | ((charcode >> 6) & 0x3F));
|
|
66
|
+
this.putByte(0x80 | (charcode & 0x3F));
|
|
67
|
+
}
|
|
68
|
+
/* eslint-enable no-bitwise */
|
|
69
|
+
}
|
|
70
|
+
buffer() {
|
|
71
|
+
return this.data.buffer.slice(0, this.length);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export { PackContext, UnpackContext };
|
package/dist/cpon.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type RpcValue, type RpcValueType, type DateTime, type List, Decimal, Double, type IMap, type Int, type MetaMap, RpcValueWithMetaData, type ShvMap, UInt } from './rpcvalue';
|
|
2
|
+
import { PackContext, UnpackContext } from './cpcontext';
|
|
3
|
+
export declare const stringifyDate: (dt: DateTime) => string;
|
|
4
|
+
declare const utf8ToString: (bytearray: ArrayBuffer) => string;
|
|
5
|
+
declare class CponReader {
|
|
6
|
+
ctx: UnpackContext;
|
|
7
|
+
constructor(unpackContext: UnpackContext);
|
|
8
|
+
read(): RpcValue;
|
|
9
|
+
skipWhitespace(): void;
|
|
10
|
+
readDateTime(): DateTime;
|
|
11
|
+
readDateTimeInner(): DateTime;
|
|
12
|
+
readCString(): string;
|
|
13
|
+
readBlobEsc(): ArrayBuffer;
|
|
14
|
+
readBlobHex(): ArrayBuffer;
|
|
15
|
+
readList(): (string | number | boolean | Double | Decimal | ArrayBuffer | DateTime | UInt<number> | List | ShvMap<Record<string, any>> | IMap<Record<number, any>> | RpcValueWithMetaData<MetaMap<Record<string | number, RpcValue>>, RpcValueType> | undefined)[];
|
|
16
|
+
readMetaMap(): MetaMap;
|
|
17
|
+
readMap(): ShvMap;
|
|
18
|
+
readIMap(): IMap;
|
|
19
|
+
readInt(): number;
|
|
20
|
+
readNumber(): number | Decimal | UInt<number>;
|
|
21
|
+
private implReadMap;
|
|
22
|
+
}
|
|
23
|
+
declare class CponWriter {
|
|
24
|
+
private readonly indentString?;
|
|
25
|
+
private oneLiners;
|
|
26
|
+
ctx: PackContext;
|
|
27
|
+
nestLevel: number;
|
|
28
|
+
constructor(indentString?: string | undefined, oneLiners?: OneLiners);
|
|
29
|
+
write(rpcVal: RpcValue): void;
|
|
30
|
+
writeJSString(str: string): void;
|
|
31
|
+
writeBlob(buffer: ArrayBuffer): void;
|
|
32
|
+
writeDateTime(dt: DateTime): void;
|
|
33
|
+
writeBool(b: boolean): void;
|
|
34
|
+
writeMeta(map: MetaMap): void;
|
|
35
|
+
writeIMap(map: IMap): void;
|
|
36
|
+
writeMap(map: ShvMap): void;
|
|
37
|
+
writeMapContent(map: MetaMap | ShvMap | IMap, delimiterStart: string, delimiterEnd: string): void;
|
|
38
|
+
writeList(lst: RpcValue[]): void;
|
|
39
|
+
writeUInt(num: UInt): void;
|
|
40
|
+
writeInt(num: Int): void;
|
|
41
|
+
writeDouble(num: Double): void;
|
|
42
|
+
writeDecimal(val: Decimal): void;
|
|
43
|
+
private doIndentIfNotOneliner;
|
|
44
|
+
private isOneLiner;
|
|
45
|
+
private increaseIndentIfNotOneLiner;
|
|
46
|
+
private decreaseIndentIfNotOneLiner;
|
|
47
|
+
}
|
|
48
|
+
export declare enum OneLiners {
|
|
49
|
+
Yes = 0,
|
|
50
|
+
No = 1
|
|
51
|
+
}
|
|
52
|
+
declare const toCpon: (value: RpcValue, indentString?: string, oneLiners?: OneLiners) => string;
|
|
53
|
+
declare const fromCpon: (str: string | Uint8Array) => RpcValue;
|
|
54
|
+
declare const CPON_PROTOCOL_TYPE = 2;
|
|
55
|
+
export { utf8ToString, CponWriter, CponReader, CPON_PROTOCOL_TYPE, toCpon, fromCpon };
|