skip to content
==> include/wasm/leb128.h <==
1/* @ajax/wasm-leb128: LEB128 as the WebAssembly binary format defines it (spec 5.2.2).
2 *
3 * Each reader decodes one number from [p, end) and returns the bytes it used, or 0 when the
4 * encoding is truncated, longer than ceil(N / 7) bytes, or its last byte has bits that don't fit
5 * N bits (unsigned: must be zero; signed: must repeat the sign). Freestanding, header-only. */
6#ifndef WASM_LEB128_H
7#define WASM_LEB128_H
8
9#include <stddef.h>
10#include <stdint.h>
11
12/* Unsigned, at most bits (1..64) bits. */
13static inline size_t wasm_leb_unsigned(const uint8_t *p, const uint8_t *end, unsigned bits, uint64_t *out) {
14    uint64_t v = 0;
15    unsigned shift = 0;
16    size_t max = (bits + 6) / 7;
17    for (size_t i = 0; i < max; i++) {
18        if (p + i >= end) return 0;
19        uint8_t b = p[i];
20        if (i == max - 1) {
21            /* The last byte may only hold the bits left. */
22            unsigned left = bits - shift;
23            if (b & 0x80) return 0;
24            if (left < 7 && (b >> left) != 0) return 0;
25        }
26        v |= (uint64_t)(b & 0x7f) << shift;
27        shift += 7;
28        if (!(b & 0x80)) {
29            *out = v;
30            return i + 1;
31        }
32    }
33    return 0;
34}
35
36/* Signed, at most bits (1..64) bits, sign-extended to 64. */
37static inline size_t wasm_leb_signed(const uint8_t *p, const uint8_t *end, unsigned bits, int64_t *out) {
38    uint64_t v = 0;
39    unsigned shift = 0;
40    size_t max = (bits + 6) / 7;
41    for (size_t i = 0; i < max; i++) {
42        if (p + i >= end) return 0;
43        uint8_t b = p[i];
44        if (i == max - 1) {
45            if (b & 0x80) return 0;
46            /* The unused high bits of the last byte must all equal the sign bit of the value. */
47            unsigned left = bits - shift; /* 1..7 value bits in this byte */
48            if (left < 7) {
49                uint8_t sign = (uint8_t)((b >> (left - 1)) & 1);
50                uint8_t rest = (uint8_t)((b & 0x7f) >> left);
51                uint8_t want = sign ? (uint8_t)(0x7f >> left) : 0;
52                if (rest != want) return 0;
53            }
54        }
55        v |= (uint64_t)(b & 0x7f) << shift;
56        shift += 7;
57        if (!(b & 0x80)) {
58            if (shift < 64 && (b & 0x40)) v |= ~(uint64_t)0 << shift;
59            *out = (int64_t)v;
60            return i + 1;
61        }
62    }
63    return 0;
64}
65
66static inline size_t wasm_leb_u32(const uint8_t *p, const uint8_t *end, uint32_t *out) {
67    uint64_t v;
68    size_t n = wasm_leb_unsigned(p, end, 32, &v);
69    if (n) *out = (uint32_t)v;
70    return n;
71}
72
73static inline size_t wasm_leb_u64(const uint8_t *p, const uint8_t *end, uint64_t *out) {
74    return wasm_leb_unsigned(p, end, 64, out);
75}
76
77static inline size_t wasm_leb_s32(const uint8_t *p, const uint8_t *end, int32_t *out) {
78    int64_t v;
79    size_t n = wasm_leb_signed(p, end, 32, &v);
80    if (n) *out = (int32_t)v;
81    return n;
82}
83
84/* Block types are s33. */
85static inline size_t wasm_leb_s33(const uint8_t *p, const uint8_t *end, int64_t *out) {
86    return wasm_leb_signed(p, end, 33, out);
87}
88
89static inline size_t wasm_leb_s64(const uint8_t *p, const uint8_t *end, int64_t *out) {
90    return wasm_leb_signed(p, end, 64, out);
91}
92
93#endif