@subsquid/evm-codec 0.0.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/README.md +3 -0
- package/lib/codec.d.ts +20 -0
- package/lib/codec.d.ts.map +1 -0
- package/lib/codec.js +5 -0
- package/lib/codec.js.map +1 -0
- package/lib/codecs/array.d.ts +22 -0
- package/lib/codecs/array.d.ts.map +1 -0
- package/lib/codecs/array.js +78 -0
- package/lib/codecs/array.js.map +1 -0
- package/lib/codecs/primitives.d.ts +70 -0
- package/lib/codecs/primitives.d.ts.map +1 -0
- package/lib/codecs/primitives.js +204 -0
- package/lib/codecs/primitives.js.map +1 -0
- package/lib/codecs/struct.d.ts +15 -0
- package/lib/codecs/struct.d.ts.map +1 -0
- package/lib/codecs/struct.js +65 -0
- package/lib/codecs/struct.js.map +1 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +23 -0
- package/lib/index.js.map +1 -0
- package/lib/sink.d.ts +42 -0
- package/lib/sink.d.ts.map +1 -0
- package/lib/sink.js +194 -0
- package/lib/sink.js.map +1 -0
- package/lib/src.d.ts +30 -0
- package/lib/src.d.ts.map +1 -0
- package/lib/src.js +141 -0
- package/lib/src.js.map +1 -0
- package/package.json +26 -0
- package/src/codec.ts +21 -0
- package/src/codecs/array.ts +83 -0
- package/src/codecs/primitives.ts +225 -0
- package/src/codecs/struct.ts +74 -0
- package/src/index.ts +4 -0
- package/src/sink.ts +213 -0
- package/src/src.ts +158 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { Codec } from '../codec'
|
|
2
|
+
import { Sink } from '../sink'
|
|
3
|
+
import { Src } from '../src'
|
|
4
|
+
import { ArrayCodec, FixedSizeArrayCodec } from './array'
|
|
5
|
+
import { StructCodec } from './struct'
|
|
6
|
+
|
|
7
|
+
export const bool: Codec<boolean> = {
|
|
8
|
+
encode: function (sink: Sink, val: boolean) {
|
|
9
|
+
sink.bool(val)
|
|
10
|
+
},
|
|
11
|
+
decode(src: Src): boolean {
|
|
12
|
+
return src.bool()
|
|
13
|
+
},
|
|
14
|
+
isDynamic: false,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const uint8: Codec<number> = {
|
|
18
|
+
encode(sink: Sink, val: number) {
|
|
19
|
+
sink.u8(val)
|
|
20
|
+
},
|
|
21
|
+
decode(src: Src): number {
|
|
22
|
+
return src.u8()
|
|
23
|
+
},
|
|
24
|
+
isDynamic: false,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const int8: Codec<number> = {
|
|
28
|
+
encode(sink: Sink, val: number) {
|
|
29
|
+
sink.i8(val)
|
|
30
|
+
},
|
|
31
|
+
decode(src: Src): number {
|
|
32
|
+
return src.i8()
|
|
33
|
+
},
|
|
34
|
+
isDynamic: false,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const uint16: Codec<number> = {
|
|
38
|
+
encode(sink: Sink, val: number) {
|
|
39
|
+
sink.u16(val)
|
|
40
|
+
},
|
|
41
|
+
decode(src: Src): number {
|
|
42
|
+
return src.u16()
|
|
43
|
+
},
|
|
44
|
+
isDynamic: false,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const int16: Codec<number> = {
|
|
48
|
+
encode(sink: Sink, val: number) {
|
|
49
|
+
sink.i16(val)
|
|
50
|
+
},
|
|
51
|
+
decode(src: Src): number {
|
|
52
|
+
return src.i16()
|
|
53
|
+
},
|
|
54
|
+
isDynamic: false,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const uint32: Codec<number> = {
|
|
58
|
+
encode(sink: Sink, val: number) {
|
|
59
|
+
sink.u32(val)
|
|
60
|
+
},
|
|
61
|
+
decode(src: Src): number {
|
|
62
|
+
return src.u32()
|
|
63
|
+
},
|
|
64
|
+
isDynamic: false,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const int32: Codec<number> = {
|
|
68
|
+
encode(sink: Sink, val: number) {
|
|
69
|
+
sink.i32(val)
|
|
70
|
+
},
|
|
71
|
+
decode(src: Src): number {
|
|
72
|
+
return src.i32()
|
|
73
|
+
},
|
|
74
|
+
isDynamic: false,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const uint64: Codec<bigint> = {
|
|
78
|
+
encode(sink: Sink, val: bigint) {
|
|
79
|
+
sink.u64(val)
|
|
80
|
+
},
|
|
81
|
+
decode(src: Src): bigint {
|
|
82
|
+
return src.u64()
|
|
83
|
+
},
|
|
84
|
+
isDynamic: false,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const int64: Codec<bigint> = {
|
|
88
|
+
encode(sink: Sink, val: bigint) {
|
|
89
|
+
sink.i64(val)
|
|
90
|
+
},
|
|
91
|
+
decode(src: Src): bigint {
|
|
92
|
+
return src.i64()
|
|
93
|
+
},
|
|
94
|
+
isDynamic: false,
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const uint128: Codec<bigint> = {
|
|
98
|
+
encode(sink: Sink, val: bigint) {
|
|
99
|
+
sink.u128(val)
|
|
100
|
+
},
|
|
101
|
+
decode(src: Src): bigint {
|
|
102
|
+
return src.u128()
|
|
103
|
+
},
|
|
104
|
+
isDynamic: false,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const int128: Codec<bigint> = {
|
|
108
|
+
encode(sink: Sink, val: bigint) {
|
|
109
|
+
sink.i128(val)
|
|
110
|
+
},
|
|
111
|
+
decode(src: Src): bigint {
|
|
112
|
+
return src.i128()
|
|
113
|
+
},
|
|
114
|
+
isDynamic: false,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const uint256: Codec<bigint> = {
|
|
118
|
+
encode(sink: Sink, val: bigint) {
|
|
119
|
+
sink.u256(val)
|
|
120
|
+
},
|
|
121
|
+
decode(src: Src): bigint {
|
|
122
|
+
return src.u256()
|
|
123
|
+
},
|
|
124
|
+
isDynamic: false,
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export const int256: Codec<bigint> = {
|
|
128
|
+
encode(sink: Sink, val: bigint) {
|
|
129
|
+
sink.i256(val)
|
|
130
|
+
},
|
|
131
|
+
decode(src: Src): bigint {
|
|
132
|
+
return src.i256()
|
|
133
|
+
},
|
|
134
|
+
isDynamic: false,
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export const string = <const>{
|
|
138
|
+
encode(sink: Sink, val: string) {
|
|
139
|
+
sink.newStaticDataArea()
|
|
140
|
+
sink.string(val)
|
|
141
|
+
sink.endCurrentDataArea()
|
|
142
|
+
},
|
|
143
|
+
decode(src: Src): string {
|
|
144
|
+
return src.string()
|
|
145
|
+
},
|
|
146
|
+
isDynamic: true,
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export const bytes = <const>{
|
|
150
|
+
encode(sink: Sink, val: Uint8Array) {
|
|
151
|
+
sink.newStaticDataArea()
|
|
152
|
+
sink.bytes(val)
|
|
153
|
+
sink.endCurrentDataArea()
|
|
154
|
+
},
|
|
155
|
+
decode(src: Src): Uint8Array {
|
|
156
|
+
return src.bytes()
|
|
157
|
+
},
|
|
158
|
+
isDynamic: true,
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const bytesN = (size: number): Codec<Uint8Array> => ({
|
|
162
|
+
encode(sink: Sink, val: Uint8Array) {
|
|
163
|
+
sink.staticBytes(size, val)
|
|
164
|
+
},
|
|
165
|
+
decode(src: Src): Uint8Array {
|
|
166
|
+
return src.staticBytes(size)
|
|
167
|
+
},
|
|
168
|
+
isDynamic: false,
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
export const bytes0 = bytesN(0)
|
|
172
|
+
export const bytes1 = bytesN(1)
|
|
173
|
+
export const bytes2 = bytesN(2)
|
|
174
|
+
export const bytes3 = bytesN(3)
|
|
175
|
+
export const bytes4 = bytesN(4)
|
|
176
|
+
export const bytes5 = bytesN(5)
|
|
177
|
+
export const bytes6 = bytesN(6)
|
|
178
|
+
export const bytes7 = bytesN(7)
|
|
179
|
+
export const bytes8 = bytesN(8)
|
|
180
|
+
export const bytes9 = bytesN(9)
|
|
181
|
+
export const bytes10 = bytesN(10)
|
|
182
|
+
export const bytes11 = bytesN(11)
|
|
183
|
+
export const bytes12 = bytesN(12)
|
|
184
|
+
export const bytes13 = bytesN(13)
|
|
185
|
+
export const bytes14 = bytesN(14)
|
|
186
|
+
export const bytes15 = bytesN(15)
|
|
187
|
+
export const bytes16 = bytesN(16)
|
|
188
|
+
export const bytes17 = bytesN(17)
|
|
189
|
+
export const bytes18 = bytesN(18)
|
|
190
|
+
export const bytes19 = bytesN(19)
|
|
191
|
+
export const bytes20 = bytesN(20)
|
|
192
|
+
export const bytes21 = bytesN(21)
|
|
193
|
+
export const bytes22 = bytesN(22)
|
|
194
|
+
export const bytes23 = bytesN(23)
|
|
195
|
+
export const bytes24 = bytesN(24)
|
|
196
|
+
export const bytes25 = bytesN(25)
|
|
197
|
+
export const bytes26 = bytesN(26)
|
|
198
|
+
export const bytes27 = bytesN(27)
|
|
199
|
+
export const bytes28 = bytesN(28)
|
|
200
|
+
export const bytes29 = bytesN(29)
|
|
201
|
+
export const bytes30 = bytesN(30)
|
|
202
|
+
export const bytes31 = bytesN(31)
|
|
203
|
+
export const bytes32 = bytesN(32)
|
|
204
|
+
|
|
205
|
+
export const address: Codec<string> = {
|
|
206
|
+
encode(sink: Sink, val: string) {
|
|
207
|
+
sink.address(val)
|
|
208
|
+
},
|
|
209
|
+
decode(src: Src): string {
|
|
210
|
+
return src.address()
|
|
211
|
+
},
|
|
212
|
+
isDynamic: false,
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export const fixedSizeArray = <T>(item: Codec<T>, size: number): Codec<T[]> => new FixedSizeArrayCodec(item, size)
|
|
216
|
+
|
|
217
|
+
export const array = <T>(item: Codec<T>): Codec<T[]> => new ArrayCodec(item)
|
|
218
|
+
|
|
219
|
+
type Struct = {
|
|
220
|
+
[key: string]: Codec<any>
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export const struct = <const T extends Struct>(components: T) => new StructCodec<T>(components)
|
|
224
|
+
|
|
225
|
+
export const tuple = struct
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Codec, Struct, StructTypes } from '../codec'
|
|
2
|
+
import { Sink } from '../sink'
|
|
3
|
+
import { Src } from '../src'
|
|
4
|
+
|
|
5
|
+
function slotsCount(codecs: readonly Codec<any>[]) {
|
|
6
|
+
let count = 0
|
|
7
|
+
for (const codec of codecs) {
|
|
8
|
+
count += codec.slotsCount ?? 1
|
|
9
|
+
}
|
|
10
|
+
return count
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class StructCodec<const T extends Struct> implements Codec<StructTypes<T>> {
|
|
14
|
+
public readonly isDynamic: boolean
|
|
15
|
+
public readonly slotsCount: number
|
|
16
|
+
private readonly childrenSlotsCount: number
|
|
17
|
+
private readonly components: T
|
|
18
|
+
|
|
19
|
+
constructor(components: T) {
|
|
20
|
+
this.components = components
|
|
21
|
+
const codecs = Object.values(components)
|
|
22
|
+
this.isDynamic = codecs.some((codec) => codec.isDynamic)
|
|
23
|
+
this.childrenSlotsCount = slotsCount(codecs)
|
|
24
|
+
if (this.isDynamic) {
|
|
25
|
+
this.slotsCount = 1
|
|
26
|
+
} else {
|
|
27
|
+
this.slotsCount = this.childrenSlotsCount
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public encode(sink: Sink, val: StructTypes<T>): void {
|
|
32
|
+
if (this.isDynamic) {
|
|
33
|
+
this.encodeDynamic(sink, val)
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
for (let i in this.components) {
|
|
37
|
+
let prop = this.components[i]
|
|
38
|
+
prop.encode(sink, val[i])
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private encodeDynamic(sink: Sink, val: StructTypes<T>): void {
|
|
43
|
+
sink.newStaticDataArea(this.childrenSlotsCount)
|
|
44
|
+
for (let i in this.components) {
|
|
45
|
+
let prop = this.components[i]
|
|
46
|
+
prop.encode(sink, val[i])
|
|
47
|
+
}
|
|
48
|
+
sink.endCurrentDataArea()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public decode(src: Src): StructTypes<T> {
|
|
52
|
+
if (this.isDynamic) {
|
|
53
|
+
return this.decodeDynamic(src)
|
|
54
|
+
}
|
|
55
|
+
let result: any = {}
|
|
56
|
+
for (let i in this.components) {
|
|
57
|
+
let prop = this.components[i]
|
|
58
|
+
result[i] = prop.decode(src)
|
|
59
|
+
}
|
|
60
|
+
return result
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private decodeDynamic(src: Src): StructTypes<T> {
|
|
64
|
+
let result: any = {}
|
|
65
|
+
|
|
66
|
+
const offset = src.u32()
|
|
67
|
+
const tmpSrc = src.slice(offset)
|
|
68
|
+
for (let i in this.components) {
|
|
69
|
+
let prop = this.components[i]
|
|
70
|
+
result[i] = prop.decode(tmpSrc)
|
|
71
|
+
}
|
|
72
|
+
return result
|
|
73
|
+
}
|
|
74
|
+
}
|
package/src/index.ts
ADDED
package/src/sink.ts
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import assert from 'node:assert'
|
|
2
|
+
import { WORD_SIZE } from './codec'
|
|
3
|
+
|
|
4
|
+
export class Sink {
|
|
5
|
+
private pos = 0
|
|
6
|
+
private buf: Buffer
|
|
7
|
+
private view: DataView
|
|
8
|
+
private stack: { start: number; jumpBackPtr: number; size: number }[] = []
|
|
9
|
+
constructor(fields: number, capacity: number = 1280) {
|
|
10
|
+
this.stack.push({
|
|
11
|
+
start: 0,
|
|
12
|
+
jumpBackPtr: 0,
|
|
13
|
+
size: fields * WORD_SIZE,
|
|
14
|
+
})
|
|
15
|
+
this.buf = Buffer.alloc(capacity)
|
|
16
|
+
this.view = new DataView(this.buf.buffer, this.buf.byteOffset, this.buf.byteLength)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
result(): Buffer {
|
|
20
|
+
assert(this.stack.length === 1, 'Cannot get result during dynamic encoding')
|
|
21
|
+
return this.buf.subarray(0, this.size())
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
toString() {
|
|
25
|
+
return '0x' + this.result().toString('hex')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
reserve(additional: number): void {
|
|
29
|
+
if (this.buf.length - this.pos < additional) {
|
|
30
|
+
this._allocate(this.pos + additional)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
size() {
|
|
35
|
+
return this.stack[this.stack.length - 1].size
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private _allocate(cap: number): void {
|
|
39
|
+
cap = Math.max(cap, this.buf.length * 2)
|
|
40
|
+
let buf = Buffer.alloc(cap)
|
|
41
|
+
buf.set(this.buf)
|
|
42
|
+
this.buf = buf
|
|
43
|
+
this.view = new DataView(this.buf.buffer, this.buf.byteOffset, this.buf.byteLength)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
u8(val: number) {
|
|
47
|
+
this.reserve(WORD_SIZE)
|
|
48
|
+
this.pos += WORD_SIZE - 1
|
|
49
|
+
this.view.setUint8(this.pos, val)
|
|
50
|
+
this.pos += 1
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
i8(val: number) {
|
|
54
|
+
this.i256(BigInt(val))
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
u16(val: number) {
|
|
58
|
+
this.reserve(WORD_SIZE)
|
|
59
|
+
this.pos += WORD_SIZE - 2
|
|
60
|
+
this.view.setUint16(this.pos, val, false)
|
|
61
|
+
this.pos += 2
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
i16(val: number) {
|
|
65
|
+
this.i256(BigInt(val))
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
u32(val: number) {
|
|
69
|
+
this.reserve(WORD_SIZE)
|
|
70
|
+
this.pos += WORD_SIZE - 4
|
|
71
|
+
this.view.setUint32(this.pos, val, false)
|
|
72
|
+
this.pos += 4
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
i32(val: number) {
|
|
76
|
+
this.i256(BigInt(val))
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
u64(val: bigint) {
|
|
80
|
+
this.reserve(WORD_SIZE)
|
|
81
|
+
this.pos += WORD_SIZE - 8
|
|
82
|
+
this.view.setBigUint64(this.pos, val, false)
|
|
83
|
+
this.pos += 8
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
i64(val: bigint) {
|
|
87
|
+
this.i256(val)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
#u64(val: bigint) {
|
|
91
|
+
this.view.setBigUint64(this.pos, val, false)
|
|
92
|
+
this.pos += 8
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
u128(val: bigint) {
|
|
96
|
+
this.reserve(WORD_SIZE)
|
|
97
|
+
this.pos += WORD_SIZE - 16
|
|
98
|
+
this.#u64(val & 0xffffffffffffffffn)
|
|
99
|
+
this.#u64(val >> 64n)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
i128(val: bigint) {
|
|
103
|
+
this.i256(BigInt(val))
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
#u128(val: bigint) {
|
|
107
|
+
this.reserve(WORD_SIZE)
|
|
108
|
+
this.#u64(val >> 64n)
|
|
109
|
+
this.#u64(val & 0xffffffffffffffffn)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
u256(val: bigint) {
|
|
113
|
+
this.reserve(WORD_SIZE)
|
|
114
|
+
this.#u128(val >> 128n)
|
|
115
|
+
this.#u128(val & (2n ** 128n - 1n))
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
i256(val: bigint) {
|
|
119
|
+
let base = 2n ** 256n
|
|
120
|
+
val = (val + base) % base
|
|
121
|
+
this.u256(val)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
bytes(val: Uint8Array) {
|
|
125
|
+
const size = Buffer.byteLength(val)
|
|
126
|
+
this.u32(size)
|
|
127
|
+
const wordsCount = Math.ceil(size / WORD_SIZE)
|
|
128
|
+
const reservedSize = WORD_SIZE * wordsCount
|
|
129
|
+
this.reserve(reservedSize)
|
|
130
|
+
this.buf.set(val, this.pos)
|
|
131
|
+
this.pos += reservedSize
|
|
132
|
+
this.increaseCurrentDataAreaSize(reservedSize + WORD_SIZE)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
staticBytes(len: number, val: Uint8Array) {
|
|
136
|
+
if (len > 32) {
|
|
137
|
+
throw new Error(`bytes${len} is not a valid type`)
|
|
138
|
+
}
|
|
139
|
+
const size = Buffer.byteLength(val)
|
|
140
|
+
if (size > len) {
|
|
141
|
+
throw new Error(`invalid data size for bytes${len}`)
|
|
142
|
+
}
|
|
143
|
+
this.reserve(WORD_SIZE)
|
|
144
|
+
this.buf.set(val, this.pos)
|
|
145
|
+
this.pos += WORD_SIZE
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
address(val: string) {
|
|
149
|
+
this.u256(BigInt(val))
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
string(val: string) {
|
|
153
|
+
const size = Buffer.byteLength(val)
|
|
154
|
+
this.u32(size)
|
|
155
|
+
const wordsCount = Math.ceil(size / WORD_SIZE)
|
|
156
|
+
const reservedSize = WORD_SIZE * wordsCount
|
|
157
|
+
this.reserve(reservedSize)
|
|
158
|
+
this.buf.write(val, this.pos)
|
|
159
|
+
this.pos += reservedSize
|
|
160
|
+
this.increaseCurrentDataAreaSize(reservedSize + WORD_SIZE)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
bool(val: boolean) {
|
|
164
|
+
this.u8(val ? 1 : 0)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @example
|
|
169
|
+
* @link [Solidity docs](https://docs.soliditylang.org/en/latest/abi-spec.html#use-of-dynamic-types)
|
|
170
|
+
*/
|
|
171
|
+
newStaticDataArea(slotsCount = 0) {
|
|
172
|
+
const offset = this.size()
|
|
173
|
+
this.u32(offset)
|
|
174
|
+
const dataAreaStart = this.currentDataAreaStart()
|
|
175
|
+
this.pushDataArea(dataAreaStart + offset, slotsCount)
|
|
176
|
+
this.pos = dataAreaStart + offset
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Adds elements count before the data area in an additional slot
|
|
180
|
+
newDynamicDataArea(slotsCount: number) {
|
|
181
|
+
const offset = this.size()
|
|
182
|
+
this.u32(offset)
|
|
183
|
+
const dataAreaStart = this.currentDataAreaStart()
|
|
184
|
+
this.pushDataArea(dataAreaStart + offset + WORD_SIZE, slotsCount)
|
|
185
|
+
this.pos = dataAreaStart + offset
|
|
186
|
+
this.u32(slotsCount)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private currentDataAreaStart() {
|
|
190
|
+
return this.stack[this.stack.length - 1].start
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
public increaseCurrentDataAreaSize(amount: number) {
|
|
194
|
+
this.stack[this.stack.length - 1].size += amount
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private pushDataArea(dataAreaStart: number, slotsCount: number) {
|
|
198
|
+
const size = slotsCount * WORD_SIZE
|
|
199
|
+
this.reserve(dataAreaStart + size)
|
|
200
|
+
this.stack.push({
|
|
201
|
+
start: dataAreaStart,
|
|
202
|
+
jumpBackPtr: this.pos,
|
|
203
|
+
size,
|
|
204
|
+
})
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
public endCurrentDataArea() {
|
|
208
|
+
assert(this.stack.length > 1, 'No dynamic encoding started')
|
|
209
|
+
const { jumpBackPtr, size } = this.stack.pop()!
|
|
210
|
+
this.increaseCurrentDataAreaSize(size)
|
|
211
|
+
this.pos = jumpBackPtr
|
|
212
|
+
}
|
|
213
|
+
}
|
package/src/src.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { WORD_SIZE } from './codec'
|
|
2
|
+
|
|
3
|
+
export class Src {
|
|
4
|
+
private view: DataView
|
|
5
|
+
private pos = 0
|
|
6
|
+
private oldPos = 0
|
|
7
|
+
constructor(private buf: Uint8Array) {
|
|
8
|
+
this.view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
slice(start: number, end?: number): Src {
|
|
12
|
+
return new Src(this.buf.subarray(start, end))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
u8(): number {
|
|
16
|
+
this.pos += WORD_SIZE - 1
|
|
17
|
+
let val = this.view.getUint8(this.pos)
|
|
18
|
+
this.pos += 1
|
|
19
|
+
return val
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
i8(): number {
|
|
23
|
+
return Number(this.i256())
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
u16(): number {
|
|
27
|
+
this.pos += WORD_SIZE - 2
|
|
28
|
+
let val = this.view.getUint16(this.pos, false)
|
|
29
|
+
this.pos += 2
|
|
30
|
+
return val
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
i16(): number {
|
|
34
|
+
return Number(this.i256())
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
u32(): number {
|
|
38
|
+
this.pos += WORD_SIZE - 4
|
|
39
|
+
let val = this.view.getUint32(this.pos, false)
|
|
40
|
+
this.pos += 4
|
|
41
|
+
return val
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
i32(): number {
|
|
45
|
+
return Number(this.i256())
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
u64(): bigint {
|
|
49
|
+
this.pos += WORD_SIZE - 8
|
|
50
|
+
return this.#u64()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#u64(): bigint {
|
|
54
|
+
let val = this.view.getBigUint64(this.pos, false)
|
|
55
|
+
this.pos += 8
|
|
56
|
+
return val
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
i64(): bigint {
|
|
60
|
+
this.pos += WORD_SIZE - 8
|
|
61
|
+
return this.#i64()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
#i64(): bigint {
|
|
65
|
+
let val = this.view.getBigInt64(this.pos, false)
|
|
66
|
+
this.pos += 8
|
|
67
|
+
return val
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
u128(): bigint {
|
|
71
|
+
this.pos += WORD_SIZE - 16
|
|
72
|
+
return this.#u128()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#u128(): bigint {
|
|
76
|
+
let hi = this.#u64()
|
|
77
|
+
let lo = this.#u64()
|
|
78
|
+
return lo + (hi << 64n)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
i128(): bigint {
|
|
82
|
+
this.pos += WORD_SIZE - 16
|
|
83
|
+
return this.#i128()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#i128(): bigint {
|
|
87
|
+
let hi = this.#i64()
|
|
88
|
+
let lo = this.#u64()
|
|
89
|
+
return lo + (hi << 64n)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
u256(): bigint {
|
|
93
|
+
let hi = this.#u128()
|
|
94
|
+
let lo = this.#u128()
|
|
95
|
+
return lo + (hi << 128n)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
i256(): bigint {
|
|
99
|
+
let hi = this.#i128()
|
|
100
|
+
let lo = this.#u128()
|
|
101
|
+
return lo + (hi << 128n)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
address(): string {
|
|
105
|
+
return '0x' + this.u256().toString(16).padStart(40, '0')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
bytes(): Uint8Array {
|
|
109
|
+
const ptr = this.u32()
|
|
110
|
+
this.safeJump(ptr)
|
|
111
|
+
const len = this.u32()
|
|
112
|
+
this.assertLength(len)
|
|
113
|
+
const val = this.buf.subarray(this.pos, this.pos + len)
|
|
114
|
+
this.jumpBack()
|
|
115
|
+
return val
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
staticBytes(len: number): Uint8Array {
|
|
119
|
+
if (len > 32) {
|
|
120
|
+
throw new Error(`bytes${len} is not a valid type`)
|
|
121
|
+
}
|
|
122
|
+
const val = this.buf.subarray(this.pos, this.pos + len)
|
|
123
|
+
this.pos += WORD_SIZE
|
|
124
|
+
return val
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
string(): string {
|
|
128
|
+
const ptr = this.u32()
|
|
129
|
+
this.safeJump(ptr)
|
|
130
|
+
const len = this.u32()
|
|
131
|
+
this.assertLength(len)
|
|
132
|
+
const val = Buffer.from(this.buf.buffer, this.buf.byteOffset + this.pos, len).toString('utf-8')
|
|
133
|
+
this.jumpBack()
|
|
134
|
+
return val
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
bool(): boolean {
|
|
138
|
+
return !!this.u8()
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private assertLength(len: number): void {
|
|
142
|
+
if (this.buf.length - this.pos < len) {
|
|
143
|
+
throw new RangeError('Unexpected end of input')
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public safeJump(pos: number): void {
|
|
148
|
+
if (pos < 0 || pos >= this.buf.length) {
|
|
149
|
+
throw new RangeError(`Unexpected pointer location: 0x${pos.toString(16)}`)
|
|
150
|
+
}
|
|
151
|
+
this.oldPos = this.pos
|
|
152
|
+
this.pos = pos
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
public jumpBack(): void {
|
|
156
|
+
this.pos = this.oldPos
|
|
157
|
+
}
|
|
158
|
+
}
|