1 | const lodash = require('lodash');
|
2 | const JSBI = require('jsbi');
|
3 | const { assert, format } = require('../util');
|
4 |
|
5 | const WORD_BYTES = 32;
|
6 | const UINT_BOUND = JSBI.leftShift(JSBI.BigInt(1), JSBI.BigInt(256));
|
7 | const MAX_UINT = JSBI.subtract(UINT_BOUND, JSBI.BigInt(1));
|
8 |
|
9 | class Pointer extends Number {}
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 | function padBuffer(buffer, alignLeft = false) {
|
17 | buffer = format.buffer(buffer);
|
18 |
|
19 | const count = WORD_BYTES - (buffer.length % WORD_BYTES);
|
20 | if (0 < count && count < WORD_BYTES) {
|
21 | buffer = alignLeft
|
22 | ? Buffer.concat([buffer, Buffer.alloc(count)])
|
23 | : Buffer.concat([Buffer.alloc(count), buffer]);
|
24 | }
|
25 |
|
26 | return buffer;
|
27 | }
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 | function encodeUInt256(value) {
|
34 | const number = format.bigUInt(value);
|
35 |
|
36 | assert(JSBI.LE(number, MAX_UINT), {
|
37 | message: 'bound error',
|
38 | expect: `<= ${MAX_UINT}`,
|
39 | got: number.toString(),
|
40 | coder: this,
|
41 | value,
|
42 | });
|
43 |
|
44 | return padBuffer(format.hex(number));
|
45 | }
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 | function decodeUInt256(stream) {
|
52 | return format.bigInt(`0x${stream.read(64)}`);
|
53 | }
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 | function pack(coders, array) {
|
61 | let offset = 0;
|
62 | const staticList = [];
|
63 | const dynamicList = [];
|
64 |
|
65 | lodash.zip(coders, array)
|
66 | .forEach(([coder, value]) => {
|
67 | const buffer = coder.encode(value);
|
68 |
|
69 | if (coder.dynamic) {
|
70 | offset += WORD_BYTES;
|
71 | staticList.push(new Pointer(dynamicList.length));
|
72 | dynamicList.push(buffer);
|
73 | } else {
|
74 | offset += buffer.length;
|
75 | staticList.push(buffer);
|
76 | }
|
77 | });
|
78 |
|
79 |
|
80 | staticList.forEach((pointer, index) => {
|
81 | if (pointer instanceof Pointer) {
|
82 | staticList[index] = encodeUInt256(offset);
|
83 | offset += dynamicList[pointer].length;
|
84 | }
|
85 | });
|
86 |
|
87 | return Buffer.concat([...staticList, ...dynamicList]);
|
88 | }
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 | function unpack(coders, stream) {
|
97 | const startIndex = stream.index;
|
98 |
|
99 | const array = coders.map(coder => {
|
100 | if (coder.dynamic) {
|
101 | const offset = Number(decodeUInt256(stream));
|
102 | return new Pointer(startIndex + offset * 2);
|
103 | } else {
|
104 | return coder.decode(stream);
|
105 | }
|
106 | });
|
107 |
|
108 | lodash.zip(coders, array)
|
109 | .forEach(([coder, value], index) => {
|
110 | if (value instanceof Pointer) {
|
111 | assert(Number(value) === stream.index, {
|
112 | message: 'stream.index error',
|
113 | expect: value,
|
114 | got: stream.index,
|
115 | coder,
|
116 | stream,
|
117 | });
|
118 |
|
119 | array[index] = coder.decode(stream);
|
120 | }
|
121 | });
|
122 |
|
123 | return array;
|
124 | }
|
125 |
|
126 | module.exports = {
|
127 | WORD_BYTES,
|
128 | MAX_UINT,
|
129 | UINT_BOUND,
|
130 | padBuffer,
|
131 | encodeUInt256,
|
132 | decodeUInt256,
|
133 | pack,
|
134 | unpack,
|
135 | };
|