[varint] Add additional LEB128 tests.

This commit is contained in:
Jeroen van Rijn
2022-03-08 19:28:55 +01:00
parent e76a5d8e12
commit 76b10b5f5d
2 changed files with 61 additions and 9 deletions
+6 -2
View File
@@ -10,6 +10,8 @@
// the LEB128 format as used by DWARF debug info, Android .dex and other file formats. // the LEB128 format as used by DWARF debug info, Android .dex and other file formats.
package varint package varint
import "core:fmt"
// In theory we should use the bigint package. In practice, varints bigger than this indicate a corrupted file. // In theory we should use the bigint package. In practice, varints bigger than this indicate a corrupted file.
// Instead we'll set limits on the values we'll encode/decode // Instead we'll set limits on the values we'll encode/decode
// 18 * 7 bits = 126, which means that a possible 19th byte may at most be `0b0000_0011`. // 18 * 7 bits = 126, which means that a possible 19th byte may at most be `0b0000_0011`.
@@ -29,6 +31,7 @@ decode_uleb128 :: proc(buf: []u8) -> (val: u128, size: int, err: Error) {
for v, i in buf { for v, i in buf {
size = i + 1 size = i + 1
// 18 * 7 bits = 126, which means that a possible 19th byte may at most be 0b0000_0011.
if size == LEB128_MAX_BYTES && v > 0b0000_0011 { if size == LEB128_MAX_BYTES && v > 0b0000_0011 {
return 0, 0, .Value_Too_Large return 0, 0, .Value_Too_Large
} }
@@ -60,8 +63,8 @@ decode_ileb128 :: proc(buf: []u8) -> (val: i128, size: int, err: Error) {
for v in buf { for v in buf {
size += 1 size += 1
// 18 * 7 bits = 126, which means that a possible 19th byte may at most be 0b0000_0011. // 18 * 7 bits = 126, which including sign means we can have a 19th byte.
if size == LEB128_MAX_BYTES && v > 0b0000_0011 { if size == LEB128_MAX_BYTES && v > 0x7f {
return 0, 0, .Value_Too_Large return 0, 0, .Value_Too_Large
} }
@@ -86,6 +89,7 @@ encode_uleb128 :: proc(buf: []u8, val: u128) -> (size: int, err: Error) {
size += 1 size += 1
if size > len(buf) { if size > len(buf) {
fmt.println(val, buf[:size - 1])
return 0, .Buffer_Too_Small return 0, .Buffer_Too_Small
} }
@@ -5,10 +5,13 @@ import "core:testing"
import "core:fmt" import "core:fmt"
import "core:os" import "core:os"
import "core:slice" import "core:slice"
import "core:math/rand"
TEST_count := 0 TEST_count := 0
TEST_fail := 0 TEST_fail := 0
RANDOM_TESTS :: 100
when ODIN_TEST { when ODIN_TEST {
expect :: testing.expect expect :: testing.expect
log :: testing.log log :: testing.log
@@ -30,7 +33,7 @@ when ODIN_TEST {
main :: proc() { main :: proc() {
t := testing.T{} t := testing.T{}
test_dwarf(&t) test_leb128(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 { if TEST_fail > 0 {
@@ -39,7 +42,7 @@ main :: proc() {
} }
@(test) @(test)
test_dwarf :: proc(t: ^testing.T) { test_leb128 :: proc(t: ^testing.T) {
buf: [varint.LEB128_MAX_BYTES]u8 buf: [varint.LEB128_MAX_BYTES]u8
for vector in ULEB_Vectors { for vector in ULEB_Vectors {
@@ -75,6 +78,46 @@ test_dwarf :: proc(t: ^testing.T) {
expect(t, size == vector.size && slice.simple_equal(vector.encoded, buf[:size]), msg) expect(t, size == vector.size && slice.simple_equal(vector.encoded, buf[:size]), msg)
} }
} }
for num_bytes in 1..uint(16) {
for _ in 0..RANDOM_TESTS {
unsigned, signed := get_random(num_bytes)
{
encode_size, encode_err := varint.encode_uleb128(buf[:], unsigned)
msg := fmt.tprintf("%v failed to encode as an unsigned LEB128 value, got %v", unsigned, encode_err)
expect(t, encode_err == .None, msg)
decoded, decode_size, decode_err := varint.decode_uleb128(buf[:])
msg = fmt.tprintf("Expected %02x to decode as %v, got %v", buf[:encode_size], unsigned, decoded)
expect(t, decode_err == .None && decode_size == encode_size && decoded == unsigned, msg)
}
{
encode_size, encode_err := varint.encode_ileb128(buf[:], signed)
msg := fmt.tprintf("%v failed to encode as a signed LEB128 value, got %v", signed, encode_err)
expect(t, encode_err == .None, msg)
decoded, decode_size, decode_err := varint.decode_ileb128(buf[:])
msg = fmt.tprintf("Expected %02x to decode as %v, got %v, err: %v", buf[:encode_size], signed, decoded, decode_err)
expect(t, decode_err == .None && decode_size == encode_size && decoded == signed, msg)
}
}
}
}
get_random :: proc(byte_count: uint) -> (u: u128, i: i128) {
assert(byte_count >= 0 && byte_count <= size_of(u128))
for _ in 1..byte_count {
u <<= 8
u |= u128(rand.uint32() & 0xff)
}
bias := i128(1 << (byte_count * 7)) - 1
i = i128(u) - bias
return
} }
ULEB_Test_Vector :: struct { ULEB_Test_Vector :: struct {
@@ -85,11 +128,13 @@ ULEB_Test_Vector :: struct {
} }
ULEB_Vectors :: []ULEB_Test_Vector{ ULEB_Vectors :: []ULEB_Test_Vector{
{ []u8{0x00}, 0, 1, .None }, { []u8{0x00}, 0, 1, .None },
{ []u8{0x7f}, 127, 1, .None }, { []u8{0x7f}, 127, 1, .None },
{ []u8{0xE5, 0x8E, 0x26}, 624485, 3, .None }, { []u8{0xE5, 0x8E, 0x26}, 624485, 3, .None },
{ []u8{0x80}, 0, 0, .Buffer_Too_Small }, { []u8{0x80}, 0, 0, .Buffer_Too_Small },
{ []u8{}, 0, 0, .Buffer_Too_Small }, { []u8{}, 0, 0, .Buffer_Too_Small },
{ []u8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03}, max(u128), 19, .None },
} }
ILEB_Test_Vector :: struct { ILEB_Test_Vector :: struct {
@@ -105,4 +150,7 @@ ILEB_Vectors :: []ILEB_Test_Vector{
{ []u8{0x40}, -64, 1, .None }, { []u8{0x40}, -64, 1, .None },
{ []u8{0xC0, 0xBB, 0x78}, -123456, 3, .None }, { []u8{0xC0, 0xBB, 0x78}, -123456, 3, .None },
{ []u8{}, 0, 0, .Buffer_Too_Small }, { []u8{}, 0, 0, .Buffer_Too_Small },
{ []u8{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7e}, min(i128), 19, .None },
{ []u8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01}, max(i128), 19, .None },
} }