lib/choir/src/backends/wasm/binary/format.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const binary = @import("root.zig");
  3 
  4 pub fn writeHeader(out: anytype) !void {
  5     try out.writeAll(&.{ 0x00, 0x61, 0x73, 0x6d });
  6     try out.writeAll(&.{ 0x01, 0x00, 0x00, 0x00 });
  7 }
  8 
  9 pub fn writeSectionHeader(out: anytype, section: binary.Section, body_len: usize) !void {
 10     try out.writeByte(@backingInt(section));
 11     try writeUleb(out, body_len);
 12 }
 13 
 14 pub fn writeName(out: anytype, name: []const u8) !void {
 15     try writeUleb(out, name.len);
 16     try out.writeAll(name);
 17 }
 18 
 19 pub fn writeValueType(out: anytype, value_type: binary.ValueType) !void {
 20     try out.writeByte(@backingInt(value_type));
 21 }
 22 
 23 pub fn writeF32(out: anytype, value: f32) !void {
 24     try writeLittleU32(out, @bitCast(value));
 25 }
 26 
 27 pub fn writeF64(out: anytype, value: f64) !void {
 28     try writeLittleU64(out, @bitCast(value));
 29 }
 30 
 31 pub fn writeLittleU32(out: anytype, value: u32) !void {
 32     try out.writeByte(@intCast(value & 0xff));
 33     try out.writeByte(@intCast((value >> 8) & 0xff));
 34     try out.writeByte(@intCast((value >> 16) & 0xff));
 35     try out.writeByte(@intCast((value >> 24) & 0xff));
 36 }
 37 
 38 pub fn writeLittleU64(out: anytype, value: u64) !void {
 39     try writeLittleU32(out, @intCast(value & 0xffff_ffff));
 40     try writeLittleU32(out, @intCast(value >> 32));
 41 }
 42 
 43 pub fn writeUleb(out: anytype, value: anytype) !void {
 44     var remaining: u64 = @intCast(value);
 45     while (true) {
 46         var byte: u8 = @intCast(remaining & 0x7f);
 47         remaining >>= 7;
 48         if (remaining != 0) byte |= 0x80;
 49         try out.writeByte(byte);
 50         if (remaining == 0) return;
 51     }
 52 }
 53 
 54 pub fn writeSleb(out: anytype, value: i64) !void {
 55     var remaining = value;
 56     while (true) {
 57         var byte: u8 = @intCast(@as(u64, @bitCast(remaining)) & 0x7f);
 58         remaining >>= 7;
 59         const sign_bit = (byte & 0x40) != 0;
 60         const done = (remaining == 0 and !sign_bit) or (remaining == -1 and sign_bit);
 61         if (!done) byte |= 0x80;
 62         try out.writeByte(byte);
 63         if (done) return;
 64     }
 65 }
 66 
 67 test "wasm binary unsigned leb128 encoding" {
 68     var bytes: [3]u8 = undefined;
 69     var out = binary.Fixed.init(&bytes);
 70     try writeUleb(&out, @as(u32, 624485));
 71     try std.testing.expectEqualSlices(u8, &.{ 0xe5, 0x8e, 0x26 }, try out.finish());
 72 }
 73 
 74 test "wasm binary signed leb128 encoding" {
 75     var bytes: [3]u8 = undefined;
 76     var out = binary.Fixed.init(&bytes);
 77     try writeSleb(&out, -624485);
 78     try std.testing.expectEqualSlices(u8, &.{ 0x9b, 0xf1, 0x59 }, try out.finish());
 79 }
 80 
 81 test "wasm binary section header carries id and body size" {
 82     var bytes: [2]u8 = undefined;
 83     var out = binary.Fixed.init(&bytes);
 84     try writeSectionHeader(&out, .type, 4);
 85     try std.testing.expectEqualSlices(u8, &.{ 0x01, 0x04 }, try out.finish());
 86 }
 87 
 88 test "wasm binary counter and fixed sink share encodings" {
 89     var count = binary.Count{};
 90     try writeName(&count, "bounded");
 91     var bytes: [8]u8 = undefined;
 92     try std.testing.expectEqual(bytes.len, count.len);
 93     var out = binary.Fixed.init(&bytes);
 94     try writeName(&out, "bounded");
 95     try std.testing.expectEqualSlices(u8, &.{ 7, 'b', 'o', 'u', 'n', 'd', 'e', 'd' }, try out.finish());
 96 }
 97 
 98 test "wasm binary unsigned lengths cross canonical boundaries exactly" {
 99     const cases = [_]struct { value: u32, len: usize }{
100         .{ .value = 127, .len = 1 },
101         .{ .value = 128, .len = 2 },
102         .{ .value = 16_383, .len = 2 },
103         .{ .value = 16_384, .len = 3 },
104         .{ .value = std.math.maxInt(u32), .len = 5 },
105     };
106     for (cases) |case| {
107         var count = binary.Count{};
108         try writeUleb(&count, case.value);
109         try std.testing.expectEqual(case.len, count.len);
110         var bytes: [5]u8 = undefined;
111         var out = binary.Fixed.init(bytes[0..case.len]);
112         try writeUleb(&out, case.value);
113         _ = try out.finish();
114     }
115 }