aboutsummaryrefslogtreecommitdiff
path: root/core/encoding/varint/leb128.odin
blob: 86a72f1216143fa20620f69d7ed4a2a971449e4f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package encoding_varint

/*
	Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
	Made available under Odin's license.

	List of contributors:
		Jeroen van Rijn: Initial implementation.
*/

// 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
// 18 * 7 bits = 126, which means that a possible 19th byte may at most be `0b0000_0011`.
LEB128_MAX_BYTES :: 19

Error :: enum {
	None             = 0,
	Buffer_Too_Small = 1,
	Value_Too_Large  = 2,
}

// Decode a slice of bytes encoding an unsigned LEB128 integer into value and number of bytes used.
// Returns `size` == 0 for an invalid value, empty slice, or a varint > 18 bytes.
decode_uleb128_buffer :: proc(buf: []u8) -> (val: u128, size: int, err: Error) {
	if len(buf) == 0 {
		return 0, 0, .Buffer_Too_Small
	}

	for v in buf {
		val, size, err = decode_uleb128_byte(v, size, val)
		if err != .Buffer_Too_Small {
			return
		}
	}

	if err == .Buffer_Too_Small {
		val, size = 0, 0
	}
	return
}

// Decodes an unsigned LEB128 integer into value a byte at a time.
// Returns `.None` when decoded properly, `.Value_Too_Large` when they value
// exceeds the limits of a u128, and `.Buffer_Too_Small` when it's not yet fully decoded.
decode_uleb128_byte :: proc(input: u8, offset: int, accumulator: u128) -> (val: u128, size: int, err: Error) {
	size = offset + 1

	// 18 * 7 bits = 126, which means that a possible 19th byte may at most be 0b0000_0011.
	if size > LEB128_MAX_BYTES || size == LEB128_MAX_BYTES && input > 0b0000_0011 {
		return 0, 0, .Value_Too_Large
	}

	val = accumulator | u128(input & 0x7f) << uint(offset * 7)

	if input < 128 {
		// We're done
		return
	}

	// If the buffer runs out before the number ends, return an error.
	return val, size, .Buffer_Too_Small
}
decode_uleb128 :: proc {decode_uleb128_buffer, decode_uleb128_byte}

// Decode a slice of bytes encoding a signed LEB128 integer into value and number of bytes used.
// Returns `size` == 0 for an invalid value, empty slice, or a varint > 18 bytes.
decode_ileb128_buffer :: proc(buf: []u8) -> (val: i128, size: int, err: Error) {
	if len(buf) == 0 {
		return 0, 0, .Buffer_Too_Small
	}

	for v in buf {
		val, size, err = decode_ileb128_byte(v, size, val)
		if err != .Buffer_Too_Small {
			return
		}
	}

	if err == .Buffer_Too_Small {
		val, size = 0, 0
	}
	return
}

// Decode a a signed LEB128 integer into value and number of bytes used, one byte at a time.
// Returns `size` == 0 for an invalid value, empty slice, or a varint > 18 bytes.
decode_ileb128_byte :: proc(input: u8, offset: int, accumulator: i128) -> (val: i128, size: int, err: Error) {
	size = offset + 1
	shift := uint(offset * 7)

	// 18 * 7 bits = 126, which including sign means we can have a 19th byte.
	if size > LEB128_MAX_BYTES || size == LEB128_MAX_BYTES && input > 0x7f {
		return 0, 0, .Value_Too_Large
	}

	val = accumulator | i128(input & 0x7f) << shift

	if input < 128 {
		if input & 0x40 == 0x40 {
			val |= max(i128) << (shift + 7)
		}
		return val, size, .None
	}
	return val, size, .Buffer_Too_Small
}
decode_ileb128 :: proc{decode_ileb128_buffer, decode_ileb128_byte}

// Encode `val` into `buf` as an unsigned LEB128 encoded series of bytes.
// `buf` must be appropriately sized.
encode_uleb128 :: proc(buf: []u8, val: u128) -> (size: int, err: Error) {
	val := val

	for {
		size += 1

		if size > len(buf) {
			return 0, .Buffer_Too_Small
		}

		low := val & 0x7f
		val >>= 7

		if val > 0 {
			low |= 0x80 // more bytes to follow
		}
		buf[size - 1] = u8(low)

		if val == 0 { break }
	}
	return
}

// Encode `val` into `buf` as a signed LEB128 encoded series of bytes.
// `buf` must be appropriately sized.
encode_ileb128 :: proc(buf: []u8, val: i128) -> (size: int, err: Error) {
	SIGN_MASK :: i128(1) << 121 // sign extend mask

	val, more := val, true

	for more {
		size += 1

		if size > len(buf) {
			return 0, .Buffer_Too_Small
		}

		low := val & 0x7f
		val >>= 7

		low = (low ~ SIGN_MASK) - SIGN_MASK

		if (val == 0 && low & 0x40 != 0x40) || (val == -1 && low & 0x40 == 0x40) {
			more = false
		} else {
			low |= 0x80
		}

		buf[size - 1] = u8(low)
	}
	return
}