Skip to main content

reifydb_core/util/encoding/keycode/
varint.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4pub fn encode_u64_varint(v: u64, output: &mut Vec<u8>) {
5	if v < (1 << 7) {
6		output.push(v as u8);
7	} else if v < (1 << 14) {
8		output.push(0x80 | (v >> 8) as u8);
9		output.push(v as u8);
10	} else if v < (1 << 21) {
11		output.push(0xc0 | (v >> 16) as u8);
12		output.push((v >> 8) as u8);
13		output.push(v as u8);
14	} else if v < (1 << 28) {
15		output.push(0xe0 | (v >> 24) as u8);
16		output.push((v >> 16) as u8);
17		output.push((v >> 8) as u8);
18		output.push(v as u8);
19	} else if v < (1 << 35) {
20		output.push(0xf0 | (v >> 32) as u8);
21		output.push((v >> 24) as u8);
22		output.push((v >> 16) as u8);
23		output.push((v >> 8) as u8);
24		output.push(v as u8);
25	} else if v < (1 << 42) {
26		output.push(0xf8 | (v >> 40) as u8);
27		output.push((v >> 32) as u8);
28		output.push((v >> 24) as u8);
29		output.push((v >> 16) as u8);
30		output.push((v >> 8) as u8);
31		output.push(v as u8);
32	} else if v < (1 << 49) {
33		output.push(0xfc | (v >> 48) as u8);
34		output.push((v >> 40) as u8);
35		output.push((v >> 32) as u8);
36		output.push((v >> 24) as u8);
37		output.push((v >> 16) as u8);
38		output.push((v >> 8) as u8);
39		output.push(v as u8);
40	} else if v < (1 << 56) {
41		output.push(0xfe | (v >> 56) as u8);
42		output.push((v >> 48) as u8);
43		output.push((v >> 40) as u8);
44		output.push((v >> 32) as u8);
45		output.push((v >> 24) as u8);
46		output.push((v >> 16) as u8);
47		output.push((v >> 8) as u8);
48		output.push(v as u8);
49	} else {
50		output.push(0xff);
51		output.extend_from_slice(&v.to_be_bytes());
52	}
53}
54
55pub fn decode_u64_varint(input: &mut &[u8]) -> Option<u64> {
56	if input.is_empty() {
57		return None;
58	}
59	let first = input[0];
60	let prefix = first.leading_ones() as usize;
61	if prefix == 0 {
62		*input = &input[1..];
63		Some(first as u64)
64	} else if prefix < 8 {
65		if input.len() <= prefix {
66			return None;
67		}
68		let mut v = if prefix == 7 {
69			0
70		} else {
71			(first & (0xff >> (prefix + 1))) as u64
72		};
73		for i in 1..=prefix {
74			v = (v << 8) | input[i] as u64;
75		}
76		*input = &input[prefix + 1..];
77		Some(v)
78	} else {
79		if input.len() < 9 {
80			return None;
81		}
82		let mut bytes = [0u8; 8];
83		bytes.copy_from_slice(&input[1..9]);
84		*input = &input[9..];
85		Some(u64::from_be_bytes(bytes))
86	}
87}
88
89pub fn encode_i64_varint(v: i64, output: &mut Vec<u8>) {
90	if v >= 0 {
91		if v < 64 {
92			output.push(0x80 | v as u8);
93		} else if v < 8192 + 64 {
94			let v = (v - 64) as u16;
95			output.push(0xc0 | (v >> 8) as u8);
96			output.push(v as u8);
97		} else {
98			output.push(0xfe);
99			output.extend_from_slice(&v.to_be_bytes());
100		}
101	} else if v >= -64 {
102		output.push(0x40 | (v + 64) as u8);
103	} else if v >= -8192 - 64 {
104		let v = (v + 64 + 8192) as u16;
105		output.push(0x20 | (v >> 8) as u8);
106		output.push(v as u8);
107	} else {
108		output.push(0x01);
109		output.extend_from_slice(&v.to_be_bytes());
110	}
111}
112
113pub fn decode_i64_varint(input: &mut &[u8]) -> Option<i64> {
114	if input.is_empty() {
115		return None;
116	}
117	let first = input[0];
118	if first >= 0x80 {
119		if first < 0xc0 {
120			*input = &input[1..];
121			Some((first & 0x3f) as i64)
122		} else if first < 0xfe {
123			if input.len() < 2 {
124				return None;
125			}
126			let v = ((first & 0x1f) as u16) << 8 | input[1] as u16;
127			*input = &input[2..];
128			Some(v as i64 + 64)
129		} else {
130			if input.len() < 9 {
131				return None;
132			}
133			let mut bytes = [0u8; 8];
134			bytes.copy_from_slice(&input[1..9]);
135			*input = &input[9..];
136			Some(i64::from_be_bytes(bytes))
137		}
138	} else if first >= 0x40 {
139		*input = &input[1..];
140		Some((first & 0x3f) as i64 - 64)
141	} else if first >= 0x20 {
142		if input.len() < 2 {
143			return None;
144		}
145		let v = ((first & 0x1f) as u16) << 8 | input[1] as u16;
146		*input = &input[2..];
147		Some(v as i64 - 64 - 8192)
148	} else {
149		if input.len() < 9 {
150			return None;
151		}
152		let mut bytes = [0u8; 8];
153		bytes.copy_from_slice(&input[1..9]);
154		*input = &input[9..];
155		Some(i64::from_be_bytes(bytes))
156	}
157}
158
159#[cfg(test)]
160mod tests {
161	use super::*;
162
163	#[test]
164	fn test_u64_varint_ordering() {
165		let values = vec![
166			0,
167			1,
168			127,
169			128,
170			16383,
171			16384,
172			1000000,
173			2000000,
174			u32::MAX as u64,
175			u32::MAX as u64 + 1,
176			u64::MAX - 1,
177			u64::MAX,
178		];
179		let mut encoded = Vec::new();
180		for &v in &values {
181			let mut buf = Vec::new();
182			encode_u64_varint(v, &mut buf);
183			encoded.push(buf);
184		}
185
186		for i in 0..values.len() - 1 {
187			assert!(values[i] < values[i + 1]);
188			assert!(encoded[i] < encoded[i + 1], "failed for {} vs {}", values[i], values[i + 1]);
189		}
190	}
191
192	#[test]
193	fn test_i64_varint_ordering() {
194		let values = vec![
195			i64::MIN,
196			i64::MIN + 1,
197			-1000000,
198			-8257,
199			-8256,
200			-65,
201			-64,
202			-1,
203			0,
204			1,
205			63,
206			64,
207			8255,
208			8256,
209			1000000,
210			i64::MAX - 1,
211			i64::MAX,
212		];
213		let mut encoded = Vec::new();
214		for &v in &values {
215			let mut buf = Vec::new();
216			encode_i64_varint(v, &mut buf);
217			encoded.push(buf);
218		}
219
220		for i in 0..values.len() - 1 {
221			assert!(values[i] < values[i + 1], "values not sorted: {} vs {}", values[i], values[i + 1]);
222			assert!(
223				encoded[i] < encoded[i + 1],
224				"encoded not sorted: {} ({:x?}) vs {} ({:x?})",
225				values[i],
226				encoded[i],
227				values[i + 1],
228				encoded[i + 1]
229			);
230		}
231	}
232
233	#[test]
234	fn test_i64_varint_roundtrip() {
235		let values = vec![i64::MIN, -1000000, -65, -64, -1, 0, 1, 63, 64, 1000000, i64::MAX];
236		for v in values {
237			let mut buf = Vec::new();
238			encode_i64_varint(v, &mut buf);
239			let mut slice = &buf[..];
240			assert_eq!(decode_i64_varint(&mut slice), Some(v), "failed for {}", v);
241			assert!(slice.is_empty());
242		}
243	}
244}