Skip to content

Commit 1d242db

Browse files
author
hackape
committed
fix: output outBegIdx and outNBElement
1 parent 24d8b9c commit 1d242db

File tree

2 files changed

+109
-36
lines changed

2 files changed

+109
-36
lines changed

scripts/gencode.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ function genCode(desc) {
5757
});
5858

5959
const __OUTS__ =
60-
'{ ' + desc.outputs.map((o) => `${o.name}: number[]`).join('; ') + ' }';
60+
'{ outBegIdx: number; outNBElement: number; ' +
61+
desc.outputs.map((o) => `${o.name}: number[];`).join(' ') +
62+
' }';
6163

6264
// prettier-ignore
6365
const code = `/** @internal */

scripts/template

Lines changed: 106 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,74 @@
11
// initialize wasm module
22

33
/** @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+
}
515

616
/** @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+
}
840

941
/** @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);
1350
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,
1667
};
68+
if (initValue !== undefined) {
69+
ref.data = initValue;
70+
}
71+
return ref;
1772
}
1873

1974
/** @internal */
@@ -39,6 +94,10 @@ const TA_RET_CODE = {
3994
[0xffff]: 'TA_UNKNOWN_ERR',
4095
};
4196

97+
// src/ta-lib/include/ta_defs.h:213
98+
/** @internal */
99+
const TA_INTEGER_DEFAULT = -2147483648;
100+
42101
/** @internal */
43102
type APIDescriptor = {
44103
name: string;
@@ -70,7 +129,7 @@ type APIDescriptor = {
70129
/** @internal */
71130
function callFunc(api: APIDescriptor, params: any): any {
72131
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.`);
74133

75134
// prettier-ignore
76135
const ccallArgsLen =
@@ -80,7 +139,7 @@ function callFunc(api: APIDescriptor, params: any): any {
80139
2 /* outBegIdx, outNBElement */ +
81140
api.outputs.length;
82141

83-
const argTypesToCcall = new Array(ccallArgsLen).fill('number');
142+
const argTypes = new Array(ccallArgsLen).fill('number');
84143

85144
/**
86145
* Input params validation
@@ -93,9 +152,9 @@ function callFunc(api: APIDescriptor, params: any): any {
93152
}
94153
}
95154

96-
for (const { name, defaultValue, range } of api.options) {
155+
for (const { name, range } of api.options) {
97156
if (params[name] === undefined) {
98-
params[name] = defaultValue;
157+
params[name] = TA_INTEGER_DEFAULT;
99158
} else if (
100159
range &&
101160
(params[name] < range.min || params[name] > range.max)
@@ -115,50 +174,62 @@ function callFunc(api: APIDescriptor, params: any): any {
115174
}
116175

117176
/**
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`
120179
*
121180
* TA-Lib function signatures are of following form:
122181
* ```
123182
* FUNC(startIdx, endIdx, ...params, outBegIdx, outNBElement, ...outputs)
124183
* ```
125184
*/
126-
const argsToCcall = [startIdx, endIdx];
127-
const arraysToRelease = [];
185+
const args = [startIdx, endIdx];
186+
const memToFree = [];
128187

129188
api.inputs.forEach(({ name }) => {
130-
const argArray = cArray(TA_WASM, endIdx - startIdx);
189+
const argArray = double_array(endIdx - startIdx);
131190
/** @type {number[]} */
132191
const paramArray = params[name];
133192
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);
136195
});
137196

138-
api.options.forEach(({ name }) => argsToCcall.push(params[name]));
197+
api.options.forEach(({ name }) => args.push(params[name]));
139198

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);
142205

143206
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);
147210
return { name, array: argArray };
148211
});
149212

150-
const retCode = TA_WASM.ccall(
213+
const retCode = Module.ccall(
151214
funcIdent,
152215
'number' /* TA_RET_CODE */,
153-
argTypesToCcall,
154-
argsToCcall
216+
argTypes,
217+
args
155218
);
156-
arraysToRelease.forEach((arr) => TA_WASM._free(arr.offset));
157219

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));
162233

163234
if (retCode === 0) {
164235
// success
@@ -221,8 +292,8 @@ export enum MAType {
221292
* @param wasmBinaryFilePath - optional, a string that specifies the location of wasm binary file
222293
* @returns A promise that resolves to the emscripten runtime `Module` object. See {@link https://emscripten.org/docs/api_reference/module.html}.
223294
*/
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);
226297

227298
if (wasmBinaryFilePath && typeof wasmBinaryFilePath !== 'string') {
228299
return Promise.reject(
@@ -234,7 +305,7 @@ export function init(wasmBinaryFilePath?: string) {
234305

235306
const locateFile = wasmBinaryFilePath ? () => wasmBinaryFilePath : undefined;
236307
return __INIT__({ locateFile })
237-
.then((Module) => (TA_WASM = Module))
308+
.then((m) => (Module = m))
238309
.catch((e) => {
239310
let message = 'TA-Lib WASM runtime init fail.';
240311
if (e && e.message) {

0 commit comments

Comments
 (0)