authorgravatar for tgschultz@gmail.comtgschultz <tgschultz@gmail.com> 2020-06-16 16:11:39+00:00
committergravatar for tgschultz@gmail.comtgschultz <tgschultz@gmail.com> 2020-06-16 16:20:59+00:00
log38e69a9e6a6d69a48d130382dcb61ba2516863d0
tree37283197df2b75d0edbee1ed8e112efb42442837
parenta0160d776f30fab70c786cd1159203a593af51b4

Added test to ensure minimum number of bytes is emitted for writes


1 files changed, 33 insertions(+), 22 deletions(-)

lib/std/debug/leb128.zig+33-22
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1const std = @import("std");1const std = @import("std");
2const testing = std.testing;2const testing = std.testing;
33
4///Read a single unsigned LEB128 value from the given reader as type T,4/// Read a single unsigned LEB128 value from the given reader as type T,
5/// or error.Overflow if the value cannot fit.5/// or error.Overflow if the value cannot fit.
6pub fn readULEB128(comptime T: type, reader: var) !T {6pub fn readULEB128(comptime T: type, reader: var) !T {
7 const U = if (T.bit_count < 8) u8 else T;7 const U = if (T.bit_count < 8) u8 else T;
...@@ -24,7 +24,7 @@ pub fn readULEB128(comptime T: type, reader: var) !T {...@@ -24,7 +24,7 @@ pub fn readULEB128(comptime T: type, reader: var) !T {
24 return error.Overflow;24 return error.Overflow;
25 }25 }
2626
27 //only applies in the case that we extended to u827 // only applies in the case that we extended to u8
28 if (U != T) {28 if (U != T) {
29 if (value > std.math.maxInt(T)) return error.Overflow;29 if (value > std.math.maxInt(T)) return error.Overflow;
30 }30 }
...@@ -32,7 +32,7 @@ pub fn readULEB128(comptime T: type, reader: var) !T {...@@ -32,7 +32,7 @@ pub fn readULEB128(comptime T: type, reader: var) !T {
32 return @truncate(T, value);32 return @truncate(T, value);
33}33}
3434
35///Write a single unsigned integer as unsigned LEB128 to the given writer.35/// Write a single unsigned integer as unsigned LEB128 to the given writer.
36pub fn writeULEB128(writer: var, uint_value: var) !void {36pub fn writeULEB128(writer: var, uint_value: var) !void {
37 const T = @TypeOf(uint_value);37 const T = @TypeOf(uint_value);
38 const U = if (T.bit_count < 8) u8 else T;38 const U = if (T.bit_count < 8) u8 else T;
...@@ -50,7 +50,7 @@ pub fn writeULEB128(writer: var, uint_value: var) !void {...@@ -50,7 +50,7 @@ pub fn writeULEB128(writer: var, uint_value: var) !void {
50 }50 }
51}51}
5252
53///Read a single unsinged integer from the given memory as type T.53/// Read a single unsinged integer from the given memory as type T.
54/// The provided slice reference will be updated to point to the byte after the last byte read.54/// The provided slice reference will be updated to point to the byte after the last byte read.
55pub fn readULEB128Mem(comptime T: type, ptr: *[]const u8) !T {55pub fn readULEB128Mem(comptime T: type, ptr: *[]const u8) !T {
56 var buf = std.io.fixedBufferStream(ptr.*);56 var buf = std.io.fixedBufferStream(ptr.*);
...@@ -59,7 +59,7 @@ pub fn readULEB128Mem(comptime T: type, ptr: *[]const u8) !T {...@@ -59,7 +59,7 @@ pub fn readULEB128Mem(comptime T: type, ptr: *[]const u8) !T {
59 return value;59 return value;
60}60}
6161
62///Write a single unsigned LEB128 integer to the given memory as unsigned LEB128,62/// Write a single unsigned LEB128 integer to the given memory as unsigned LEB128,
63/// returning the number of bytes written.63/// returning the number of bytes written.
64pub fn writeULEB128Mem(ptr: []u8, uint_value: var) !usize {64pub fn writeULEB128Mem(ptr: []u8, uint_value: var) !usize {
65 const T = @TypeOf(uint_value);65 const T = @TypeOf(uint_value);
...@@ -69,7 +69,7 @@ pub fn writeULEB128Mem(ptr: []u8, uint_value: var) !usize {...@@ -69,7 +69,7 @@ pub fn writeULEB128Mem(ptr: []u8, uint_value: var) !usize {
69 return buf.pos;69 return buf.pos;
70}70}
7171
72///Read a single signed LEB128 value from the given reader as type T,72/// Read a single signed LEB128 value from the given reader as type T,
73/// or error.Overflow if the value cannot fit.73/// or error.Overflow if the value cannot fit.
74pub fn readILEB128(comptime T: type, reader: var) !T {74pub fn readILEB128(comptime T: type, reader: var) !T {
75 const S = if (T.bit_count < 8) i8 else T;75 const S = if (T.bit_count < 8) i8 else T;
...@@ -87,11 +87,11 @@ pub fn readILEB128(comptime T: type, reader: var) !T {...@@ -87,11 +87,11 @@ pub fn readILEB128(comptime T: type, reader: var) !T {
8787
88 const shift = group * 7;88 const shift = group * 7;
89 if (@shlWithOverflow(U, temp, shift, &temp)) {89 if (@shlWithOverflow(U, temp, shift, &temp)) {
90 //Overflow is ok so long as the sign bit is set and this is the last byte90 // Overflow is ok so long as the sign bit is set and this is the last byte
91 if (byte & 0x80 != 0) return error.Overflow;91 if (byte & 0x80 != 0) return error.Overflow;
92 if (@bitCast(S, temp) >= 0) return error.Overflow;92 if (@bitCast(S, temp) >= 0) return error.Overflow;
9393
94 //and all the overflowed bits are 194 // and all the overflowed bits are 1
95 const remaining_shift = @intCast(u3, U.bit_count - @as(u16, shift));95 const remaining_shift = @intCast(u3, U.bit_count - @as(u16, shift));
96 const remaining_bits = @bitCast(i8, byte | 0x80) >> remaining_shift;96 const remaining_bits = @bitCast(i8, byte | 0x80) >> remaining_shift;
97 if (remaining_bits != -1) return error.Overflow;97 if (remaining_bits != -1) return error.Overflow;
...@@ -111,7 +111,7 @@ pub fn readILEB128(comptime T: type, reader: var) !T {...@@ -111,7 +111,7 @@ pub fn readILEB128(comptime T: type, reader: var) !T {
111 }111 }
112112
113 const result = @bitCast(S, value);113 const result = @bitCast(S, value);
114 //Only applies if we extended to i8114 // Only applies if we extended to i8
115 if (S != T) {115 if (S != T) {
116 if (result > std.math.maxInt(T) or result < std.math.minInt(T)) return error.Overflow;116 if (result > std.math.maxInt(T) or result < std.math.minInt(T)) return error.Overflow;
117 }117 }
...@@ -119,7 +119,7 @@ pub fn readILEB128(comptime T: type, reader: var) !T {...@@ -119,7 +119,7 @@ pub fn readILEB128(comptime T: type, reader: var) !T {
119 return @truncate(T, result);119 return @truncate(T, result);
120}120}
121121
122///Write a single signed integer as signed LEB128 to the given writer.122/// Write a single signed integer as signed LEB128 to the given writer.
123pub fn writeILEB128(writer: var, int_value: var) !void {123pub fn writeILEB128(writer: var, int_value: var) !void {
124 const T = @TypeOf(int_value);124 const T = @TypeOf(int_value);
125 const S = if (T.bit_count < 8) i8 else T;125 const S = if (T.bit_count < 8) i8 else T;
...@@ -141,7 +141,7 @@ pub fn writeILEB128(writer: var, int_value: var) !void {...@@ -141,7 +141,7 @@ pub fn writeILEB128(writer: var, int_value: var) !void {
141 }141 }
142}142}
143143
144///Read a single singed LEB128 integer from the given memory as type T.144/// Read a single singed LEB128 integer from the given memory as type T.
145/// The provided slice reference will be updated to point to the byte after the last byte read.145/// The provided slice reference will be updated to point to the byte after the last byte read.
146pub fn readILEB128Mem(comptime T: type, ptr: *[]const u8) !T {146pub fn readILEB128Mem(comptime T: type, ptr: *[]const u8) !T {
147 var buf = std.io.fixedBufferStream(ptr.*);147 var buf = std.io.fixedBufferStream(ptr.*);
...@@ -150,7 +150,7 @@ pub fn readILEB128Mem(comptime T: type, ptr: *[]const u8) !T {...@@ -150,7 +150,7 @@ pub fn readILEB128Mem(comptime T: type, ptr: *[]const u8) !T {
150 return value;150 return value;
151}151}
152152
153///Write a single signed LEB128 integer to the given memory as unsigned LEB128,153/// Write a single signed LEB128 integer to the given memory as unsigned LEB128,
154/// returning the number of bytes written.154/// returning the number of bytes written.
155pub fn writeILEB128Mem(ptr: []u8, int_value: var) !usize {155pub fn writeILEB128Mem(ptr: []u8, int_value: var) !usize {
156 const T = @TypeOf(int_value);156 const T = @TypeOf(int_value);
...@@ -159,7 +159,7 @@ pub fn writeILEB128Mem(ptr: []u8, int_value: var) !usize {...@@ -159,7 +159,7 @@ pub fn writeILEB128Mem(ptr: []u8, int_value: var) !usize {
159 return buf.pos;159 return buf.pos;
160}160}
161161
162//tests162// tests
163fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T {163fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T {
164 var reader = std.io.fixedBufferStream(encoded);164 var reader = std.io.fixedBufferStream(encoded);
165 return try readILEB128(T, reader.reader());165 return try readILEB128(T, reader.reader());
...@@ -303,43 +303,54 @@ fn test_write_leb128(value: var) !void {...@@ -303,43 +303,54 @@ fn test_write_leb128(value: var) !void {
303 const readStream = if (T.is_signed) readILEB128 else readULEB128;303 const readStream = if (T.is_signed) readILEB128 else readULEB128;
304 const readMem = if (T.is_signed) readILEB128Mem else readULEB128Mem;304 const readMem = if (T.is_signed) readILEB128Mem else readULEB128Mem;
305305
306 //decode to a larger bit size too, to ensure sign extension306 // decode to a larger bit size too, to ensure sign extension
307 // is working as expected307 // is working as expected
308 const larger_type_bits = ((T.bit_count + 8) / 8) * 8;308 const larger_type_bits = ((T.bit_count + 8) / 8) * 8;
309 const B = std.meta.Int(T.is_signed, larger_type_bits);309 const B = std.meta.Int(T.is_signed, larger_type_bits);
310
311 const bytes_needed = bn: {
312 const S = std.meta.Int(T.is_signed, @sizeOf(T) * 8);
313 if (T.bit_count <= 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 = (T.bit_count - unused_bits) + @boolToInt(T.is_signed);
317 if (used_bits <= 7) break :bn @as(u16, 1);
318 break :bn ((used_bits + 6) / 7);
319 };
320
310 const max_groups = if (T.bit_count == 0) 1 else (T.bit_count + 6) / 7;321 const max_groups = if (T.bit_count == 0) 1 else (T.bit_count + 6) / 7;
311322
312 var buf: [max_groups]u8 = undefined;323 var buf: [max_groups]u8 = undefined;
313 var fbs = std.io.fixedBufferStream(&buf);324 var fbs = std.io.fixedBufferStream(&buf);
314325
315 //stream write326 // stream write
316 try writeStream(fbs.writer(), value);327 try writeStream(fbs.writer(), value);
317 const w1_pos = fbs.pos;328 const w1_pos = fbs.pos;
318 testing.expect(w1_pos > 0);329 testing.expect(w1_pos == bytes_needed);
319330
320 //stream read331 // stream read
321 fbs.pos = 0;332 fbs.pos = 0;
322 const sr = try readStream(T, fbs.reader());333 const sr = try readStream(T, fbs.reader());
323 testing.expect(fbs.pos == w1_pos);334 testing.expect(fbs.pos == w1_pos);
324 testing.expect(sr == value);335 testing.expect(sr == value);
325336
326 //bigger type stream read337 // bigger type stream read
327 fbs.pos = 0;338 fbs.pos = 0;
328 const bsr = try readStream(B, fbs.reader());339 const bsr = try readStream(B, fbs.reader());
329 testing.expect(fbs.pos == w1_pos);340 testing.expect(fbs.pos == w1_pos);
330 testing.expect(bsr == value);341 testing.expect(bsr == value);
331342
332 //mem write343 // mem write
333 const w2_pos = try writeMem(&buf, value);344 const w2_pos = try writeMem(&buf, value);
334 testing.expect(w2_pos == w1_pos);345 testing.expect(w2_pos == w1_pos);
335346
336 //mem read347 // mem read
337 var buf_ref: []u8 = buf[0..];348 var buf_ref: []u8 = buf[0..];
338 const mr = try readMem(T, &buf_ref);349 const mr = try readMem(T, &buf_ref);
339 testing.expect(@ptrToInt(buf_ref.ptr) - @ptrToInt(&buf) == w2_pos);350 testing.expect(@ptrToInt(buf_ref.ptr) - @ptrToInt(&buf) == w2_pos);
340 testing.expect(mr == value);351 testing.expect(mr == value);
341352
342 //bigger type mem read353 // bigger type mem read
343 buf_ref = buf[0..];354 buf_ref = buf[0..];
344 const bmr = try readMem(T, &buf_ref);355 const bmr = try readMem(T, &buf_ref);
345 testing.expect(@ptrToInt(buf_ref.ptr) - @ptrToInt(&buf) == w2_pos);356 testing.expect(@ptrToInt(buf_ref.ptr) - @ptrToInt(&buf) == w2_pos);
...@@ -361,7 +372,7 @@ test "serialize unsigned LEB128" {...@@ -361,7 +372,7 @@ test "serialize unsigned LEB128" {
361}372}
362373
363test "serialize signed LEB128" {374test "serialize signed LEB128" {
364 //explicitly test i0 because starting `t` at 0375 // explicitly test i0 because starting `t` at 0
365 // will break the while loop376 // will break the while loop
366 try test_write_leb128(@as(i0, 0));377 try test_write_leb128(@as(i0, 0));
367378