I was reading this article (https://www.smashingmagazine.com/2019/04/webassembly-speed-web-app/) that explained how they used zlib, among other things, to speed up their web project:
To support the zlib library, we use the flag USE_ZLIB; zlib is so common that it’s already been ported to WebAssembly, and Emscripten will include it for us in our project
I would like to use zlib in my own WASM module.
In my C code (compiled with emcc), I wrote this interfacing function:
#include <zlib.h>
int pcf_decompress_zlib(unsigned char *input, int input_length, unsigned char *output, int output_length)
{
uLongf output_length_result = output_length;
int result = uncompress(output, &output_length_result, input, input_length);
if (result != Z_OK) {
return 0;
} else {
return output_length_result;
}
}
I compiled it like so:
emcc decompress.c -O3 -s WASM=1 -s SIDE_MODULE=1 -s "EXPORTED_FUNCTIONS=['_pcf_decompress_zlib']" -s USE_ZLIB=1 -o decompress.wasm
When I did that, emcc automatically downloaded in a zlib library, so it seemed to know how to handle this.
Then in the browser, I have this class:
export class Decompressor {
wasmOnLoad(obj) {
this.instance = obj.instance;
console.log("Loaded WASM");
console.log(obj.instance);
// Don't do anything else yet
}
constructor() {
this.memory = new WebAssembly.Memory({
initial: 1
});
this.heap = new Uint8Array(this.memory.buffer);
this.imports = {
env: {
__memory_base: 0,
memory: this.memory,
abort: function(err) {
throw new Error('abort ' + err);
},
}
};
}
start() {
console.log("startWasm");
WebAssembly.instantiateStreaming(fetch('decompress/decompress.wasm'), this.imports)
.then(this.wasmOnLoad.bind(this));
}
}
And then this in my main JS code loaded from my HTML:
import { Decompressor } from "./decompress/decompress.js";
var l = new Decompressor();
l.start();
When I load the page, Firefox gives me this error:
LinkError: import object field '_uncompress' is not a Function
It appears that the wasm code being emitted doesn't include zlib, and zlib is also not built into the browser. I thought about changing SIDE_MODULE
to MAIN_MODULE
, but that resulted in dozens of undefined symbols, making the problem even worse.
There would be no point in having emcc provide a USE_ZLIB=1
option if it didn't automatically make zlib available. So what am I missing t make this work? How do I get emcc to statically include the zlib code that it already has into the wasm module I'm compiling?
Thanks.
When you've written a new code module in a language like C/C++, you can compile it into WebAssembly using a tool like Emscripten.
Once GCC supports '--target=[WebAssembly]', it can then be ported so that it can itself be compiled for/on a WebAssembly system (GCC native build), and then run inside a WebAssembly "machine" (web browser). >
A program that can take our existing C++ code and compile it into a WebAssembly module. For this, we'll use Emscripten's emcc compiler, the most popular C++ to WebAssembly compiler of the bunch. To use the WebAssembly functionality from JavaScript, a binding is required.
By itself, WebAssembly cannot currently directly access the DOM; it can only call JavaScript, passing in integer and floating point primitive data types. Thus, to access any Web API, WebAssembly needs to call out to JavaScript, which then makes the Web API call.
One way is to include the zlib source during the emcc
build. I tested below. First, create this file structure (include the zlib source folder you downloaded)
$ tree -L 2 .
.
├── build.sh
├── dist
├── lib
│ └── zlib-1.2.11
└── src
└── decompress.c
build.sh
ZLIB="lib/zlib-1.2.11"
emcc \
-O3 \
-s WASM=1 \
-s EXPORTED_FUNCTIONS="[ \
'_free', '_malloc' \
, '_pcf_decompress_zlib' \
]" \
-I $ZLIB \
-o dist/decompress.wasm \
$ZLIB/*.c \
src/decompress.c
Now, configure zlib and build!
$ lib/zlib-1.2.11/configure `# you only need to run this once`
$ ./build.sh
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With