authorgravatar for tgschultz@gmail.comtgschultz <tgschultz@gmail.com> 2020-11-16 17:51:54-06:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-11-16 18:51:54-05:00
log48d60834fd61404ea009f7f970775f9c59de1240
treebea3b1427db82fc781e216933fe44dc5b20fdb3f
parentd9c36cb2506f1b8cb30c7e9e108c6005eea7cf66
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Move leb128 and remove trivial *mem functions as discussed in #5588 (#6876)

* 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;
2222const File = std.fs.File;
2323const windows = std.os.windows;
2424
25pub const leb = @import("debug/leb128.zig");
26
2725pub const runtime_safety = switch (builtin.mode) {
2826 .Debug, .ReleaseSafe => true,
2927 .ReleaseFast, .ReleaseSmall => false,
......@@ -1843,8 +1841,3 @@ pub fn dumpStackPointerAddr(prefix: []const u8) void {
18431841 );
18441842 std.debug.warn("{} sp = 0x{x}\n", .{ prefix, sp });
18451843}
1846
1847// Reference everything so it gets tested.
1848test "" {
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.
6const std = @import("std");
7const 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.
11pub 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.
41pub 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.
60pub 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.
69pub 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.
79pub 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.
128pub 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.
151pub 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.
160pub 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.
174pub 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
188test "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
212fn 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
217fn 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
222fn 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
231fn 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
240fn 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
251fn 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
262test "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
307test "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
347fn 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
411test "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
425test "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;
1010const io = std.io;
1111const mem = std.mem;
1212const math = std.math;
13const leb = @import("debug/leb128.zig");
13const leb = @import("leb128.zig");
1414
1515const ArrayList = std.ArrayList;
1616
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.
6const std = @import("std");
7const 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.
11pub 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.
41pub 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.
60pub 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.
109pub 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.
137pub 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
151test "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
175fn 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
180fn 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
185fn 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
191fn 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
197fn 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
205fn 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
213test "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
258test "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
298fn 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
344test "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
358test "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");
6565pub const heap = @import("heap.zig");
6666pub const io = @import("io.zig");
6767pub const json = @import("json.zig");
68pub const leb = @import("leb128.zig");
6869pub const log = @import("log.zig");
6970pub const macho = @import("macho.zig");
7071pub const math = @import("math.zig");
src/codegen.zig+1-1
......@@ -14,7 +14,7 @@ const Target = std.Target;
1414const Allocator = mem.Allocator;
1515const trace = @import("tracy.zig").trace;
1616const DW = std.dwarf;
17const leb128 = std.debug.leb;
17const leb128 = std.leb;
1818const log = std.log.scoped(.codegen);
1919
2020/// 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");
22const Allocator = std.mem.Allocator;
33const ArrayList = std.ArrayList;
44const assert = std.debug.assert;
5const leb = std.debug.leb;
5const leb = std.leb;
66const mem = std.mem;
77
88const Module = @import("../Module.zig");
src/link/Elf.zig+1-1
......@@ -8,7 +8,7 @@ const fs = std.fs;
88const elf = std.elf;
99const log = std.log.scoped(.link);
1010const DW = std.dwarf;
11const leb128 = std.debug.leb;
11const leb128 = std.leb;
1212
1313const ir = @import("../ir.zig");
1414const Module = @import("../Module.zig");
src/link/MachO/Trie.zig+11-8
......@@ -32,7 +32,7 @@ const Trie = @This();
3232
3333const std = @import("std");
3434const mem = std.mem;
35const leb = std.debug.leb;
35const leb = std.leb;
3636const log = std.log.scoped(.link);
3737const testing = std.testing;
3838const assert = std.debug.assert;
......@@ -139,16 +139,18 @@ const Node = struct {
139139 // Terminal node info: encode export flags and vmaddr offset of this symbol.
140140 var info_buf_len: usize = 0;
141141 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);
144145
145146 // Encode the size of the terminal node info.
146147 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);
148150
149151 // 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]);
152154 } else {
153155 // Non-terminal node is delimited by 0 byte.
154156 buffer.appendAssumeCapacity(0);
......@@ -162,8 +164,9 @@ const Node = struct {
162164 buffer.appendAssumeCapacity(0);
163165
164166 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]);
167170 }
168171 }
169172
src/link/Wasm.zig+1-1
......@@ -5,7 +5,7 @@ const mem = std.mem;
55const Allocator = std.mem.Allocator;
66const assert = std.debug.assert;
77const fs = std.fs;
8const leb = std.debug.leb;
8const leb = std.leb;
99const log = std.log.scoped(.link);
1010
1111const Module = @import("../Module.zig");