-
Notifications
You must be signed in to change notification settings - Fork 6
/
bridge.ts
213 lines (176 loc) · 5.31 KB
/
bridge.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// Copyright 2019-2022 @polkadot/wasm-bridge authors & contributors
// SPDX-License-Identifier: Apache-2.0
// A number of functions are "unsafe" and purposefully so - it is
// assumed that where the bridge is used, it is correctly wrapped
// in a safeguard (see withWasm in the wasm-crypto package) which
// then ensures that the internal wasm instance here is available
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import type { BridgeBase, InitFn, InitPromise, WasmBaseInstance, WasmImports } from './types.ts';
import { stringToU8a, u8aToString } from 'https://deno.land/x/polkadot@0.2.8/util/mod.ts';
import { Wbg } from './wbg.ts';
/**
* @name Bridge
* @description
* Creates a bridge between the JS and WASM environments.
*
* For any bridge it is passed an function white is then called internally at the
* time of initialization. This affectively implements the layer between WASM and
* the native environment, providing all the plumbing needed for the Wbg classes.
*/
export class Bridge<C extends WasmBaseInstance> implements BridgeBase<C> {
#cachegetInt32: Int32Array | null;
#cachegetUint8: Uint8Array | null;
#createWasm: InitFn<C>;
#heap: unknown[];
#heapNext: number;
#wasm: C | null;
#wasmError: string | null;
#wasmPromise: InitPromise<C> | null;
#wbg: WasmImports;
#type: 'asm' | 'wasm' | 'none';
constructor (createWasm: InitFn<C>) {
this.#createWasm = createWasm;
this.#cachegetInt32 = null;
this.#cachegetUint8 = null;
this.#heap = new Array(32)
.fill(undefined)
.concat(undefined, null, true, false);
this.#heapNext = this.#heap.length;
this.#type = 'none';
this.#wasm = null;
this.#wasmError = null;
this.#wasmPromise = null;
this.#wbg = { ...new Wbg(this) };
}
/** @description Returns the init error */
get error (): string | null {
return this.#wasmError;
}
/** @description Returns the init type */
get type (): 'asm' | 'wasm' | 'none' {
return this.#type;
}
/** @description Returns the created wasm interface */
get wasm (): C | null {
return this.#wasm;
}
/** @description Performs the wasm initialization */
async init (createWasm?: InitFn<C>): Promise<C | null> {
if (!this.#wasmPromise || createWasm) {
this.#wasmPromise = (createWasm || this.#createWasm)(this.#wbg);
}
const { error, type, wasm } = await this.#wasmPromise;
this.#type = type;
this.#wasm = wasm;
this.#wasmError = error;
return this.#wasm;
}
/**
* @internal
* @description Gets an object from the heap
*/
getObject (idx: number): unknown {
return this.#heap[idx];
}
/**
* @internal
* @description Removes an object from the heap
*/
dropObject (idx: number) {
if (idx < 36) {
return;
}
this.#heap[idx] = this.#heapNext;
this.#heapNext = idx;
}
/**
* @internal
* @description Retrieves and removes an object to the heap
*/
takeObject (idx: number): unknown {
const ret = this.getObject(idx);
this.dropObject(idx);
return ret;
}
/**
* @internal
* @description Adds an object to the heap
*/
addObject (obj: unknown): number {
if (this.#heapNext === this.#heap.length) {
this.#heap.push(this.#heap.length + 1);
}
const idx = this.#heapNext;
this.#heapNext = this.#heap[idx] as number;
this.#heap[idx] = obj;
return idx;
}
/**
* @internal
* @description Retrieve an Int32 in the WASM interface
*/
getInt32 (): Int32Array {
if (this.#cachegetInt32 === null || this.#cachegetInt32.buffer !== this.#wasm!.memory.buffer) {
this.#cachegetInt32 = new Int32Array(this.#wasm!.memory.buffer);
}
return this.#cachegetInt32;
}
/**
* @internal
* @description Retrieve an Uint8Array in the WASM interface
*/
getUint8 (): Uint8Array {
if (this.#cachegetUint8 === null || this.#cachegetUint8.buffer !== this.#wasm!.memory.buffer) {
this.#cachegetUint8 = new Uint8Array(this.#wasm!.memory.buffer);
}
return this.#cachegetUint8;
}
/**
* @internal
* @description Retrieves an Uint8Array in the WASM interface
*/
getU8a (ptr: number, len: number): Uint8Array {
return this.getUint8().subarray(ptr / 1, ptr / 1 + len);
}
/**
* @internal
* @description Retrieves a string in the WASM interface
*/
getString (ptr: number, len: number): string {
return u8aToString(this.getU8a(ptr, len));
}
/**
* @internal
* @description Allocates an Uint8Array in the WASM interface
*/
allocU8a (arg: Uint8Array): [number, number] {
const ptr = this.#wasm!.__wbindgen_malloc(arg.length * 1);
this.getUint8().set(arg, ptr / 1);
return [ptr, arg.length];
}
/**
* @internal
* @description Allocates a string in the WASM interface
*/
allocString (arg: string): [number, number] {
return this.allocU8a(stringToU8a(arg));
}
/**
* @internal
* @description Retrieves an Uint8Array from the WASM interface
*/
resultU8a (): Uint8Array {
const r0 = this.getInt32()[8 / 4 + 0];
const r1 = this.getInt32()[8 / 4 + 1];
const ret = this.getU8a(r0, r1).slice();
this.#wasm!.__wbindgen_free(r0, r1 * 1);
return ret;
}
/**
* @internal
* @description Retrieve a string from the WASM interface
*/
resultString (): string {
return u8aToString(this.resultU8a());
}
}