master
  1const builtin = @import("builtin");
  2const std = @import("std");
  3const testing = std.testing;
  4
  5/// This is an "advanced" function. It allows one to use a fixed amount of memory to store a
  6/// ULEB128. This defeats the entire purpose of using this data encoding; it will no longer use
  7/// fewer bytes to store smaller numbers. The advantage of using a fixed width is that it makes
  8/// fields have a predictable size and so depending on the use case this tradeoff can be worthwhile.
  9/// An example use case of this is in emitting DWARF info where one wants to make a ULEB128 field
 10/// "relocatable", meaning that it becomes possible to later go back and patch the number to be a
 11/// different value without shifting all the following code.
 12pub fn writeUnsignedFixed(comptime l: usize, ptr: *[l]u8, int: std.meta.Int(.unsigned, l * 7)) void {
 13    writeUnsignedExtended(ptr, int);
 14}
 15
 16/// Same as `writeUnsignedFixed` but with a runtime-known length.
 17/// Asserts `slice.len > 0`.
 18pub fn writeUnsignedExtended(slice: []u8, arg: anytype) void {
 19    const Arg = @TypeOf(arg);
 20    const Int = switch (Arg) {
 21        comptime_int => std.math.IntFittingRange(arg, arg),
 22        else => Arg,
 23    };
 24    const Value = if (@typeInfo(Int).int.bits < 8) u8 else Int;
 25    var value: Value = arg;
 26
 27    for (slice[0 .. slice.len - 1]) |*byte| {
 28        byte.* = @truncate(0x80 | value);
 29        value >>= 7;
 30    }
 31    slice[slice.len - 1] = @as(u7, @intCast(value));
 32}
 33
 34test writeUnsignedFixed {
 35    {
 36        var buf: [4]u8 = undefined;
 37        writeUnsignedFixed(4, &buf, 0);
 38        var reader: std.Io.Reader = .fixed(&buf);
 39        try testing.expectEqual(0, try reader.takeLeb128(u64));
 40    }
 41    {
 42        var buf: [4]u8 = undefined;
 43        writeUnsignedFixed(4, &buf, 1);
 44        var reader: std.Io.Reader = .fixed(&buf);
 45        try testing.expectEqual(1, try reader.takeLeb128(u64));
 46    }
 47    {
 48        var buf: [4]u8 = undefined;
 49        writeUnsignedFixed(4, &buf, 1000);
 50        var reader: std.Io.Reader = .fixed(&buf);
 51        try testing.expectEqual(1000, try reader.takeLeb128(u64));
 52    }
 53    {
 54        var buf: [4]u8 = undefined;
 55        writeUnsignedFixed(4, &buf, 10000000);
 56        var reader: std.Io.Reader = .fixed(&buf);
 57        try testing.expectEqual(10000000, try reader.takeLeb128(u64));
 58    }
 59}
 60
 61/// This is an "advanced" function. It allows one to use a fixed amount of memory to store an
 62/// ILEB128. This defeats the entire purpose of using this data encoding; it will no longer use
 63/// fewer bytes to store smaller numbers. The advantage of using a fixed width is that it makes
 64/// fields have a predictable size and so depending on the use case this tradeoff can be worthwhile.
 65/// An example use case of this is in emitting DWARF info where one wants to make a ILEB128 field
 66/// "relocatable", meaning that it becomes possible to later go back and patch the number to be a
 67/// different value without shifting all the following code.
 68pub fn writeSignedFixed(comptime l: usize, ptr: *[l]u8, int: std.meta.Int(.signed, l * 7)) void {
 69    const T = @TypeOf(int);
 70    const U = if (@typeInfo(T).int.bits < 8) u8 else T;
 71    var value: U = @intCast(int);
 72
 73    comptime var i = 0;
 74    inline while (i < (l - 1)) : (i += 1) {
 75        const byte: u8 = @bitCast(@as(i8, @truncate(value)) | -0b1000_0000);
 76        value >>= 7;
 77        ptr[i] = byte;
 78    }
 79    ptr[i] = @as(u7, @bitCast(@as(i7, @truncate(value))));
 80}
 81
 82test writeSignedFixed {
 83    {
 84        var buf: [4]u8 = undefined;
 85        writeSignedFixed(4, &buf, 0);
 86        var reader: std.Io.Reader = .fixed(&buf);
 87        try testing.expectEqual(0, try reader.takeLeb128(i64));
 88    }
 89    {
 90        var buf: [4]u8 = undefined;
 91        writeSignedFixed(4, &buf, 1);
 92        var reader: std.Io.Reader = .fixed(&buf);
 93        try testing.expectEqual(1, try reader.takeLeb128(i64));
 94    }
 95    {
 96        var buf: [4]u8 = undefined;
 97        writeSignedFixed(4, &buf, -1);
 98        var reader: std.Io.Reader = .fixed(&buf);
 99        try testing.expectEqual(-1, try reader.takeLeb128(i64));
100    }
101    {
102        var buf: [4]u8 = undefined;
103        writeSignedFixed(4, &buf, 1000);
104        var reader: std.Io.Reader = .fixed(&buf);
105        try testing.expectEqual(1000, try reader.takeLeb128(i64));
106    }
107    {
108        var buf: [4]u8 = undefined;
109        writeSignedFixed(4, &buf, -1000);
110        var reader: std.Io.Reader = .fixed(&buf);
111        try testing.expectEqual(-1000, try reader.takeLeb128(i64));
112    }
113    {
114        var buf: [4]u8 = undefined;
115        writeSignedFixed(4, &buf, -10000000);
116        var reader: std.Io.Reader = .fixed(&buf);
117        try testing.expectEqual(-10000000, try reader.takeLeb128(i64));
118    }
119    {
120        var buf: [4]u8 = undefined;
121        writeSignedFixed(4, &buf, 10000000);
122        var reader: std.Io.Reader = .fixed(&buf);
123        try testing.expectEqual(10000000, try reader.takeLeb128(i64));
124    }
125}