Skip to content

Commit

Permalink
Add browser support; Fixed memory leak
Browse files Browse the repository at this point in the history
  • Loading branch information
samsam2310 committed Jun 4, 2020
1 parent 93d6243 commit ebdcd62
Show file tree
Hide file tree
Showing 13 changed files with 103 additions and 28 deletions.
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ EM_VERSION = 1.39.8-upstream
EM_DOCKER = docker run --rm -w /src -v $$PWD:/src trzeci/emscripten:$(EM_VERSION)
EMCC = $(EM_DOCKER) emcc
EMXX = $(EM_DOCKER) em++
WASM2WAT = $(EM_DOCKER) wasm2wat
EMMAKE = $(EM_DOCKER) emmake
EMCONFIG = $(EM_DOCKER) emconfigure

Expand All @@ -20,16 +21,20 @@ TSC_FLAGS = -p ./

all: dist/zbar.wasm .ts

debug: $(ZBAR_DEPS) src/module.cc
debug: $(ZBAR_DEPS) dist/zbar.wast src/module.cc
$(EMXX) $(EMXX_FLAGS) -g2 -o dist/zbar-debug.js src/module.cc $(ZBAR_INC) \
$(ZBAR_OBJS)

dist/symbol.test.o: $(ZBAR_DEPS) src/symbol.test.c
$(EMCC) -Wall -Werror -g2 -c src/symbol.test.c -o $@ $(ZBAR_INC)

dist/zbar.wast: dist/zbar.wasm
$(WASM2WAT) dist/zbar.wasm -o dist/zbar.wast

dist/zbar.wasm: $(ZBAR_DEPS) src/module.cc dist/symbol.test.o
$(EMXX) $(EMXX_FLAGS) -o dist/zbar.wasm src/module.cc $(ZBAR_INC) \
$(ZBAR_OBJS)
cp dist/zbar.wasm dist/zbar.wasm.bin

$(ZBAR_DEPS): $(ZBAR_SOURCE)/Makefile
cd $(ZBAR_SOURCE) && $(EMMAKE) make CFLAGS=-Os CXXFLAGS=-Os
Expand Down
1 change: 1 addition & 0 deletions dist/.npmignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.gitignore
*.pre
*.o
*.wast
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
"name": "zbar.wasm",
"version": "1.0.0",
"description": "A wasm build for C/C++ Zbar barcode scanning library.",
"main": "index.js",
"type": "module",
"main": "dist/index.js",
"browser": {
"./dist/load.js": "./dist/load-browser.js"
},
"jest": {
"testMatch": [
"**/?(*.)+(spec|test).[j]s?(x)"
Expand Down
2 changes: 2 additions & 0 deletions src/Symbol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class SymbolSetPtr extends TypePointer {

export class Symbol {
type: ZBarSymbolType;
typeName: string;
data: Int8Array;
points: Array<Point>;
time: number;
Expand All @@ -88,6 +89,7 @@ export class Symbol {

private constructor(ptr: SymbolPtr) {
this.type = ptr.type;
this.typeName = ZBarSymbolType[this.type];
this.data = ptr.data;
this.points = ptr.points;
this.time = ptr.time;
Expand Down
19 changes: 15 additions & 4 deletions src/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,12 @@ const ___wasi_fd_seek = () => {
return 0;
};

let lastGrowTimestamp = 0;
const emscripten_notify_memory_growth = (idx: number) => {
if (lastGrowTimestamp) {
console.warn('zbar.wasm: Memory Grow: ', inst!.memory.buffer.byteLength);
}
lastGrowTimestamp = Date.now();
HEAPU8 = new Uint8Array(inst!.memory.buffer);
HEAP32 = new Int32Array(inst!.memory.buffer);
};
Expand All @@ -95,14 +100,20 @@ const importObj = {
}
};

let instPromise = loadWasmInstance(importObj);

export const getInstance = async (): Promise<ZBar> => {
const res = await instPromise;
let instPromise = (async () => {
const res = await loadWasmInstance(importObj);
if (!res) {
throw Error('WASM was not loaded');
}
inst = res.exports as ZBar;
emscripten_notify_memory_growth(0);
return inst;
})();

export const getInstance = async (): Promise<ZBar> => {
return await instPromise;
};

export const getMemoryGrowTimestamp = (): number => {
return lastGrowTimestamp;
};
29 changes: 18 additions & 11 deletions src/load-browser.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import wasmBinaryFile from 'raw-loader!./zbar.wasm';
/**
* Webpack File-loader will break when the extension is .wasm.
* Changing the extension is a workaround. And because of this
* |instantiateStreaming| is always failed due to wrong MIME type.
* see https://github.com/webpack/webpack/issues/6725
*/
// import wasmBinaryFile from './zbar.wasm';
import wasmBinaryFile from './zbar.wasm.bin';

export const loadWasmInstance = async (
importObj: any
): Promise<WebAssembly.Instance | null> => {
try {
const output = await WebAssembly.instantiateStreaming(
fetch(wasmBinaryFile),
importObj
);
return output.instance;
} catch (err) {
console.error('Wasm streaming compile failed: ' + err);
console.error('Falling back to ArrayBuffer instantiation');
}
// try {
// const output = await WebAssembly.instantiateStreaming(
// fetch(wasmBinaryFile),
// importObj
// );
// return output.instance;
// } catch (err) {
// console.error('Wasm streaming compile failed: ' + err);
// console.error('Falling back to ArrayBuffer instantiation');
// }
const res = await fetch(wasmBinaryFile);
if (!res['ok']) {
console.error('Failed to load wasm binary file at ' + wasmBinaryFile);
Expand Down
5 changes: 3 additions & 2 deletions src/module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,19 @@ EXPORT int ImageScanner_scan(zbar::ImageScanner* scanner, zbar::Image* image) {
EXPORT zbar::Image* Image_create(uint32_t width,
uint32_t height,
uint32_t format,
const void* data,
void* data,
uint32_t length,
uint32_t sequence_num) {
zbar::Image* image = new zbar::Image(width, height);
image->set_format(format);
/* image will take ownership of data */
image->set_data(data, length);
image->set_sequence(sequence_num);
return image;
}

EXPORT void Image_destory(zbar::Image* image) {
/* Image object won't free image data. */
free(const_cast<void*>(image->get_data()));
delete image;
}

Expand Down
28 changes: 28 additions & 0 deletions src/test/memorygrow.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { getImageData } from './utils';
import { getInstance, getMemoryGrowTimestamp } from '../instance';
import { scanImageData } from '../module';

test('Multiple Scan Test', async () => {
const inst = await getInstance();
const dir = __dirname + '/../../src/test';
let res;
const img4 = await getImageData(dir + '/test4.png');
res = await scanImageData(img4);
expect(res).toHaveLength(2);

let t1 = getMemoryGrowTimestamp();
inst.malloc(100000000);
let t2 = getMemoryGrowTimestamp();
expect(t1).not.toEqual(t2);
t1 = t2;

for (let i = 0; i < 100; ++i) {
res = await scanImageData(img4);
expect(res).toHaveLength(2);

inst.malloc(655360);
t2 = getMemoryGrowTimestamp();
expect(t1).not.toEqual(t2);
t1 = t2;
}
});
17 changes: 17 additions & 0 deletions src/test/memoryleak.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { getImageData } from './utils';
import { getMemoryGrowTimestamp } from '../instance';
import { scanImageData } from '../module';

test('Multiple Scan Test', async () => {
const dir = __dirname + '/../../src/test';
let res;
const img4 = await getImageData(dir + '/test4.png');
res = await scanImageData(img4);
expect(res).toHaveLength(2);
const memoryGrowTimestamp = getMemoryGrowTimestamp();
for (let i = 0; i < 100; ++i) {
res = await scanImageData(img4);
expect(res).toHaveLength(2);
expect(getMemoryGrowTimestamp()).toEqual(memoryGrowTimestamp);
}
});
5 changes: 3 additions & 2 deletions src/test/module.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ test('Barcode', async () => {
res = await scanImageData(img5);
expect(res).toHaveLength(0);

defaultScanner.destory();

const scanner = await ImageScanner.create();
res = await scanImageData(img5, scanner);
expect(res).toHaveLength(1);
Expand Down Expand Up @@ -95,4 +93,7 @@ test('Barcode', async () => {
expect(res).toHaveLength(1);
expect(res[0].type).toEqual(ZBarSymbolType.ZBAR_EAN13);
expect(res[0].decode()).toEqual('9781234567897');

scanner.destory();
expect(scanImageData(img5, scanner)).rejects.toThrow('Call after destroyed');
});
1 change: 0 additions & 1 deletion src/test/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import fs from 'fs';
import { createCanvas, loadImage } from 'canvas';

export const getImageData = async (src: string) => {
Expand Down
9 changes: 5 additions & 4 deletions src/zbar.wasm.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
declare module 'raw-loader!*' {
const content: string;
export default content;
}
declare module '*.bin';
// declare module '!./zbar.wasm' {
// const content: string;
// export default content;
// }
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "ES2016", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"target": "es2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "CommonJS", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
Expand Down

0 comments on commit ebdcd62

Please sign in to comment.