Skip to content

Instantly share code, notes, and snippets.

@shritesh
Last active April 18, 2019 02:43
Show Gist options
  • Save shritesh/1f6f4b6843e72df3aaa880a1ff786b93 to your computer and use it in GitHub Desktop.
Save shritesh/1f6f4b6843e72df3aaa880a1ff786b93 to your computer and use it in GitHub Desktop.
LeftPad in JS With Zig and WASM
const fs = require('fs');
const source = fs.readFileSync("./left_pad.wasm");
const sourceArray = new Uint8Array(source);
WebAssembly.instantiate(sourceArray, {}).then(result => {
const leftPad = left_pad.bind(result.instance.exports);
const paddedStr = leftPad("hello zig", 20, '*');
console.log(paddedStr);
});
// This is intentionally verbose
const left_pad = function(str, len, char) {
// convert str to Uint8Array
const textEncoder = new TextEncoder();
const strArray = textEncoder.encode(str);
// get memory from wasm
// assume first memory locations are for return values, the rest are for params
const return_len = (32 / 8) + (32 / 8); // u32, u32
const str_len = strArray.length;
const ptr = this._wasm_alloc(return_len + str_len);
if (ptr === 0) {
throw "Cannot allocate memory";
}
// copy strArray to wasm
var memoryu8 = new Uint8Array(this.memory.buffer);
for (let i = 0; i < str_len; ++i) {
memoryu8[ptr + return_len + i] = strArray[i];
}
// call function
const succeed = this.left_pad_extern(ptr + return_len, str_len, len, char.codePointAt(0), ptr, ptr + (32/ 8));
// read result into Uint32Array()
const return_slice = new Uint32Array(this.memory.buffer.slice(ptr, ptr + return_len));
const return_val_ptr = return_slice[0];
const return_val_len = return_slice[1];
// dealloc function params
this._wasm_dealloc(ptr, return_len + str_len);
// throw if function returned error
if (!succeed) {
throw "WASM Call returned error"
}
// get the result
const result = new Uint8Array(this.memory.buffer.slice(return_val_ptr, return_val_ptr + return_val_len));
// dealloc result
this._wasm_dealloc(return_val_ptr, return_val_len);
// decode result
const textDecoder = new TextDecoder();
// return result
return textDecoder.decode(result);
}
const std = @import("std");
const Allocator = std.mem.Allocator;
/// Returns a `len`-length u8 slice with `str` padded by `char` in the front
/// Caller must free
pub fn left_pad(allocator: *Allocator, str: []const u8, len: usize, char: u8) ![]u8 {
if (len < str.len) {
return error.InvalidLength;
}
const buf = try allocator.alloc(u8, len);
errdefer allocator.free(buf);
const pad = len - str.len;
var i: usize = 0;
while (i < len) : (i += 1) {
buf[i] = if (i < pad) char else str[i - pad];
}
return buf;
}
export fn left_pad_extern(
input_ptr: [*]const u8,
input_len: usize,
len: usize,
char: u8,
output_ptr: *[*]u8,
output_size: *usize,
) bool {
const input = input_ptr[0..input_len];
var output = left_pad(std.heap.wasm_allocator, input, len, char) catch |err| return false;
output_ptr.* = output.ptr;
output_size.* = output.len;
return true;
}
export fn _wasm_alloc(len: usize) u32 {
var buf = std.heap.wasm_allocator.alloc(u8, len) catch |err| return 0;
return @ptrToInt(buf.ptr);
}
export fn _wasm_dealloc(ptr: [*]const u8, len: usize) void {
std.heap.wasm_allocator.free(ptr[0..len]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment