1
1
// initialize wasm module
2
2
3
3
/** @internal */
4
- var __INIT__: (m: any) => Promise<any> = __INIT__;
4
+ declare var __INIT__: (m: any) => Promise<IWasmModule>;
5
+
6
+ /** @interal */
7
+ const enum ByteSize {
8
+ i8 = 1,
9
+ i16 = 2,
10
+ i32 = 4,
11
+ i64 = 8,
12
+ float = 4,
13
+ double = 8,
14
+ }
5
15
6
16
/** @internal */
7
- let TA_WASM: any;
17
+ type IWasmCTypes = 'i1' | 'i8' | 'i16' | 'i32' | 'i64' | 'float' | 'double';
18
+
19
+ /** @internal */
20
+ interface IWasmModule {
21
+ _malloc(size: number): number;
22
+ _free(offset: number): void;
23
+ HEAP8: Int8Array;
24
+ HEAP16: Int16Array;
25
+ HEAP32: Int32Array;
26
+ HEAPU8: Uint8Array;
27
+ HEAPU16: Uint16Array;
28
+ HEAPU32: Uint32Array;
29
+ HEAPF32: Float32Array;
30
+ HEAPF64: Float64Array;
31
+ ccall(
32
+ funcIdent: string,
33
+ retCodeType: string,
34
+ argTypes: any[],
35
+ args: any[]
36
+ ): number;
37
+ setValue(ptr: number, value: number, type: IWasmCTypes): void;
38
+ getValue(ptr: number, type: IWasmCTypes): number;
39
+ }
8
40
9
41
/** @internal */
10
- function cArray(Module: any, size: number) {
11
- const offset = Module._malloc(size * 8);
12
- Module.HEAPF64.set(new Float64Array(size), offset / 8);
42
+ let Module: IWasmModule;
43
+
44
+ /** @internal */
45
+ function double_array(size: number) {
46
+ const BYTE_SIZE = ByteSize.double;
47
+ const offset = Module._malloc(size * BYTE_SIZE);
48
+ const offsetF64 = offset / BYTE_SIZE;
49
+ Module.HEAPF64.set(new Float64Array(size), offsetF64);
13
50
return {
14
- data: Module.HEAPF64.subarray(offset / 8, offset / 8 + size),
15
- offset: offset,
51
+ data: Module.HEAPF64.subarray(offsetF64, offsetF64 + size),
52
+ pointer: offset,
53
+ };
54
+ }
55
+
56
+ /** @internal */
57
+ function c_pointer(type: IWasmCTypes, initValue?: number) {
58
+ const offset = Module._malloc(ByteSize.i32);
59
+ const ref = {
60
+ get data() {
61
+ return Module.getValue(offset, type);
62
+ },
63
+ set data(val: number) {
64
+ Module.setValue(offset, val, type);
65
+ },
66
+ pointer: offset,
16
67
};
68
+ if (initValue !== undefined) {
69
+ ref.data = initValue;
70
+ }
71
+ return ref;
17
72
}
18
73
19
74
/** @internal */
@@ -39,6 +94,10 @@ const TA_RET_CODE = {
39
94
[0xffff]: 'TA_UNKNOWN_ERR',
40
95
};
41
96
97
+ // src/ta-lib/include/ta_defs.h:213
98
+ /** @internal */
99
+ const TA_INTEGER_DEFAULT = -2147483648;
100
+
42
101
/** @internal */
43
102
type APIDescriptor = {
44
103
name: string;
@@ -70,7 +129,7 @@ type APIDescriptor = {
70
129
/** @internal */
71
130
function callFunc(api: APIDescriptor, params: any): any {
72
131
const funcIdent = `TA_${api.name}`;
73
- if (!TA_WASM ) throw Error(`${api.name}() called before initialization.`);
132
+ if (!Module ) throw Error(`${api.name}() called before initialization.`);
74
133
75
134
// prettier-ignore
76
135
const ccallArgsLen =
@@ -80,7 +139,7 @@ function callFunc(api: APIDescriptor, params: any): any {
80
139
2 /* outBegIdx, outNBElement */ +
81
140
api.outputs.length;
82
141
83
- const argTypesToCcall = new Array(ccallArgsLen).fill('number');
142
+ const argTypes = new Array(ccallArgsLen).fill('number');
84
143
85
144
/**
86
145
* Input params validation
@@ -93,9 +152,9 @@ function callFunc(api: APIDescriptor, params: any): any {
93
152
}
94
153
}
95
154
96
- for (const { name, defaultValue, range } of api.options) {
155
+ for (const { name, range } of api.options) {
97
156
if (params[name] === undefined) {
98
- params[name] = defaultValue ;
157
+ params[name] = TA_INTEGER_DEFAULT ;
99
158
} else if (
100
159
range &&
101
160
(params[name] < range.min || params[name] > range.max)
@@ -115,50 +174,62 @@ function callFunc(api: APIDescriptor, params: any): any {
115
174
}
116
175
117
176
/**
118
- * Constructing `argsToCcall ` to pass to the `Module.ccall` API
119
- * Move things from `params` onto `argsToCcall `
177
+ * Constructing `args ` to pass to the `Module.ccall` API
178
+ * Move things from `params` onto `args `
120
179
*
121
180
* TA-Lib function signatures are of following form:
122
181
* ```
123
182
* FUNC(startIdx, endIdx, ...params, outBegIdx, outNBElement, ...outputs)
124
183
* ```
125
184
*/
126
- const argsToCcall = [startIdx, endIdx];
127
- const arraysToRelease = [];
185
+ const args = [startIdx, endIdx];
186
+ const memToFree = [];
128
187
129
188
api.inputs.forEach(({ name }) => {
130
- const argArray = cArray(TA_WASM, endIdx - startIdx);
189
+ const argArray = double_array( endIdx - startIdx);
131
190
/** @type {number[]} */
132
191
const paramArray = params[name];
133
192
for (const i in paramArray) argArray.data[i] = paramArray[i];
134
- arraysToRelease .push(argArray);
135
- argsToCcall .push(argArray.offset );
193
+ memToFree .push(argArray.pointer );
194
+ args .push(argArray.pointer );
136
195
});
137
196
138
- api.options.forEach(({ name }) => argsToCcall .push(params[name]));
197
+ api.options.forEach(({ name }) => args .push(params[name]));
139
198
140
- argsToCcall.push(0); // outBegIdx
141
- argsToCcall.push(0); // outNBElement
199
+ const outBegIdxRef = c_pointer('i32', 0);
200
+ const outNBElementRef = c_pointer('i32', 0);
201
+ memToFree.push(outBegIdxRef.pointer);
202
+ memToFree.push(outNBElementRef.pointer);
203
+ args.push(outBegIdxRef.pointer);
204
+ args.push(outNBElementRef.pointer);
142
205
143
206
const outputs = api.outputs.map(({ name }) => {
144
- const argArray = cArray(TA_WASM, endIdx - startIdx);
145
- arraysToRelease .push(argArray);
146
- argsToCcall .push(argArray.offset );
207
+ const argArray = double_array( endIdx - startIdx);
208
+ memToFree .push(argArray.pointer );
209
+ args .push(argArray.pointer );
147
210
return { name, array: argArray };
148
211
});
149
212
150
- const retCode = TA_WASM .ccall(
213
+ const retCode = Module .ccall(
151
214
funcIdent,
152
215
'number' /* TA_RET_CODE */,
153
- argTypesToCcall ,
154
- argsToCcall
216
+ argTypes ,
217
+ args
155
218
);
156
- arraysToRelease.forEach((arr) => TA_WASM._free(arr.offset));
157
219
158
- const result = outputs.reduce((result, current) => {
159
- result[current.name] = Array.from(current.array.data);
160
- return result;
161
- }, {});
220
+ const outBegIdx = outBegIdxRef.data;
221
+ const outNBElement = outNBElementRef.data;
222
+
223
+ const result = outputs.reduce(
224
+ (result, current) => {
225
+ const data = Array.from(current.array.data.slice(0, outNBElement));
226
+ result[current.name] = data;
227
+ return result;
228
+ },
229
+ { outBegIdx, outNBElement }
230
+ );
231
+
232
+ memToFree.forEach((offset) => Module._free(offset));
162
233
163
234
if (retCode === 0) {
164
235
// success
@@ -221,8 +292,8 @@ export enum MAType {
221
292
* @param wasmBinaryFilePath - optional, a string that specifies the location of wasm binary file
222
293
* @returns A promise that resolves to the emscripten runtime `Module` object. See {@link https://emscripten.org/docs/api_reference/module.html}.
223
294
*/
224
- export function init(wasmBinaryFilePath?: string) {
225
- if (TA_WASM ) return Promise.resolve(TA_WASM );
295
+ export function init(wasmBinaryFilePath?: string): Promise<IWasmModule> {
296
+ if (Module ) return Promise.resolve(Module );
226
297
227
298
if (wasmBinaryFilePath && typeof wasmBinaryFilePath !== 'string') {
228
299
return Promise.reject(
@@ -234,7 +305,7 @@ export function init(wasmBinaryFilePath?: string) {
234
305
235
306
const locateFile = wasmBinaryFilePath ? () => wasmBinaryFilePath : undefined;
236
307
return __INIT__({ locateFile })
237
- .then((Module ) => (TA_WASM = Module ))
308
+ .then((m ) => (Module = m ))
238
309
.catch((e) => {
239
310
let message = 'TA-Lib WASM runtime init fail.';
240
311
if (e && e.message) {
0 commit comments