2

I have C code as follows:

int stringlen(char* p){
    return strlen(p);
}

Which I compile using Emscripten compiler: emcc -s EXPORTED_FUNCTIONS="['_stringlen']" example.c -o example.js

Then, in Node.js, I call the compiled function em_module1._stringlen('sadfsa'). Unfortunately, the function returns 4 instead of 6 (the correct length of the string 'sadfsa').

How can I pass correctly the char* from the Node.js to this compiled function?

quepas
  • 956
  • 1
  • 13
  • 30
vincent
  • 113
  • 8
  • embind & c++ & std::string is easier than c & raw pointer. https://stackoverflow.com/questions/21816960/how-to-pass-strings-between-c-and-javascript-via-emscripten/38237481 – zakki May 08 '18 at 05:49

1 Answers1

1

Explanation

The compiled WebAssembly module, with the function _stringlen() inside, uses its memory expressed as a linear memory. The compiled function only knows the memory of its module. Therefore, the pointer char* must point to a chunk of the module's memory.

Quick solution

We will keep the C code intact, except for adding the missing include:

#include <string.h>

int stringlen(char *p)
{
   return strlen(p);
};

The output JavaScript code example.js contains functions for memory management and more. Next, in the test.js script, we:

  1. Convert our string to raw bytes (into UInt8Array)
  2. Allocate a chunk of the module's memory for the string (no need for \0 character at the end)
  3. (A) Access the allocated chunk of the module's memory and (B) fill it with our raw string (we access the memory as it represents single unsigned bytes HEAPU8)
  4. Free the allocated chunk
const Module = require('./example.js');

const str_raw = new TextEncoder().encode('Lorem ipsum'); // 1)
let ptr = Module._malloc(str_raw.length); // 2)
let chunk = Module.HEAPU8.subarray(ptr, ptr + str_raw.length); // 3A)
chunk.set(str_raw); // 3B)

console.log(Module._stringlen(chunk.byteOffset));
Module._free(ptr); // 4)

For the compilation, I have forced a synchronised compilation of the WebAssembly module (-s WASM_ASYNC_COMPILATION=0) to make sure the module will be ready after require():

emcc -s EXPORTED_FUNCTIONS="['_stringlen']" example.c -o example.js -s WASM_ASYNC_COMPILATION=0

We execute the script as follows node test.js.

Other solutions

My favourite solution is the already mentioned embind. It requires you to use C++:

#include <string>
#include <emscripten.h>
#include <emscripten/bind.h>

size_t stringlen(std::string p)
{
   return p.size();
};

EMSCRIPTEN_BINDINGS(Module)
{
   emscripten::function("stringlen", &stringlen);
}

We compile the code by indicating the use of embind with --bind flag:

emcc example.cpp -o example.js -s WASM_ASYNC_COMPILATION=0 -std=c++11 --bind

The function call is much simpler now:

const Module = require('./example.js');

console.log(Module.stringlen('Lorem ipsum'))
quepas
  • 956
  • 1
  • 13
  • 30