| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | | 3 | |
| 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. |
| 6 | pub fn readULEB128(comptime T: type, reader: var) !T { | 6 | pub 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 | } |
| 26 | | 26 | |
| 27 | //only applies in the case that we extended to u8 | 27 | // 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 | } |
| 34 | | 34 | |
| 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. |
| 36 | pub fn writeULEB128(writer: var, uint_value: var) !void { | 36 | pub 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 | } |
| 52 | | 52 | |
| 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. |
| 55 | pub fn readULEB128Mem(comptime T: type, ptr: *[]const u8) !T { | 55 | pub 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 | } |
| 61 | | 61 | |
| 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. |
| 64 | pub fn writeULEB128Mem(ptr: []u8, uint_value: var) !usize { | 64 | pub 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 | } |
| 71 | | 71 | |
| 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. |
| 74 | pub fn readILEB128(comptime T: type, reader: var) !T { | 74 | pub 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 { |
| 87 | | 87 | |
| 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 byte | 90 | // 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; |
| 93 | | 93 | |
| 94 | //and all the overflowed bits are 1 | 94 | // 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 | } |
| 112 | | 112 | |
| 113 | const result = @bitCast(S, value); | 113 | const result = @bitCast(S, value); |
| 114 | //Only applies if we extended to i8 | 114 | // 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 | } |
| 121 | | 121 | |
| 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. |
| 123 | pub fn writeILEB128(writer: var, int_value: var) !void { | 123 | pub 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 | } |
| 143 | | 143 | |
| 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. |
| 146 | pub fn readILEB128Mem(comptime T: type, ptr: *[]const u8) !T { | 146 | pub 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 | } |
| 152 | | 152 | |
| 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. |
| 155 | pub fn writeILEB128Mem(ptr: []u8, int_value: var) !usize { | 155 | pub 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 | } |
| 161 | | 161 | |
| 162 | //tests | 162 | // tests |
| 163 | fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T { | 163 | fn 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; |
| 305 | | 305 | |
| 306 | //decode to a larger bit size too, to ensure sign extension | 306 | // decode to a larger bit size too, to ensure sign extension |
| 307 | // is working as expected | 307 | // 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; |
| 311 | | 322 | |
| 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); |
| 314 | | 325 | |
| 315 | //stream write | 326 | // 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); |
| 319 | | 330 | |
| 320 | //stream read | 331 | // 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); |
| 325 | | 336 | |
| 326 | //bigger type stream read | 337 | // 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); |
| 331 | | 342 | |
| 332 | //mem write | 343 | // 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); |
| 335 | | 346 | |
| 336 | //mem read | 347 | // 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); |
| 341 | | 352 | |
| 342 | //bigger type mem read | 353 | // 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 | } |
| 362 | | 373 | |
| 363 | test "serialize signed LEB128" { | 374 | test "serialize signed LEB128" { |
| 364 | //explicitly test i0 because starting `t` at 0 | 375 | // explicitly test i0 because starting `t` at 0 |
| 365 | // will break the while loop | 376 | // will break the while loop |
| 366 | try test_write_leb128(@as(i0, 0)); | 377 | try test_write_leb128(@as(i0, 0)); |
| 367 | | 378 | |