| 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 | 22 | const File = std.fs.File; |
| 23 | 23 | const windows = std.os.windows; |
| 24 | 24 | |
| 25 | pub const leb = @import("debug/leb128.zig"); | |
| 26 | ||
| 27 | 25 | pub const runtime_safety = switch (builtin.mode) { |
| 28 | 26 | .Debug, .ReleaseSafe => true, |
| 29 | 27 | .ReleaseFast, .ReleaseSmall => false, |
| ... | ... | @@ -1843,8 +1841,3 @@ pub fn dumpStackPointerAddr(prefix: []const u8) void { |
| 1843 | 1841 | ); |
| 1844 | 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 | 10 | const io = std.io; |
| 11 | 11 | const mem = std.mem; |
| 12 | 12 | const math = std.math; |
| 13 | const leb = @import("debug/leb128.zig"); | |
| 13 | const leb = @import("leb128.zig"); | |
| 14 | 14 | |
| 15 | 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 | 65 | pub const heap = @import("heap.zig"); |
| 66 | 66 | pub const io = @import("io.zig"); |
| 67 | 67 | pub const json = @import("json.zig"); |
| 68 | pub const leb = @import("leb128.zig"); | |
| 68 | 69 | pub const log = @import("log.zig"); |
| 69 | 70 | pub const macho = @import("macho.zig"); |
| 70 | 71 | pub const math = @import("math.zig"); |
src/codegen.zig+1-1| ... | ... | @@ -14,7 +14,7 @@ const Target = std.Target; |
| 14 | 14 | const Allocator = mem.Allocator; |
| 15 | 15 | const trace = @import("tracy.zig").trace; |
| 16 | 16 | const DW = std.dwarf; |
| 17 | const leb128 = std.debug.leb; | |
| 17 | const leb128 = std.leb; | |
| 18 | 18 | const log = std.log.scoped(.codegen); |
| 19 | 19 | |
| 20 | 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 | 2 | const Allocator = std.mem.Allocator; |
| 3 | 3 | const ArrayList = std.ArrayList; |
| 4 | 4 | const assert = std.debug.assert; |
| 5 | const leb = std.debug.leb; | |
| 5 | const leb = std.leb; | |
| 6 | 6 | const mem = std.mem; |
| 7 | 7 | |
| 8 | 8 | const Module = @import("../Module.zig"); |
src/link/Elf.zig+1-1| ... | ... | @@ -8,7 +8,7 @@ const fs = std.fs; |
| 8 | 8 | const elf = std.elf; |
| 9 | 9 | const log = std.log.scoped(.link); |
| 10 | 10 | const DW = std.dwarf; |
| 11 | const leb128 = std.debug.leb; | |
| 11 | const leb128 = std.leb; | |
| 12 | 12 | |
| 13 | 13 | const ir = @import("../ir.zig"); |
| 14 | 14 | const Module = @import("../Module.zig"); |
src/link/MachO/Trie.zig+11-8| ... | ... | @@ -32,7 +32,7 @@ const Trie = @This(); |
| 32 | 32 | |
| 33 | 33 | const std = @import("std"); |
| 34 | 34 | const mem = std.mem; |
| 35 | const leb = std.debug.leb; | |
| 35 | const leb = std.leb; | |
| 36 | 36 | const log = std.log.scoped(.link); |
| 37 | 37 | const testing = std.testing; |
| 38 | 38 | const assert = std.debug.assert; |
| ... | ... | @@ -139,16 +139,18 @@ const Node = struct { |
| 139 | 139 | // Terminal node info: encode export flags and vmaddr offset of this symbol. |
| 140 | 140 | var info_buf_len: usize = 0; |
| 141 | 141 | var info_buf: [@sizeOf(u64) * 2]u8 = undefined; |
| 142 | info_buf_len += try leb.writeULEB128Mem(info_buf[0..], self.export_flags.?); | |
| 143 | info_buf_len += try leb.writeULEB128Mem(info_buf[info_buf_len..], offset); | |
| 142 | var info_stream = std.io.fixedBufferStream(&info_buf); | |
| 143 | try leb.writeULEB128(info_stream.writer(), self.export_flags.?); | |
| 144 | try leb.writeULEB128(info_stream.writer(), offset); | |
| 144 | 145 | |
| 145 | 146 | // Encode the size of the terminal node info. |
| 146 | 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 | 151 | // Now, write them to the output buffer. |
| 150 | buffer.appendSliceAssumeCapacity(size_buf[0..size_buf_len]); | |
| 151 | buffer.appendSliceAssumeCapacity(info_buf[0..info_buf_len]); | |
| 152 | buffer.appendSliceAssumeCapacity(size_buf[0..size_stream.pos]); | |
| 153 | buffer.appendSliceAssumeCapacity(info_buf[0..info_stream.pos]); | |
| 152 | 154 | } else { |
| 153 | 155 | // Non-terminal node is delimited by 0 byte. |
| 154 | 156 | buffer.appendAssumeCapacity(0); |
| ... | ... | @@ -162,8 +164,9 @@ const Node = struct { |
| 162 | 164 | buffer.appendAssumeCapacity(0); |
| 163 | 165 | |
| 164 | 166 | var buf: [@sizeOf(u64)]u8 = undefined; |
| 165 | const buf_len = try leb.writeULEB128Mem(buf[0..], edge.to.trie_offset.?); | |
| 166 | buffer.appendSliceAssumeCapacity(buf[0..buf_len]); | |
| 167 | var buf_stream = std.io.fixedBufferStream(&buf); | |
| 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 | 5 | const Allocator = std.mem.Allocator; |
| 6 | 6 | const assert = std.debug.assert; |
| 7 | 7 | const fs = std.fs; |
| 8 | const leb = std.debug.leb; | |
| 8 | const leb = std.leb; | |
| 9 | 9 | const log = std.log.scoped(.link); |
| 10 | 10 | |
| 11 | 11 | const Module = @import("../Module.zig"); |