Seven bits at a time
Wasm stores sizes, indexes and constants as LEB128: each byte carries 7 bits of the number, and its top bit says "more bytes follow". Small numbers (most of them) take one byte. Signed LEB128 is the same idea with the sign extended from bit 6 of the last byte.
Encode and decode
function encodeULEB128(n) {
const out = [];
do {
let byte = n & 0x7f; // low 7 bits
n >>>= 7;
if (n !== 0) byte |= 0x80; // more bytes follow
out.push(byte);
} while (n !== 0);
return out;
}
function encodeSLEB128(n) {
const out = [];
while (true) {
const byte = n & 0x7f;
n >>= 7; // arithmetic shift keeps the sign
const done = (n === 0 && (byte & 0x40) === 0) || (n === -1 && (byte & 0x40) !== 0);
if (done) {
out.push(byte);
return out;
}
out.push(byte | 0x80);
}
}
function decodeULEB128(bytes, at = 0) {
let value = 0;
let shift = 0;
let byte;
do {
byte = bytes[at++];
value |= (byte & 0x7f) << shift;
shift += 7;
} while (byte & 0x80);
return { value: value >>> 0, next: at };
}