| ... | ... | @@ -1,7 +1,7 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 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 | 5 | /// or error.Overflow if the value cannot fit. |
| 6 | 6 | pub fn readULEB128(comptime T: type, reader: var) !T { |
| 7 | 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 | 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 | 28 | if (U != T) { |
| 29 | 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 | 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 | 36 | pub fn writeULEB128(writer: var, uint_value: var) !void { |
| 37 | 37 | const T = @TypeOf(uint_value); |
| 38 | 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 | 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 | 54 | /// The provided slice reference will be updated to point to the byte after the last byte read. |
| 55 | 55 | pub fn readULEB128Mem(comptime T: type, ptr: *[]const u8) !T { |
| 56 | 56 | var buf = std.io.fixedBufferStream(ptr.*); |
| ... | ... | @@ -59,7 +59,7 @@ pub fn readULEB128Mem(comptime T: type, ptr: *[]const u8) !T { |
| 59 | 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 | 63 | /// returning the number of bytes written. |
| 64 | 64 | pub fn writeULEB128Mem(ptr: []u8, uint_value: var) !usize { |
| 65 | 65 | const T = @TypeOf(uint_value); |
| ... | ... | @@ -69,7 +69,7 @@ pub fn writeULEB128Mem(ptr: []u8, uint_value: var) !usize { |
| 69 | 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 | 73 | /// or error.Overflow if the value cannot fit. |
| 74 | 74 | pub fn readILEB128(comptime T: type, reader: var) !T { |
| 75 | 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 | 87 | |
| 88 | 88 | const shift = group * 7; |
| 89 | 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 | 91 | if (byte & 0x80 != 0) return error.Overflow; |
| 92 | 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 | 95 | const remaining_shift = @intCast(u3, U.bit_count - @as(u16, shift)); |
| 96 | 96 | const remaining_bits = @bitCast(i8, byte | 0x80) >> remaining_shift; |
| 97 | 97 | if (remaining_bits != -1) return error.Overflow; |
| ... | ... | @@ -111,7 +111,7 @@ pub fn readILEB128(comptime T: type, reader: var) !T { |
| 111 | 111 | } |
| 112 | 112 | |
| 113 | 113 | const result = @bitCast(S, value); |
| 114 | | //Only applies if we extended to i8 |
| 114 | // Only applies if we extended to i8 |
| 115 | 115 | if (S != T) { |
| 116 | 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 | 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 | 123 | pub fn writeILEB128(writer: var, int_value: var) !void { |
| 124 | 124 | const T = @TypeOf(int_value); |
| 125 | 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 | 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 | 145 | /// The provided slice reference will be updated to point to the byte after the last byte read. |
| 146 | 146 | pub fn readILEB128Mem(comptime T: type, ptr: *[]const u8) !T { |
| 147 | 147 | var buf = std.io.fixedBufferStream(ptr.*); |
| ... | ... | @@ -150,7 +150,7 @@ pub fn readILEB128Mem(comptime T: type, ptr: *[]const u8) !T { |
| 150 | 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 | 154 | /// returning the number of bytes written. |
| 155 | 155 | pub fn writeILEB128Mem(ptr: []u8, int_value: var) !usize { |
| 156 | 156 | const T = @TypeOf(int_value); |
| ... | ... | @@ -159,7 +159,7 @@ pub fn writeILEB128Mem(ptr: []u8, int_value: var) !usize { |
| 159 | 159 | return buf.pos; |
| 160 | 160 | } |
| 161 | 161 | |
| 162 | | //tests |
| 162 | // tests |
| 163 | 163 | fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T { |
| 164 | 164 | var reader = std.io.fixedBufferStream(encoded); |
| 165 | 165 | return try readILEB128(T, reader.reader()); |
| ... | ... | @@ -303,43 +303,54 @@ fn test_write_leb128(value: var) !void { |
| 303 | 303 | const readStream = if (T.is_signed) readILEB128 else readULEB128; |
| 304 | 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 | 307 | // is working as expected |
| 308 | 308 | const larger_type_bits = ((T.bit_count + 8) / 8) * 8; |
| 309 | 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 | 321 | const max_groups = if (T.bit_count == 0) 1 else (T.bit_count + 6) / 7; |
| 311 | 322 | |
| 312 | 323 | var buf: [max_groups]u8 = undefined; |
| 313 | 324 | var fbs = std.io.fixedBufferStream(&buf); |
| 314 | 325 | |
| 315 | | //stream write |
| 326 | // stream write |
| 316 | 327 | try writeStream(fbs.writer(), value); |
| 317 | 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 | 332 | fbs.pos = 0; |
| 322 | 333 | const sr = try readStream(T, fbs.reader()); |
| 323 | 334 | testing.expect(fbs.pos == w1_pos); |
| 324 | 335 | testing.expect(sr == value); |
| 325 | 336 | |
| 326 | | //bigger type stream read |
| 337 | // bigger type stream read |
| 327 | 338 | fbs.pos = 0; |
| 328 | 339 | const bsr = try readStream(B, fbs.reader()); |
| 329 | 340 | testing.expect(fbs.pos == w1_pos); |
| 330 | 341 | testing.expect(bsr == value); |
| 331 | 342 | |
| 332 | | //mem write |
| 343 | // mem write |
| 333 | 344 | const w2_pos = try writeMem(&buf, value); |
| 334 | 345 | testing.expect(w2_pos == w1_pos); |
| 335 | 346 | |
| 336 | | //mem read |
| 347 | // mem read |
| 337 | 348 | var buf_ref: []u8 = buf[0..]; |
| 338 | 349 | const mr = try readMem(T, &buf_ref); |
| 339 | 350 | testing.expect(@ptrToInt(buf_ref.ptr) - @ptrToInt(&buf) == w2_pos); |
| 340 | 351 | testing.expect(mr == value); |
| 341 | 352 | |
| 342 | | //bigger type mem read |
| 353 | // bigger type mem read |
| 343 | 354 | buf_ref = buf[0..]; |
| 344 | 355 | const bmr = try readMem(T, &buf_ref); |
| 345 | 356 | testing.expect(@ptrToInt(buf_ref.ptr) - @ptrToInt(&buf) == w2_pos); |
| ... | ... | @@ -361,7 +372,7 @@ test "serialize unsigned LEB128" { |
| 361 | 372 | } |
| 362 | 373 | |
| 363 | 374 | test "serialize signed LEB128" { |
| 364 | | //explicitly test i0 because starting `t` at 0 |
| 375 | // explicitly test i0 because starting `t` at 0 |
| 365 | 376 | // will break the while loop |
| 366 | 377 | try test_write_leb128(@as(i0, 0)); |
| 367 | 378 | |