| author | |
| committer | |
| log | 48d60834fd61404ea009f7f970775f9c59de1240 |
| tree | bea3b1427db82fc781e216933fe44dc5b20fdb3f |
| parent | d9c36cb2506f1b8cb30c7e9e108c6005eea7cf66 |
| signature |
* Move leb128 out of debug and remove trivial *mem functions as discussed in #5588
* Turns out one of the *Mem functions was used by MachO. Replaced with trivial use of FixedBufferStream.10 files changed, 391 insertions(+), 461 deletions(-)
lib/std/debug.zig-7| ... | @@ -22,8 +22,6 @@ const maxInt = std.math.maxInt; | ... | @@ -22,8 +22,6 @@ const maxInt = std.math.maxInt; |
| 22 | const File = std.fs.File; | 22 | const File = std.fs.File; |
| 23 | const windows = std.os.windows; | 23 | const windows = std.os.windows; |
| 24 | 24 | ||
| 25 | pub const leb = @import("debug/leb128.zig"); | ||
| 26 | |||
| 27 | pub const runtime_safety = switch (builtin.mode) { | 25 | pub const runtime_safety = switch (builtin.mode) { |
| 28 | .Debug, .ReleaseSafe => true, | 26 | .Debug, .ReleaseSafe => true, |
| 29 | .ReleaseFast, .ReleaseSmall => false, | 27 | .ReleaseFast, .ReleaseSmall => false, |
| ... | @@ -1843,8 +1841,3 @@ pub fn dumpStackPointerAddr(prefix: []const u8) void { | ... | @@ -1843,8 +1841,3 @@ pub fn dumpStackPointerAddr(prefix: []const u8) void { |
| 1843 | ); | 1841 | ); |
| 1844 | std.debug.warn("{} sp = 0x{x}\n", .{ prefix, sp }); | 1842 | std.debug.warn("{} sp = 0x{x}\n", .{ prefix, sp }); |
| 1845 | } | 1843 | } |
| 1846 | |||
| 1847 | // Reference everything so it gets tested. | ||
| 1848 | test "" { | ||
| 1849 | _ = leb; | ||
| 1850 | } |
lib/std/debug/leb128.zig deleted-441| ... | @@ -1,441 +0,0 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2020 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const std = @import("std"); | ||
| 7 | const testing = std.testing; | ||
| 8 | |||
| 9 | /// Read a single unsigned LEB128 value from the given reader as type T, | ||
| 10 | /// or error.Overflow if the value cannot fit. | ||
| 11 | pub fn readULEB128(comptime T: type, reader: anytype) !T { | ||
| 12 | const U = if (@typeInfo(T).Int.bits < 8) u8 else T; | ||
| 13 | const ShiftT = std.math.Log2Int(U); | ||
| 14 | |||
| 15 | const max_group = (@typeInfo(U).Int.bits + 6) / 7; | ||
| 16 | |||
| 17 | var value = @as(U, 0); | ||
| 18 | var group = @as(ShiftT, 0); | ||
| 19 | |||
| 20 | while (group < max_group) : (group += 1) { | ||
| 21 | const byte = try reader.readByte(); | ||
| 22 | var temp = @as(U, byte & 0x7f); | ||
| 23 | |||
| 24 | if (@shlWithOverflow(U, temp, group * 7, &temp)) return error.Overflow; | ||
| 25 | |||
| 26 | value |= temp; | ||
| 27 | if (byte & 0x80 == 0) break; | ||
| 28 | } else { | ||
| 29 | return error.Overflow; | ||
| 30 | } | ||
| 31 | |||
| 32 | // only applies in the case that we extended to u8 | ||
| 33 | if (U != T) { | ||
| 34 | if (value > std.math.maxInt(T)) return error.Overflow; | ||
| 35 | } | ||
| 36 | |||
| 37 | return @truncate(T, value); | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Write a single unsigned integer as unsigned LEB128 to the given writer. | ||
| 41 | pub fn writeULEB128(writer: anytype, uint_value: anytype) !void { | ||
| 42 | const T = @TypeOf(uint_value); | ||
| 43 | const U = if (@typeInfo(T).Int.bits < 8) u8 else T; | ||
| 44 | var value = @intCast(U, uint_value); | ||
| 45 | |||
| 46 | while (true) { | ||
| 47 | const byte = @truncate(u8, value & 0x7f); | ||
| 48 | value >>= 7; | ||
| 49 | if (value == 0) { | ||
| 50 | try writer.writeByte(byte); | ||
| 51 | break; | ||
| 52 | } else { | ||
| 53 | try writer.writeByte(byte | 0x80); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | /// Read a single unsigned integer from the given memory as type T. | ||
| 59 | /// The provided slice reference will be updated to point to the byte after the last byte read. | ||
| 60 | pub fn readULEB128Mem(comptime T: type, ptr: *[]const u8) !T { | ||
| 61 | var buf = std.io.fixedBufferStream(ptr.*); | ||
| 62 | const value = try readULEB128(T, buf.reader()); | ||
| 63 | ptr.*.ptr += buf.pos; | ||
| 64 | return value; | ||
| 65 | } | ||
| 66 | |||
| 67 | /// Write a single unsigned LEB128 integer to the given memory as unsigned LEB128, | ||
| 68 | /// returning the number of bytes written. | ||
| 69 | pub fn writeULEB128Mem(ptr: []u8, uint_value: anytype) !usize { | ||
| 70 | const T = @TypeOf(uint_value); | ||
| 71 | const max_group = (@typeInfo(T).Int.bits + 6) / 7; | ||
| 72 | var buf = std.io.fixedBufferStream(ptr); | ||
| 73 | try writeULEB128(buf.writer(), uint_value); | ||
| 74 | return buf.pos; | ||
| 75 | } | ||
| 76 | |||
| 77 | /// Read a single signed LEB128 value from the given reader as type T, | ||
| 78 | /// or error.Overflow if the value cannot fit. | ||
| 79 | pub fn readILEB128(comptime T: type, reader: anytype) !T { | ||
| 80 | const S = if (@typeInfo(T).Int.bits < 8) i8 else T; | ||
| 81 | const U = std.meta.Int(.unsigned, @typeInfo(S).Int.bits); | ||
| 82 | const ShiftU = std.math.Log2Int(U); | ||
| 83 | |||
| 84 | const max_group = (@typeInfo(U).Int.bits + 6) / 7; | ||
| 85 | |||
| 86 | var value = @as(U, 0); | ||
| 87 | var group = @as(ShiftU, 0); | ||
| 88 | |||
| 89 | while (group < max_group) : (group += 1) { | ||
| 90 | const byte = try reader.readByte(); | ||
| 91 | var temp = @as(U, byte & 0x7f); | ||
| 92 | |||
| 93 | const shift = group * 7; | ||
| 94 | if (@shlWithOverflow(U, temp, shift, &temp)) { | ||
| 95 | // Overflow is ok so long as the sign bit is set and this is the last byte | ||
| 96 | if (byte & 0x80 != 0) return error.Overflow; | ||
| 97 | if (@bitCast(S, temp) >= 0) return error.Overflow; | ||
| 98 | |||
| 99 | // and all the overflowed bits are 1 | ||
| 100 | const remaining_shift = @intCast(u3, @typeInfo(U).Int.bits - @as(u16, shift)); | ||
| 101 | const remaining_bits = @bitCast(i8, byte | 0x80) >> remaining_shift; | ||
| 102 | if (remaining_bits != -1) return error.Overflow; | ||
| 103 | } | ||
| 104 | |||
| 105 | value |= temp; | ||
| 106 | if (byte & 0x80 == 0) { | ||
| 107 | const needs_sign_ext = group + 1 < max_group; | ||
| 108 | if (byte & 0x40 != 0 and needs_sign_ext) { | ||
| 109 | const ones = @as(S, -1); | ||
| 110 | value |= @bitCast(U, ones) << (shift + 7); | ||
| 111 | } | ||
| 112 | break; | ||
| 113 | } | ||
| 114 | } else { | ||
| 115 | return error.Overflow; | ||
| 116 | } | ||
| 117 | |||
| 118 | const result = @bitCast(S, value); | ||
| 119 | // Only applies if we extended to i8 | ||
| 120 | if (S != T) { | ||
| 121 | if (result > std.math.maxInt(T) or result < std.math.minInt(T)) return error.Overflow; | ||
| 122 | } | ||
| 123 | |||
| 124 | return @truncate(T, result); | ||
| 125 | } | ||
| 126 | |||
| 127 | /// Write a single signed integer as signed LEB128 to the given writer. | ||
| 128 | pub fn writeILEB128(writer: anytype, int_value: anytype) !void { | ||
| 129 | const T = @TypeOf(int_value); | ||
| 130 | const S = if (@typeInfo(T).Int.bits < 8) i8 else T; | ||
| 131 | const U = std.meta.Int(.unsigned, @typeInfo(S).Int.bits); | ||
| 132 | |||
| 133 | var value = @intCast(S, int_value); | ||
| 134 | |||
| 135 | while (true) { | ||
| 136 | const uvalue = @bitCast(U, value); | ||
| 137 | const byte = @truncate(u8, uvalue); | ||
| 138 | value >>= 6; | ||
| 139 | if (value == -1 or value == 0) { | ||
| 140 | try writer.writeByte(byte & 0x7F); | ||
| 141 | break; | ||
| 142 | } else { | ||
| 143 | value >>= 1; | ||
| 144 | try writer.writeByte(byte | 0x80); | ||
| 145 | } | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | /// Read a single singed LEB128 integer from the given memory as type T. | ||
| 150 | /// The provided slice reference will be updated to point to the byte after the last byte read. | ||
| 151 | pub fn readILEB128Mem(comptime T: type, ptr: *[]const u8) !T { | ||
| 152 | var buf = std.io.fixedBufferStream(ptr.*); | ||
| 153 | const value = try readILEB128(T, buf.reader()); | ||
| 154 | ptr.*.ptr += buf.pos; | ||
| 155 | return value; | ||
| 156 | } | ||
| 157 | |||
| 158 | /// Write a single signed LEB128 integer to the given memory as unsigned LEB128, | ||
| 159 | /// returning the number of bytes written. | ||
| 160 | pub fn writeILEB128Mem(ptr: []u8, int_value: anytype) !usize { | ||
| 161 | const T = @TypeOf(int_value); | ||
| 162 | var buf = std.io.fixedBufferStream(ptr); | ||
| 163 | try writeILEB128(buf.writer(), int_value); | ||
| 164 | return buf.pos; | ||
| 165 | } | ||
| 166 | |||
| 167 | /// This is an "advanced" function. It allows one to use a fixed amount of memory to store a | ||
| 168 | /// ULEB128. This defeats the entire purpose of using this data encoding; it will no longer use | ||
| 169 | /// fewer bytes to store smaller numbers. The advantage of using a fixed width is that it makes | ||
| 170 | /// fields have a predictable size and so depending on the use case this tradeoff can be worthwhile. | ||
| 171 | /// An example use case of this is in emitting DWARF info where one wants to make a ULEB128 field | ||
| 172 | /// "relocatable", meaning that it becomes possible to later go back and patch the number to be a | ||
| 173 | /// different value without shifting all the following code. | ||
| 174 | pub fn writeUnsignedFixed(comptime l: usize, ptr: *[l]u8, int: std.meta.Int(.unsigned, l * 7)) void { | ||
| 175 | const T = @TypeOf(int); | ||
| 176 | const U = if (@typeInfo(T).Int.bits < 8) u8 else T; | ||
| 177 | var value = @intCast(U, int); | ||
| 178 | |||
| 179 | comptime var i = 0; | ||
| 180 | inline while (i < (l - 1)) : (i += 1) { | ||
| 181 | const byte = @truncate(u8, value) | 0b1000_0000; | ||
| 182 | value >>= 7; | ||
| 183 | ptr[i] = byte; | ||
| 184 | } | ||
| 185 | ptr[i] = @truncate(u8, value); | ||
| 186 | } | ||
| 187 | |||
| 188 | test "writeUnsignedFixed" { | ||
| 189 | { | ||
| 190 | var buf: [4]u8 = undefined; | ||
| 191 | writeUnsignedFixed(4, &buf, 0); | ||
| 192 | testing.expect((try test_read_uleb128(u64, &buf)) == 0); | ||
| 193 | } | ||
| 194 | { | ||
| 195 | var buf: [4]u8 = undefined; | ||
| 196 | writeUnsignedFixed(4, &buf, 1); | ||
| 197 | testing.expect((try test_read_uleb128(u64, &buf)) == 1); | ||
| 198 | } | ||
| 199 | { | ||
| 200 | var buf: [4]u8 = undefined; | ||
| 201 | writeUnsignedFixed(4, &buf, 1000); | ||
| 202 | testing.expect((try test_read_uleb128(u64, &buf)) == 1000); | ||
| 203 | } | ||
| 204 | { | ||
| 205 | var buf: [4]u8 = undefined; | ||
| 206 | writeUnsignedFixed(4, &buf, 10000000); | ||
| 207 | testing.expect((try test_read_uleb128(u64, &buf)) == 10000000); | ||
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 211 | // tests | ||
| 212 | fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T { | ||
| 213 | var reader = std.io.fixedBufferStream(encoded); | ||
| 214 | return try readILEB128(T, reader.reader()); | ||
| 215 | } | ||
| 216 | |||
| 217 | fn test_read_stream_uleb128(comptime T: type, encoded: []const u8) !T { | ||
| 218 | var reader = std.io.fixedBufferStream(encoded); | ||
| 219 | return try readULEB128(T, reader.reader()); | ||
| 220 | } | ||
| 221 | |||
| 222 | fn test_read_ileb128(comptime T: type, encoded: []const u8) !T { | ||
| 223 | var reader = std.io.fixedBufferStream(encoded); | ||
| 224 | const v1 = try readILEB128(T, reader.reader()); | ||
| 225 | var in_ptr = encoded; | ||
| 226 | const v2 = try readILEB128Mem(T, &in_ptr); | ||
| 227 | testing.expectEqual(v1, v2); | ||
| 228 | return v1; | ||
| 229 | } | ||
| 230 | |||
| 231 | fn test_read_uleb128(comptime T: type, encoded: []const u8) !T { | ||
| 232 | var reader = std.io.fixedBufferStream(encoded); | ||
| 233 | const v1 = try readULEB128(T, reader.reader()); | ||
| 234 | var in_ptr = encoded; | ||
| 235 | const v2 = try readULEB128Mem(T, &in_ptr); | ||
| 236 | testing.expectEqual(v1, v2); | ||
| 237 | return v1; | ||
| 238 | } | ||
| 239 | |||
| 240 | fn test_read_ileb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) !void { | ||
| 241 | var reader = std.io.fixedBufferStream(encoded); | ||
| 242 | var in_ptr = encoded; | ||
| 243 | var i: usize = 0; | ||
| 244 | while (i < N) : (i += 1) { | ||
| 245 | const v1 = try readILEB128(T, reader.reader()); | ||
| 246 | const v2 = try readILEB128Mem(T, &in_ptr); | ||
| 247 | testing.expectEqual(v1, v2); | ||
| 248 | } | ||
| 249 | } | ||
| 250 | |||
| 251 | fn test_read_uleb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) !void { | ||
| 252 | var reader = std.io.fixedBufferStream(encoded); | ||
| 253 | var in_ptr = encoded; | ||
| 254 | var i: usize = 0; | ||
| 255 | while (i < N) : (i += 1) { | ||
| 256 | const v1 = try readULEB128(T, reader.reader()); | ||
| 257 | const v2 = try readULEB128Mem(T, &in_ptr); | ||
| 258 | testing.expectEqual(v1, v2); | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | test "deserialize signed LEB128" { | ||
| 263 | // Truncated | ||
| 264 | testing.expectError(error.EndOfStream, test_read_stream_ileb128(i64, "\x80")); | ||
| 265 | |||
| 266 | // Overflow | ||
| 267 | testing.expectError(error.Overflow, test_read_ileb128(i8, "\x80\x80\x40")); | ||
| 268 | testing.expectError(error.Overflow, test_read_ileb128(i16, "\x80\x80\x80\x40")); | ||
| 269 | testing.expectError(error.Overflow, test_read_ileb128(i32, "\x80\x80\x80\x80\x40")); | ||
| 270 | testing.expectError(error.Overflow, test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x40")); | ||
| 271 | testing.expectError(error.Overflow, test_read_ileb128(i8, "\xff\x7e")); | ||
| 272 | |||
| 273 | // Decode SLEB128 | ||
| 274 | testing.expect((try test_read_ileb128(i64, "\x00")) == 0); | ||
| 275 | testing.expect((try test_read_ileb128(i64, "\x01")) == 1); | ||
| 276 | testing.expect((try test_read_ileb128(i64, "\x3f")) == 63); | ||
| 277 | testing.expect((try test_read_ileb128(i64, "\x40")) == -64); | ||
| 278 | testing.expect((try test_read_ileb128(i64, "\x41")) == -63); | ||
| 279 | testing.expect((try test_read_ileb128(i64, "\x7f")) == -1); | ||
| 280 | testing.expect((try test_read_ileb128(i64, "\x80\x01")) == 128); | ||
| 281 | testing.expect((try test_read_ileb128(i64, "\x81\x01")) == 129); | ||
| 282 | testing.expect((try test_read_ileb128(i64, "\xff\x7e")) == -129); | ||
| 283 | testing.expect((try test_read_ileb128(i64, "\x80\x7f")) == -128); | ||
| 284 | testing.expect((try test_read_ileb128(i64, "\x81\x7f")) == -127); | ||
| 285 | testing.expect((try test_read_ileb128(i64, "\xc0\x00")) == 64); | ||
| 286 | testing.expect((try test_read_ileb128(i64, "\xc7\x9f\x7f")) == -12345); | ||
| 287 | testing.expect((try test_read_ileb128(i8, "\xff\x7f")) == -1); | ||
| 288 | testing.expect((try test_read_ileb128(i16, "\xff\xff\x7f")) == -1); | ||
| 289 | testing.expect((try test_read_ileb128(i32, "\xff\xff\xff\xff\x7f")) == -1); | ||
| 290 | testing.expect((try test_read_ileb128(i32, "\x80\x80\x80\x80\x08")) == -0x80000000); | ||
| 291 | testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01")) == @bitCast(i64, @intCast(u64, 0x8000000000000000))); | ||
| 292 | testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x40")) == -0x4000000000000000); | ||
| 293 | testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x7f")) == -0x8000000000000000); | ||
| 294 | |||
| 295 | // Decode unnormalized SLEB128 with extra padding bytes. | ||
| 296 | testing.expect((try test_read_ileb128(i64, "\x80\x00")) == 0); | ||
| 297 | testing.expect((try test_read_ileb128(i64, "\x80\x80\x00")) == 0); | ||
| 298 | testing.expect((try test_read_ileb128(i64, "\xff\x00")) == 0x7f); | ||
| 299 | testing.expect((try test_read_ileb128(i64, "\xff\x80\x00")) == 0x7f); | ||
| 300 | testing.expect((try test_read_ileb128(i64, "\x80\x81\x00")) == 0x80); | ||
| 301 | testing.expect((try test_read_ileb128(i64, "\x80\x81\x80\x00")) == 0x80); | ||
| 302 | |||
| 303 | // Decode sequence of SLEB128 values | ||
| 304 | try test_read_ileb128_seq(i64, 4, "\x81\x01\x3f\x80\x7f\x80\x80\x80\x00"); | ||
| 305 | } | ||
| 306 | |||
| 307 | test "deserialize unsigned LEB128" { | ||
| 308 | // Truncated | ||
| 309 | testing.expectError(error.EndOfStream, test_read_stream_uleb128(u64, "\x80")); | ||
| 310 | |||
| 311 | // Overflow | ||
| 312 | testing.expectError(error.Overflow, test_read_uleb128(u8, "\x80\x02")); | ||
| 313 | testing.expectError(error.Overflow, test_read_uleb128(u8, "\x80\x80\x40")); | ||
| 314 | testing.expectError(error.Overflow, test_read_uleb128(u16, "\x80\x80\x84")); | ||
| 315 | testing.expectError(error.Overflow, test_read_uleb128(u16, "\x80\x80\x80\x40")); | ||
| 316 | testing.expectError(error.Overflow, test_read_uleb128(u32, "\x80\x80\x80\x80\x90")); | ||
| 317 | testing.expectError(error.Overflow, test_read_uleb128(u32, "\x80\x80\x80\x80\x40")); | ||
| 318 | testing.expectError(error.Overflow, test_read_uleb128(u64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x40")); | ||
| 319 | |||
| 320 | // Decode ULEB128 | ||
| 321 | testing.expect((try test_read_uleb128(u64, "\x00")) == 0); | ||
| 322 | testing.expect((try test_read_uleb128(u64, "\x01")) == 1); | ||
| 323 | testing.expect((try test_read_uleb128(u64, "\x3f")) == 63); | ||
| 324 | testing.expect((try test_read_uleb128(u64, "\x40")) == 64); | ||
| 325 | testing.expect((try test_read_uleb128(u64, "\x7f")) == 0x7f); | ||
| 326 | testing.expect((try test_read_uleb128(u64, "\x80\x01")) == 0x80); | ||
| 327 | testing.expect((try test_read_uleb128(u64, "\x81\x01")) == 0x81); | ||
| 328 | testing.expect((try test_read_uleb128(u64, "\x90\x01")) == 0x90); | ||
| 329 | testing.expect((try test_read_uleb128(u64, "\xff\x01")) == 0xff); | ||
| 330 | testing.expect((try test_read_uleb128(u64, "\x80\x02")) == 0x100); | ||
| 331 | testing.expect((try test_read_uleb128(u64, "\x81\x02")) == 0x101); | ||
| 332 | testing.expect((try test_read_uleb128(u64, "\x80\xc1\x80\x80\x10")) == 4294975616); | ||
| 333 | testing.expect((try test_read_uleb128(u64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01")) == 0x8000000000000000); | ||
| 334 | |||
| 335 | // Decode ULEB128 with extra padding bytes | ||
| 336 | testing.expect((try test_read_uleb128(u64, "\x80\x00")) == 0); | ||
| 337 | testing.expect((try test_read_uleb128(u64, "\x80\x80\x00")) == 0); | ||
| 338 | testing.expect((try test_read_uleb128(u64, "\xff\x00")) == 0x7f); | ||
| 339 | testing.expect((try test_read_uleb128(u64, "\xff\x80\x00")) == 0x7f); | ||
| 340 | testing.expect((try test_read_uleb128(u64, "\x80\x81\x00")) == 0x80); | ||
| 341 | testing.expect((try test_read_uleb128(u64, "\x80\x81\x80\x00")) == 0x80); | ||
| 342 | |||
| 343 | // Decode sequence of ULEB128 values | ||
| 344 | try test_read_uleb128_seq(u64, 4, "\x81\x01\x3f\x80\x7f\x80\x80\x80\x00"); | ||
| 345 | } | ||
| 346 | |||
| 347 | fn test_write_leb128(value: anytype) !void { | ||
| 348 | const T = @TypeOf(value); | ||
| 349 | const t_signed = @typeInfo(T).Int.is_signed; | ||
| 350 | const signedness = if (t_signed) .signed else .unsigned; | ||
| 351 | |||
| 352 | const writeStream = if (t_signed) writeILEB128 else writeULEB128; | ||
| 353 | const writeMem = if (t_signed) writeILEB128Mem else writeULEB128Mem; | ||
| 354 | const readStream = if (t_signed) readILEB128 else readULEB128; | ||
| 355 | const readMem = if (t_signed) readILEB128Mem else readULEB128Mem; | ||
| 356 | |||
| 357 | // decode to a larger bit size too, to ensure sign extension | ||
| 358 | // is working as expected | ||
| 359 | const larger_type_bits = ((@typeInfo(T).Int.bits + 8) / 8) * 8; | ||
| 360 | const B = std.meta.Int(signedness, larger_type_bits); | ||
| 361 | |||
| 362 | const bytes_needed = bn: { | ||
| 363 | const S = std.meta.Int(signedness, @sizeOf(T) * 8); | ||
| 364 | if (@typeInfo(T).Int.bits <= 7) break :bn @as(u16, 1); | ||
| 365 | |||
| 366 | const unused_bits = if (value < 0) @clz(T, ~value) else @clz(T, value); | ||
| 367 | const used_bits: u16 = (@typeInfo(T).Int.bits - unused_bits) + @boolToInt(t_signed); | ||
| 368 | if (used_bits <= 7) break :bn @as(u16, 1); | ||
| 369 | break :bn ((used_bits + 6) / 7); | ||
| 370 | }; | ||
| 371 | |||
| 372 | const max_groups = if (@typeInfo(T).Int.bits == 0) 1 else (@typeInfo(T).Int.bits + 6) / 7; | ||
| 373 | |||
| 374 | var buf: [max_groups]u8 = undefined; | ||
| 375 | var fbs = std.io.fixedBufferStream(&buf); | ||
| 376 | |||
| 377 | // stream write | ||
| 378 | try writeStream(fbs.writer(), value); | ||
| 379 | const w1_pos = fbs.pos; | ||
| 380 | testing.expect(w1_pos == bytes_needed); | ||
| 381 | |||
| 382 | // stream read | ||
| 383 | fbs.pos = 0; | ||
| 384 | const sr = try readStream(T, fbs.reader()); | ||
| 385 | testing.expect(fbs.pos == w1_pos); | ||
| 386 | testing.expect(sr == value); | ||
| 387 | |||
| 388 | // bigger type stream read | ||
| 389 | fbs.pos = 0; | ||
| 390 | const bsr = try readStream(B, fbs.reader()); | ||
| 391 | testing.expect(fbs.pos == w1_pos); | ||
| 392 | testing.expect(bsr == value); | ||
| 393 | |||
| 394 | // mem write | ||
| 395 | const w2_pos = try writeMem(&buf, value); | ||
| 396 | testing.expect(w2_pos == w1_pos); | ||
| 397 | |||
| 398 | // mem read | ||
| 399 | var buf_ref: []u8 = buf[0..]; | ||
| 400 | const mr = try readMem(T, &buf_ref); | ||
| 401 | testing.expect(@ptrToInt(buf_ref.ptr) - @ptrToInt(&buf) == w2_pos); | ||
| 402 | testing.expect(mr == value); | ||
| 403 | |||
| 404 | // bigger type mem read | ||
| 405 | buf_ref = buf[0..]; | ||
| 406 | const bmr = try readMem(T, &buf_ref); | ||
| 407 | testing.expect(@ptrToInt(buf_ref.ptr) - @ptrToInt(&buf) == w2_pos); | ||
| 408 | testing.expect(bmr == value); | ||
| 409 | } | ||
| 410 | |||
| 411 | test "serialize unsigned LEB128" { | ||
| 412 | const max_bits = 18; | ||
| 413 | |||
| 414 | comptime var t = 0; | ||
| 415 | inline while (t <= max_bits) : (t += 1) { | ||
| 416 | const T = std.meta.Int(.unsigned, t); | ||
| 417 | const min = std.math.minInt(T); | ||
| 418 | const max = std.math.maxInt(T); | ||
| 419 | var i = @as(std.meta.Int(.unsigned, @typeInfo(T).Int.bits + 1), min); | ||
| 420 | |||
| 421 | while (i <= max) : (i += 1) try test_write_leb128(@intCast(T, i)); | ||
| 422 | } | ||
| 423 | } | ||
| 424 | |||
| 425 | test "serialize signed LEB128" { | ||
| 426 | // explicitly test i0 because starting `t` at 0 | ||
| 427 | // will break the while loop | ||
| 428 | try test_write_leb128(@as(i0, 0)); | ||
| 429 | |||
| 430 | const max_bits = 18; | ||
| 431 | |||
| 432 | comptime var t = 1; | ||
| 433 | inline while (t <= max_bits) : (t += 1) { | ||
| 434 | const T = std.meta.Int(.signed, t); | ||
| 435 | const min = std.math.minInt(T); | ||
| 436 | const max = std.math.maxInt(T); | ||
| 437 | var i = @as(std.meta.Int(.signed, @typeInfo(T).Int.bits + 1), min); | ||
| 438 | |||
| 439 | while (i <= max) : (i += 1) try test_write_leb128(@intCast(T, i)); | ||
| 440 | } | ||
| 441 | } | ||
lib/std/dwarf.zig+1-1| ... | @@ -10,7 +10,7 @@ const fs = std.fs; | ... | @@ -10,7 +10,7 @@ const fs = std.fs; |
| 10 | const io = std.io; | 10 | const io = std.io; |
| 11 | const mem = std.mem; | 11 | const mem = std.mem; |
| 12 | const math = std.math; | 12 | const math = std.math; |
| 13 | const leb = @import("debug/leb128.zig"); | 13 | const leb = @import("leb128.zig"); |
| 14 | 14 | ||
| 15 | const ArrayList = std.ArrayList; | 15 | const ArrayList = std.ArrayList; |
| 16 | 16 |
lib/std/leb128.zig created+374| ... | @@ -0,0 +1,374 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2020 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const std = @import("std"); | ||
| 7 | const testing = std.testing; | ||
| 8 | |||
| 9 | /// Read a single unsigned LEB128 value from the given reader as type T, | ||
| 10 | /// or error.Overflow if the value cannot fit. | ||
| 11 | pub fn readULEB128(comptime T: type, reader: anytype) !T { | ||
| 12 | const U = if (@typeInfo(T).Int.bits < 8) u8 else T; | ||
| 13 | const ShiftT = std.math.Log2Int(U); | ||
| 14 | |||
| 15 | const max_group = (@typeInfo(U).Int.bits + 6) / 7; | ||
| 16 | |||
| 17 | var value = @as(U, 0); | ||
| 18 | var group = @as(ShiftT, 0); | ||
| 19 | |||
| 20 | while (group < max_group) : (group += 1) { | ||
| 21 | const byte = try reader.readByte(); | ||
| 22 | var temp = @as(U, byte & 0x7f); | ||
| 23 | |||
| 24 | if (@shlWithOverflow(U, temp, group * 7, &temp)) return error.Overflow; | ||
| 25 | |||
| 26 | value |= temp; | ||
| 27 | if (byte & 0x80 == 0) break; | ||
| 28 | } else { | ||
| 29 | return error.Overflow; | ||
| 30 | } | ||
| 31 | |||
| 32 | // only applies in the case that we extended to u8 | ||
| 33 | if (U != T) { | ||
| 34 | if (value > std.math.maxInt(T)) return error.Overflow; | ||
| 35 | } | ||
| 36 | |||
| 37 | return @truncate(T, value); | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Write a single unsigned integer as unsigned LEB128 to the given writer. | ||
| 41 | pub fn writeULEB128(writer: anytype, uint_value: anytype) !void { | ||
| 42 | const T = @TypeOf(uint_value); | ||
| 43 | const U = if (@typeInfo(T).Int.bits < 8) u8 else T; | ||
| 44 | var value = @intCast(U, uint_value); | ||
| 45 | |||
| 46 | while (true) { | ||
| 47 | const byte = @truncate(u8, value & 0x7f); | ||
| 48 | value >>= 7; | ||
| 49 | if (value == 0) { | ||
| 50 | try writer.writeByte(byte); | ||
| 51 | break; | ||
| 52 | } else { | ||
| 53 | try writer.writeByte(byte | 0x80); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | /// Read a single signed LEB128 value from the given reader as type T, | ||
| 59 | /// or error.Overflow if the value cannot fit. | ||
| 60 | pub fn readILEB128(comptime T: type, reader: anytype) !T { | ||
| 61 | const S = if (@typeInfo(T).Int.bits < 8) i8 else T; | ||
| 62 | const U = std.meta.Int(.unsigned, @typeInfo(S).Int.bits); | ||
| 63 | const ShiftU = std.math.Log2Int(U); | ||
| 64 | |||
| 65 | const max_group = (@typeInfo(U).Int.bits + 6) / 7; | ||
| 66 | |||
| 67 | var value = @as(U, 0); | ||
| 68 | var group = @as(ShiftU, 0); | ||
| 69 | |||
| 70 | while (group < max_group) : (group += 1) { | ||
| 71 | const byte = try reader.readByte(); | ||
| 72 | var temp = @as(U, byte & 0x7f); | ||
| 73 | |||
| 74 | const shift = group * 7; | ||
| 75 | if (@shlWithOverflow(U, temp, shift, &temp)) { | ||
| 76 | // Overflow is ok so long as the sign bit is set and this is the last byte | ||
| 77 | if (byte & 0x80 != 0) return error.Overflow; | ||
| 78 | if (@bitCast(S, temp) >= 0) return error.Overflow; | ||
| 79 | |||
| 80 | // and all the overflowed bits are 1 | ||
| 81 | const remaining_shift = @intCast(u3, @typeInfo(U).Int.bits - @as(u16, shift)); | ||
| 82 | const remaining_bits = @bitCast(i8, byte | 0x80) >> remaining_shift; | ||
| 83 | if (remaining_bits != -1) return error.Overflow; | ||
| 84 | } | ||
| 85 | |||
| 86 | value |= temp; | ||
| 87 | if (byte & 0x80 == 0) { | ||
| 88 | const needs_sign_ext = group + 1 < max_group; | ||
| 89 | if (byte & 0x40 != 0 and needs_sign_ext) { | ||
| 90 | const ones = @as(S, -1); | ||
| 91 | value |= @bitCast(U, ones) << (shift + 7); | ||
| 92 | } | ||
| 93 | break; | ||
| 94 | } | ||
| 95 | } else { | ||
| 96 | return error.Overflow; | ||
| 97 | } | ||
| 98 | |||
| 99 | const result = @bitCast(S, value); | ||
| 100 | // Only applies if we extended to i8 | ||
| 101 | if (S != T) { | ||
| 102 | if (result > std.math.maxInt(T) or result < std.math.minInt(T)) return error.Overflow; | ||
| 103 | } | ||
| 104 | |||
| 105 | return @truncate(T, result); | ||
| 106 | } | ||
| 107 | |||
| 108 | /// Write a single signed integer as signed LEB128 to the given writer. | ||
| 109 | pub fn writeILEB128(writer: anytype, int_value: anytype) !void { | ||
| 110 | const T = @TypeOf(int_value); | ||
| 111 | const S = if (@typeInfo(T).Int.bits < 8) i8 else T; | ||
| 112 | const U = std.meta.Int(.unsigned, @typeInfo(S).Int.bits); | ||
| 113 | |||
| 114 | var value = @intCast(S, int_value); | ||
| 115 | |||
| 116 | while (true) { | ||
| 117 | const uvalue = @bitCast(U, value); | ||
| 118 | const byte = @truncate(u8, uvalue); | ||
| 119 | value >>= 6; | ||
| 120 | if (value == -1 or value == 0) { | ||
| 121 | try writer.writeByte(byte & 0x7F); | ||
| 122 | break; | ||
| 123 | } else { | ||
| 124 | value >>= 1; | ||
| 125 | try writer.writeByte(byte | 0x80); | ||
| 126 | } | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | /// This is an "advanced" function. It allows one to use a fixed amount of memory to store a | ||
| 131 | /// ULEB128. This defeats the entire purpose of using this data encoding; it will no longer use | ||
| 132 | /// fewer bytes to store smaller numbers. The advantage of using a fixed width is that it makes | ||
| 133 | /// fields have a predictable size and so depending on the use case this tradeoff can be worthwhile. | ||
| 134 | /// An example use case of this is in emitting DWARF info where one wants to make a ULEB128 field | ||
| 135 | /// "relocatable", meaning that it becomes possible to later go back and patch the number to be a | ||
| 136 | /// different value without shifting all the following code. | ||
| 137 | pub fn writeUnsignedFixed(comptime l: usize, ptr: *[l]u8, int: std.meta.Int(.unsigned, l * 7)) void { | ||
| 138 | const T = @TypeOf(int); | ||
| 139 | const U = if (@typeInfo(T).Int.bits < 8) u8 else T; | ||
| 140 | var value = @intCast(U, int); | ||
| 141 | |||
| 142 | comptime var i = 0; | ||
| 143 | inline while (i < (l - 1)) : (i += 1) { | ||
| 144 | const byte = @truncate(u8, value) | 0b1000_0000; | ||
| 145 | value >>= 7; | ||
| 146 | ptr[i] = byte; | ||
| 147 | } | ||
| 148 | ptr[i] = @truncate(u8, value); | ||
| 149 | } | ||
| 150 | |||
| 151 | test "writeUnsignedFixed" { | ||
| 152 | { | ||
| 153 | var buf: [4]u8 = undefined; | ||
| 154 | writeUnsignedFixed(4, &buf, 0); | ||
| 155 | testing.expect((try test_read_uleb128(u64, &buf)) == 0); | ||
| 156 | } | ||
| 157 | { | ||
| 158 | var buf: [4]u8 = undefined; | ||
| 159 | writeUnsignedFixed(4, &buf, 1); | ||
| 160 | testing.expect((try test_read_uleb128(u64, &buf)) == 1); | ||
| 161 | } | ||
| 162 | { | ||
| 163 | var buf: [4]u8 = undefined; | ||
| 164 | writeUnsignedFixed(4, &buf, 1000); | ||
| 165 | testing.expect((try test_read_uleb128(u64, &buf)) == 1000); | ||
| 166 | } | ||
| 167 | { | ||
| 168 | var buf: [4]u8 = undefined; | ||
| 169 | writeUnsignedFixed(4, &buf, 10000000); | ||
| 170 | testing.expect((try test_read_uleb128(u64, &buf)) == 10000000); | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | // tests | ||
| 175 | fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T { | ||
| 176 | var reader = std.io.fixedBufferStream(encoded); | ||
| 177 | return try readILEB128(T, reader.reader()); | ||
| 178 | } | ||
| 179 | |||
| 180 | fn test_read_stream_uleb128(comptime T: type, encoded: []const u8) !T { | ||
| 181 | var reader = std.io.fixedBufferStream(encoded); | ||
| 182 | return try readULEB128(T, reader.reader()); | ||
| 183 | } | ||
| 184 | |||
| 185 | fn test_read_ileb128(comptime T: type, encoded: []const u8) !T { | ||
| 186 | var reader = std.io.fixedBufferStream(encoded); | ||
| 187 | const v1 = try readILEB128(T, reader.reader()); | ||
| 188 | return v1; | ||
| 189 | } | ||
| 190 | |||
| 191 | fn test_read_uleb128(comptime T: type, encoded: []const u8) !T { | ||
| 192 | var reader = std.io.fixedBufferStream(encoded); | ||
| 193 | const v1 = try readULEB128(T, reader.reader()); | ||
| 194 | return v1; | ||
| 195 | } | ||
| 196 | |||
| 197 | fn test_read_ileb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) !void { | ||
| 198 | var reader = std.io.fixedBufferStream(encoded); | ||
| 199 | var i: usize = 0; | ||
| 200 | while (i < N) : (i += 1) { | ||
| 201 | const v1 = try readILEB128(T, reader.reader()); | ||
| 202 | } | ||
| 203 | } | ||
| 204 | |||
| 205 | fn test_read_uleb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) !void { | ||
| 206 | var reader = std.io.fixedBufferStream(encoded); | ||
| 207 | var i: usize = 0; | ||
| 208 | while (i < N) : (i += 1) { | ||
| 209 | const v1 = try readULEB128(T, reader.reader()); | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | test "deserialize signed LEB128" { | ||
| 214 | // Truncated | ||
| 215 | testing.expectError(error.EndOfStream, test_read_stream_ileb128(i64, "\x80")); | ||
| 216 | |||
| 217 | // Overflow | ||
| 218 | testing.expectError(error.Overflow, test_read_ileb128(i8, "\x80\x80\x40")); | ||
| 219 | testing.expectError(error.Overflow, test_read_ileb128(i16, "\x80\x80\x80\x40")); | ||
| 220 | testing.expectError(error.Overflow, test_read_ileb128(i32, "\x80\x80\x80\x80\x40")); | ||
| 221 | testing.expectError(error.Overflow, test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x40")); | ||
| 222 | testing.expectError(error.Overflow, test_read_ileb128(i8, "\xff\x7e")); | ||
| 223 | |||
| 224 | // Decode SLEB128 | ||
| 225 | testing.expect((try test_read_ileb128(i64, "\x00")) == 0); | ||
| 226 | testing.expect((try test_read_ileb128(i64, "\x01")) == 1); | ||
| 227 | testing.expect((try test_read_ileb128(i64, "\x3f")) == 63); | ||
| 228 | testing.expect((try test_read_ileb128(i64, "\x40")) == -64); | ||
| 229 | testing.expect((try test_read_ileb128(i64, "\x41")) == -63); | ||
| 230 | testing.expect((try test_read_ileb128(i64, "\x7f")) == -1); | ||
| 231 | testing.expect((try test_read_ileb128(i64, "\x80\x01")) == 128); | ||
| 232 | testing.expect((try test_read_ileb128(i64, "\x81\x01")) == 129); | ||
| 233 | testing.expect((try test_read_ileb128(i64, "\xff\x7e")) == -129); | ||
| 234 | testing.expect((try test_read_ileb128(i64, "\x80\x7f")) == -128); | ||
| 235 | testing.expect((try test_read_ileb128(i64, "\x81\x7f")) == -127); | ||
| 236 | testing.expect((try test_read_ileb128(i64, "\xc0\x00")) == 64); | ||
| 237 | testing.expect((try test_read_ileb128(i64, "\xc7\x9f\x7f")) == -12345); | ||
| 238 | testing.expect((try test_read_ileb128(i8, "\xff\x7f")) == -1); | ||
| 239 | testing.expect((try test_read_ileb128(i16, "\xff\xff\x7f")) == -1); | ||
| 240 | testing.expect((try test_read_ileb128(i32, "\xff\xff\xff\xff\x7f")) == -1); | ||
| 241 | testing.expect((try test_read_ileb128(i32, "\x80\x80\x80\x80\x08")) == -0x80000000); | ||
| 242 | testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01")) == @bitCast(i64, @intCast(u64, 0x8000000000000000))); | ||
| 243 | testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x40")) == -0x4000000000000000); | ||
| 244 | testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x7f")) == -0x8000000000000000); | ||
| 245 | |||
| 246 | // Decode unnormalized SLEB128 with extra padding bytes. | ||
| 247 | testing.expect((try test_read_ileb128(i64, "\x80\x00")) == 0); | ||
| 248 | testing.expect((try test_read_ileb128(i64, "\x80\x80\x00")) == 0); | ||
| 249 | testing.expect((try test_read_ileb128(i64, "\xff\x00")) == 0x7f); | ||
| 250 | testing.expect((try test_read_ileb128(i64, "\xff\x80\x00")) == 0x7f); | ||
| 251 | testing.expect((try test_read_ileb128(i64, "\x80\x81\x00")) == 0x80); | ||
| 252 | testing.expect((try test_read_ileb128(i64, "\x80\x81\x80\x00")) == 0x80); | ||
| 253 | |||
| 254 | // Decode sequence of SLEB128 values | ||
| 255 | try test_read_ileb128_seq(i64, 4, "\x81\x01\x3f\x80\x7f\x80\x80\x80\x00"); | ||
| 256 | } | ||
| 257 | |||
| 258 | test "deserialize unsigned LEB128" { | ||
| 259 | // Truncated | ||
| 260 | testing.expectError(error.EndOfStream, test_read_stream_uleb128(u64, "\x80")); | ||
| 261 | |||
| 262 | // Overflow | ||
| 263 | testing.expectError(error.Overflow, test_read_uleb128(u8, "\x80\x02")); | ||
| 264 | testing.expectError(error.Overflow, test_read_uleb128(u8, "\x80\x80\x40")); | ||
| 265 | testing.expectError(error.Overflow, test_read_uleb128(u16, "\x80\x80\x84")); | ||
| 266 | testing.expectError(error.Overflow, test_read_uleb128(u16, "\x80\x80\x80\x40")); | ||
| 267 | testing.expectError(error.Overflow, test_read_uleb128(u32, "\x80\x80\x80\x80\x90")); | ||
| 268 | testing.expectError(error.Overflow, test_read_uleb128(u32, "\x80\x80\x80\x80\x40")); | ||
| 269 | testing.expectError(error.Overflow, test_read_uleb128(u64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x40")); | ||
| 270 | |||
| 271 | // Decode ULEB128 | ||
| 272 | testing.expect((try test_read_uleb128(u64, "\x00")) == 0); | ||
| 273 | testing.expect((try test_read_uleb128(u64, "\x01")) == 1); | ||
| 274 | testing.expect((try test_read_uleb128(u64, "\x3f")) == 63); | ||
| 275 | testing.expect((try test_read_uleb128(u64, "\x40")) == 64); | ||
| 276 | testing.expect((try test_read_uleb128(u64, "\x7f")) == 0x7f); | ||
| 277 | testing.expect((try test_read_uleb128(u64, "\x80\x01")) == 0x80); | ||
| 278 | testing.expect((try test_read_uleb128(u64, "\x81\x01")) == 0x81); | ||
| 279 | testing.expect((try test_read_uleb128(u64, "\x90\x01")) == 0x90); | ||
| 280 | testing.expect((try test_read_uleb128(u64, "\xff\x01")) == 0xff); | ||
| 281 | testing.expect((try test_read_uleb128(u64, "\x80\x02")) == 0x100); | ||
| 282 | testing.expect((try test_read_uleb128(u64, "\x81\x02")) == 0x101); | ||
| 283 | testing.expect((try test_read_uleb128(u64, "\x80\xc1\x80\x80\x10")) == 4294975616); | ||
| 284 | testing.expect((try test_read_uleb128(u64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01")) == 0x8000000000000000); | ||
| 285 | |||
| 286 | // Decode ULEB128 with extra padding bytes | ||
| 287 | testing.expect((try test_read_uleb128(u64, "\x80\x00")) == 0); | ||
| 288 | testing.expect((try test_read_uleb128(u64, "\x80\x80\x00")) == 0); | ||
| 289 | testing.expect((try test_read_uleb128(u64, "\xff\x00")) == 0x7f); | ||
| 290 | testing.expect((try test_read_uleb128(u64, "\xff\x80\x00")) == 0x7f); | ||
| 291 | testing.expect((try test_read_uleb128(u64, "\x80\x81\x00")) == 0x80); | ||
| 292 | testing.expect((try test_read_uleb128(u64, "\x80\x81\x80\x00")) == 0x80); | ||
| 293 | |||
| 294 | // Decode sequence of ULEB128 values | ||
| 295 | try test_read_uleb128_seq(u64, 4, "\x81\x01\x3f\x80\x7f\x80\x80\x80\x00"); | ||
| 296 | } | ||
| 297 | |||
| 298 | fn test_write_leb128(value: anytype) !void { | ||
| 299 | const T = @TypeOf(value); | ||
| 300 | const t_signed = @typeInfo(T).Int.is_signed; | ||
| 301 | const signedness = if (t_signed) .signed else .unsigned; | ||
| 302 | |||
| 303 | const writeStream = if (t_signed) writeILEB128 else writeULEB128; | ||
| 304 | const readStream = if (t_signed) readILEB128 else readULEB128; | ||
| 305 | |||
| 306 | // decode to a larger bit size too, to ensure sign extension | ||
| 307 | // is working as expected | ||
| 308 | const larger_type_bits = ((@typeInfo(T).Int.bits + 8) / 8) * 8; | ||
| 309 | const B = std.meta.Int(signedness, larger_type_bits); | ||
| 310 | |||
| 311 | const bytes_needed = bn: { | ||
| 312 | const S = std.meta.Int(signedness, @sizeOf(T) * 8); | ||
| 313 | if (@typeInfo(T).Int.bits <= 7) break :bn @as(u16, 1); | ||
| 314 | |||
| 315 | const unused_bits = if (value < 0) @clz(T, ~value) else @clz(T, value); | ||
| 316 | const used_bits: u16 = (@typeInfo(T).Int.bits - unused_bits) + @boolToInt(t_signed); | ||
| 317 | if (used_bits <= 7) break :bn @as(u16, 1); | ||
| 318 | break :bn ((used_bits + 6) / 7); | ||
| 319 | }; | ||
| 320 | |||
| 321 | const max_groups = if (@typeInfo(T).Int.bits == 0) 1 else (@typeInfo(T).Int.bits + 6) / 7; | ||
| 322 | |||
| 323 | var buf: [max_groups]u8 = undefined; | ||
| 324 | var fbs = std.io.fixedBufferStream(&buf); | ||
| 325 | |||
| 326 | // stream write | ||
| 327 | try writeStream(fbs.writer(), value); | ||
| 328 | const w1_pos = fbs.pos; | ||
| 329 | testing.expect(w1_pos == bytes_needed); | ||
| 330 | |||
| 331 | // stream read | ||
| 332 | fbs.pos = 0; | ||
| 333 | const sr = try readStream(T, fbs.reader()); | ||
| 334 | testing.expect(fbs.pos == w1_pos); | ||
| 335 | testing.expect(sr == value); | ||
| 336 | |||
| 337 | // bigger type stream read | ||
| 338 | fbs.pos = 0; | ||
| 339 | const bsr = try readStream(B, fbs.reader()); | ||
| 340 | testing.expect(fbs.pos == w1_pos); | ||
| 341 | testing.expect(bsr == value); | ||
| 342 | } | ||
| 343 | |||
| 344 | test "serialize unsigned LEB128" { | ||
| 345 | const max_bits = 18; | ||
| 346 | |||
| 347 | comptime var t = 0; | ||
| 348 | inline while (t <= max_bits) : (t += 1) { | ||
| 349 | const T = std.meta.Int(.unsigned, t); | ||
| 350 | const min = std.math.minInt(T); | ||
| 351 | const max = std.math.maxInt(T); | ||
| 352 | var i = @as(std.meta.Int(.unsigned, @typeInfo(T).Int.bits + 1), min); | ||
| 353 | |||
| 354 | while (i <= max) : (i += 1) try test_write_leb128(@intCast(T, i)); | ||
| 355 | } | ||
| 356 | } | ||
| 357 | |||
| 358 | test "serialize signed LEB128" { | ||
| 359 | // explicitly test i0 because starting `t` at 0 | ||
| 360 | // will break the while loop | ||
| 361 | try test_write_leb128(@as(i0, 0)); | ||
| 362 | |||
| 363 | const max_bits = 18; | ||
| 364 | |||
| 365 | comptime var t = 1; | ||
| 366 | inline while (t <= max_bits) : (t += 1) { | ||
| 367 | const T = std.meta.Int(.signed, t); | ||
| 368 | const min = std.math.minInt(T); | ||
| 369 | const max = std.math.maxInt(T); | ||
| 370 | var i = @as(std.meta.Int(.signed, @typeInfo(T).Int.bits + 1), min); | ||
| 371 | |||
| 372 | while (i <= max) : (i += 1) try test_write_leb128(@intCast(T, i)); | ||
| 373 | } | ||
| 374 | } | ||
lib/std/std.zig+1| ... | @@ -65,6 +65,7 @@ pub const hash_map = @import("hash_map.zig"); | ... | @@ -65,6 +65,7 @@ pub const hash_map = @import("hash_map.zig"); |
| 65 | pub const heap = @import("heap.zig"); | 65 | pub const heap = @import("heap.zig"); |
| 66 | pub const io = @import("io.zig"); | 66 | pub const io = @import("io.zig"); |
| 67 | pub const json = @import("json.zig"); | 67 | pub const json = @import("json.zig"); |
| 68 | pub const leb = @import("leb128.zig"); | ||
| 68 | pub const log = @import("log.zig"); | 69 | pub const log = @import("log.zig"); |
| 69 | pub const macho = @import("macho.zig"); | 70 | pub const macho = @import("macho.zig"); |
| 70 | pub const math = @import("math.zig"); | 71 | pub const math = @import("math.zig"); |
src/codegen.zig+1-1| ... | @@ -14,7 +14,7 @@ const Target = std.Target; | ... | @@ -14,7 +14,7 @@ const Target = std.Target; |
| 14 | const Allocator = mem.Allocator; | 14 | const Allocator = mem.Allocator; |
| 15 | const trace = @import("tracy.zig").trace; | 15 | const trace = @import("tracy.zig").trace; |
| 16 | const DW = std.dwarf; | 16 | const DW = std.dwarf; |
| 17 | const leb128 = std.debug.leb; | 17 | const leb128 = std.leb; |
| 18 | const log = std.log.scoped(.codegen); | 18 | const log = std.log.scoped(.codegen); |
| 19 | 19 | ||
| 20 | /// The codegen-related data that is stored in `ir.Inst.Block` instructions. | 20 | /// The codegen-related data that is stored in `ir.Inst.Block` instructions. |
src/codegen/wasm.zig+1-1| ... | @@ -2,7 +2,7 @@ const std = @import("std"); | ... | @@ -2,7 +2,7 @@ const std = @import("std"); |
| 2 | const Allocator = std.mem.Allocator; | 2 | const Allocator = std.mem.Allocator; |
| 3 | const ArrayList = std.ArrayList; | 3 | const ArrayList = std.ArrayList; |
| 4 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 5 | const leb = std.debug.leb; | 5 | const leb = std.leb; |
| 6 | const mem = std.mem; | 6 | const mem = std.mem; |
| 7 | 7 | ||
| 8 | const Module = @import("../Module.zig"); | 8 | const Module = @import("../Module.zig"); |
src/link/Elf.zig+1-1| ... | @@ -8,7 +8,7 @@ const fs = std.fs; | ... | @@ -8,7 +8,7 @@ const fs = std.fs; |
| 8 | const elf = std.elf; | 8 | const elf = std.elf; |
| 9 | const log = std.log.scoped(.link); | 9 | const log = std.log.scoped(.link); |
| 10 | const DW = std.dwarf; | 10 | const DW = std.dwarf; |
| 11 | const leb128 = std.debug.leb; | 11 | const leb128 = std.leb; |
| 12 | 12 | ||
| 13 | const ir = @import("../ir.zig"); | 13 | const ir = @import("../ir.zig"); |
| 14 | const Module = @import("../Module.zig"); | 14 | const Module = @import("../Module.zig"); |
src/link/MachO/Trie.zig+11-8| ... | @@ -32,7 +32,7 @@ const Trie = @This(); | ... | @@ -32,7 +32,7 @@ const Trie = @This(); |
| 32 | 32 | ||
| 33 | const std = @import("std"); | 33 | const std = @import("std"); |
| 34 | const mem = std.mem; | 34 | const mem = std.mem; |
| 35 | const leb = std.debug.leb; | 35 | const leb = std.leb; |
| 36 | const log = std.log.scoped(.link); | 36 | const log = std.log.scoped(.link); |
| 37 | const testing = std.testing; | 37 | const testing = std.testing; |
| 38 | const assert = std.debug.assert; | 38 | const assert = std.debug.assert; |
| ... | @@ -139,16 +139,18 @@ const Node = struct { | ... | @@ -139,16 +139,18 @@ const Node = struct { |
| 139 | // Terminal node info: encode export flags and vmaddr offset of this symbol. | 139 | // Terminal node info: encode export flags and vmaddr offset of this symbol. |
| 140 | var info_buf_len: usize = 0; | 140 | var info_buf_len: usize = 0; |
| 141 | var info_buf: [@sizeOf(u64) * 2]u8 = undefined; | 141 | var info_buf: [@sizeOf(u64) * 2]u8 = undefined; |
| 142 | info_buf_len += try leb.writeULEB128Mem(info_buf[0..], self.export_flags.?); | 142 | var info_stream = std.io.fixedBufferStream(&info_buf); |
| 143 | info_buf_len += try leb.writeULEB128Mem(info_buf[info_buf_len..], offset); | 143 | try leb.writeULEB128(info_stream.writer(), self.export_flags.?); |
| 144 | try leb.writeULEB128(info_stream.writer(), offset); | ||
| 144 | 145 | ||
| 145 | // Encode the size of the terminal node info. | 146 | // Encode the size of the terminal node info. |
| 146 | var size_buf: [@sizeOf(u64)]u8 = undefined; | 147 | var size_buf: [@sizeOf(u64)]u8 = undefined; |
| 147 | const size_buf_len = try leb.writeULEB128Mem(size_buf[0..], info_buf_len); | 148 | var size_stream = std.io.fixedBufferStream(&size_buf); |
| 149 | try leb.writeULEB128(size_stream.writer(), info_stream.pos); | ||
| 148 | 150 | ||
| 149 | // Now, write them to the output buffer. | 151 | // Now, write them to the output buffer. |
| 150 | buffer.appendSliceAssumeCapacity(size_buf[0..size_buf_len]); | 152 | buffer.appendSliceAssumeCapacity(size_buf[0..size_stream.pos]); |
| 151 | buffer.appendSliceAssumeCapacity(info_buf[0..info_buf_len]); | 153 | buffer.appendSliceAssumeCapacity(info_buf[0..info_stream.pos]); |
| 152 | } else { | 154 | } else { |
| 153 | // Non-terminal node is delimited by 0 byte. | 155 | // Non-terminal node is delimited by 0 byte. |
| 154 | buffer.appendAssumeCapacity(0); | 156 | buffer.appendAssumeCapacity(0); |
| ... | @@ -162,8 +164,9 @@ const Node = struct { | ... | @@ -162,8 +164,9 @@ const Node = struct { |
| 162 | buffer.appendAssumeCapacity(0); | 164 | buffer.appendAssumeCapacity(0); |
| 163 | 165 | ||
| 164 | var buf: [@sizeOf(u64)]u8 = undefined; | 166 | var buf: [@sizeOf(u64)]u8 = undefined; |
| 165 | const buf_len = try leb.writeULEB128Mem(buf[0..], edge.to.trie_offset.?); | 167 | var buf_stream = std.io.fixedBufferStream(&buf); |
| 166 | buffer.appendSliceAssumeCapacity(buf[0..buf_len]); | 168 | try leb.writeULEB128(buf_stream.writer(), edge.to.trie_offset.?); |
| 169 | buffer.appendSliceAssumeCapacity(buf[0..buf_stream.pos]); | ||
| 167 | } | 170 | } |
| 168 | } | 171 | } |
| 169 | 172 |
src/link/Wasm.zig+1-1| ... | @@ -5,7 +5,7 @@ const mem = std.mem; | ... | @@ -5,7 +5,7 @@ const mem = std.mem; |
| 5 | const Allocator = std.mem.Allocator; | 5 | const Allocator = std.mem.Allocator; |
| 6 | const assert = std.debug.assert; | 6 | const assert = std.debug.assert; |
| 7 | const fs = std.fs; | 7 | const fs = std.fs; |
| 8 | const leb = std.debug.leb; | 8 | const leb = std.leb; |
| 9 | const log = std.log.scoped(.link); | 9 | const log = std.log.scoped(.link); |
| 10 | 10 | ||
| 11 | const Module = @import("../Module.zig"); | 11 | const Module = @import("../Module.zig"); |