varint: reimplement as test balloon for Rust

Implement a trivial test balloon for our Rust build infrastructure by reimplementing the "varint.c" subsystem in Rust. This subsystem is chosen because it is trivial to convert and because it doesn't have any dependencies to other components of Git. If support for Rust is enabled, we stop compiling "varint.c" and instead compile and use "src/varint.rs". Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Oct 2, 2025 at 09:29 UTC 8832e728d362992a38eef89613b44d24f18e6c2a
5 files changed +101 -1
Makefile
+3
@@ -1307,7 +1307,9 @@ LIB_OBJS += urlmatch.o
1307 LIB_OBJS += usage.o
1308 LIB_OBJS += userdiff.o
1309 LIB_OBJS += utf8.o
1310 +ifndef WITH_RUST
1311 LIB_OBJS += varint.o
1312 +endif
1313 LIB_OBJS += version.o
1314 LIB_OBJS += versioncmp.o
1315 LIB_OBJS += walker.o
@@ -1499,6 +1501,7 @@ CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
1501 UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
1502
1503 RUST_SOURCES += src/lib.rs
1504 +RUST_SOURCES += src/varint.rs
1505
1506 GIT-VERSION-FILE: FORCE
1507 @OLD=$$(cat $@ 2>/dev/null || :) && \
meson.build
+4 -1
@@ -522,7 +522,6 @@ libgit_sources = [
522 'usage.c',
523 'userdiff.c',
524 'utf8.c',
525 - 'varint.c',
525 'version.c',
526 'versioncmp.c',
527 'walker.c',
@@ -1707,6 +1706,10 @@ rust_option = get_option('rust').disable_auto_if(not cargo.found())
1706 if rust_option.allowed()
1707 subdir('src')
1708 libgit_c_args += '-DWITH_RUST'
1709 +else
1710 + libgit_sources += [
1711 + 'varint.c',
1712 + ]
1713 endif
1714
1715 libgit = declare_dependency(
src/lib.rs
+1
@@ -0,0 +1 @@
1 +pub mod varint;
src/meson.build
+1
@@ -1,5 +1,6 @@
1 libgit_rs_sources = [
2 'lib.rs',
3 + 'varint.rs',
4 ]
5
6 # Unfortunately we must use a wrapper command to move the output file into the
src/varint.rs new
+92
@@ -0,0 +1,92 @@
1 +#[no_mangle]
2 +pub unsafe extern "C" fn decode_varint(bufp: *mut *const u8) -> u64 {
3 + let mut buf = *bufp;
4 + let mut c = *buf;
5 + let mut val = u64::from(c & 127);
6 +
7 + buf = buf.add(1);
8 +
9 + while (c & 128) != 0 {
10 + val = val.wrapping_add(1);
11 + if val == 0 || val.leading_zeros() < 7 {
12 + return 0; // overflow
13 + }
14 +
15 + c = *buf;
16 + buf = buf.add(1);
17 +
18 + val = (val << 7) + u64::from(c & 127);
19 + }
20 +
21 + *bufp = buf;
22 + val
23 +}
24 +
25 +#[no_mangle]
26 +pub unsafe extern "C" fn encode_varint(value: u64, buf: *mut u8) -> u8 {
27 + let mut varint: [u8; 16] = [0; 16];
28 + let mut pos = varint.len() - 1;
29 +
30 + varint[pos] = (value & 127) as u8;
31 +
32 + let mut value = value >> 7;
33 + while value != 0 {
34 + pos -= 1;
35 + value -= 1;
36 + varint[pos] = 128 | (value & 127) as u8;
37 + value >>= 7;
38 + }
39 +
40 + if !buf.is_null() {
41 + std::ptr::copy_nonoverlapping(varint.as_ptr().add(pos), buf, varint.len() - pos);
42 + }
43 +
44 + (varint.len() - pos) as u8
45 +}
46 +
47 +#[cfg(test)]
48 +mod tests {
49 + use super::*;
50 +
51 + #[test]
52 + fn test_decode_varint() {
53 + unsafe {
54 + assert_eq!(decode_varint(&mut [0x00].as_slice().as_ptr()), 0);
55 + assert_eq!(decode_varint(&mut [0x01].as_slice().as_ptr()), 1);
56 + assert_eq!(decode_varint(&mut [0x7f].as_slice().as_ptr()), 127);
57 + assert_eq!(decode_varint(&mut [0x80, 0x00].as_slice().as_ptr()), 128);
58 + assert_eq!(decode_varint(&mut [0x80, 0x01].as_slice().as_ptr()), 129);
59 + assert_eq!(decode_varint(&mut [0x80, 0x7f].as_slice().as_ptr()), 255);
60 +
61 + // Overflows are expected to return 0.
62 + assert_eq!(decode_varint(&mut [0x88; 16].as_slice().as_ptr()), 0);
63 + }
64 + }
65 +
66 + #[test]
67 + fn test_encode_varint() {
68 + unsafe {
69 + let mut varint: [u8; 16] = [0; 16];
70 +
71 + assert_eq!(encode_varint(0, std::ptr::null_mut()), 1);
72 +
73 + assert_eq!(encode_varint(0, varint.as_mut_slice().as_mut_ptr()), 1);
74 + assert_eq!(varint, [0; 16]);
75 +
76 + assert_eq!(encode_varint(10, varint.as_mut_slice().as_mut_ptr()), 1);
77 + assert_eq!(varint, [10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
78 +
79 + assert_eq!(encode_varint(127, varint.as_mut_slice().as_mut_ptr()), 1);
80 + assert_eq!(varint, [127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
81 +
82 + assert_eq!(encode_varint(128, varint.as_mut_slice().as_mut_ptr()), 2);
83 + assert_eq!(varint, [128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
84 +
85 + assert_eq!(encode_varint(129, varint.as_mut_slice().as_mut_ptr()), 2);
86 + assert_eq!(varint, [128, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
87 +
88 + assert_eq!(encode_varint(255, varint.as_mut_slice().as_mut_ptr()), 2);
89 + assert_eq!(varint, [128, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
90 + }
91 + }
92 +}