UNPKG

3.12 kBJavaScriptView Raw
1const lodash = require('lodash');
2const JSBI = require('jsbi');
3const { assert, format } = require('../util');
4
5const WORD_BYTES = 32; // byte number pre abi word
6const UINT_BOUND = JSBI.leftShift(JSBI.BigInt(1), JSBI.BigInt(256));
7const MAX_UINT = JSBI.subtract(UINT_BOUND, JSBI.BigInt(1));
8
9class Pointer extends Number {}
10
11/**
12 * @param buffer {Buffer}
13 * @param alignLeft {boolean}
14 * @return {Buffer}
15 */
16function padBuffer(buffer, alignLeft = false) {
17 buffer = format.buffer(buffer); // accept hex
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 * @param value {number|string}
31 * @return {Buffer}
32 */
33function 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 * @param stream {HexStream}
49 * @return {JSBI}
50 */
51function decodeUInt256(stream) {
52 return format.bigInt(`0x${stream.read(64)}`);
53}
54
55/**
56 * @param coders {BaseCoder[]}
57 * @param array {array}
58 * @return {Buffer}
59 */
60function 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)); // push index of dynamic to static
72 dynamicList.push(buffer);
73 } else {
74 offset += buffer.length;
75 staticList.push(buffer);
76 }
77 });
78
79 // write back the dynamic address
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 * @param coders {BaseCoder[]}
93 * @param stream {HexStream}
94 * @return {array}
95 */
96function 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
126module.exports = {
127 WORD_BYTES,
128 MAX_UINT,
129 UINT_BOUND,
130 padBuffer,
131 encodeUInt256,
132 decodeUInt256,
133 pack,
134 unpack,
135};