varint: use explicit width for integers

The varint subsystem currently uses implicit widths for integers. On the one hand we use `uintmax_t` for the actual value. On the other hand, we use `int` for the length of the encoded varint. Both of these have known maximum values, as we only support at most 16 bytes when encoding varints. Thus, we know that we won't ever exceed `uint64_t` for the actual value and `uint8_t` for the prefix length. Refactor the code to use explicit widths. Besides making the logic platform-independent, it also makes our life a bit easier in the next commit, where we reimplement "varint.c" in Rust. Suggested-by: Ezekiel Newren <ezekielnewren@gmail.com> 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 f366bfe16b350240c70c487d180c76ddcb8a1b2d
4 files changed +19 -15
dir.c
+10 -8
@@ -3579,7 +3579,8 @@ static void write_one_dir(struct untracked_cache_dir *untracked,
3579 struct stat_data stat_data;
3580 struct strbuf *out = &wd->out;
3581 unsigned char intbuf[16];
3582 - unsigned int intlen, value;
3582 + unsigned int value;
3583 + uint8_t intlen;
3584 int i = wd->index++;
3585
3586 /*
@@ -3632,7 +3633,7 @@ void write_untracked_extension(struct strbuf *out, struct untracked_cache *untra
3633 struct ondisk_untracked_cache *ouc;
3634 struct write_data wd;
3635 unsigned char varbuf[16];
3635 - int varint_len;
3636 + uint8_t varint_len;
3637 const unsigned hashsz = the_hash_algo->rawsz;
3638
3639 CALLOC_ARRAY(ouc, 1);
@@ -3738,7 +3739,7 @@ static int read_one_dir(struct untracked_cache_dir **untracked_,
3739 struct untracked_cache_dir ud, *untracked;
3740 const unsigned char *data = rd->data, *end = rd->end;
3741 const unsigned char *eos;
3741 - unsigned int value;
3742 + uint64_t value;
3743 int i;
3744
3745 memset(&ud, 0, sizeof(ud));
@@ -3830,7 +3831,8 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
3831 struct read_data rd;
3832 const unsigned char *next = data, *end = (const unsigned char *)data + sz;
3833 const char *ident;
3833 - int ident_len;
3834 + uint64_t ident_len;
3835 + uint64_t varint_len;
3836 ssize_t len;
3837 const char *exclude_per_dir;
3838 const unsigned hashsz = the_hash_algo->rawsz;
@@ -3867,8 +3869,8 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
3869 if (next >= end)
3870 goto done2;
3871
3870 - len = decode_varint(&next);
3871 - if (next > end || len == 0)
3872 + varint_len = decode_varint(&next);
3873 + if (next > end || varint_len == 0)
3874 goto done2;
3875
3876 rd.valid = ewah_new();
@@ -3877,9 +3879,9 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
3879 rd.data = next;
3880 rd.end = end;
3881 rd.index = 0;
3880 - ALLOC_ARRAY(rd.ucd, len);
3882 + ALLOC_ARRAY(rd.ucd, varint_len);
3883
3882 - if (read_one_dir(&uc->root, &rd) || rd.index != len)
3884 + if (read_one_dir(&uc->root, &rd) || rd.index != varint_len)
3885 goto done;
3886
3887 next = rd.data;
read-cache.c
+4 -2
@@ -1807,7 +1807,7 @@ static struct cache_entry *create_from_disk(struct mem_pool *ce_mem_pool,
1807
1808 if (expand_name_field) {
1809 const unsigned char *cp = (const unsigned char *)name;
1810 - size_t strip_len, previous_len;
1810 + uint64_t strip_len, previous_len;
1811
1812 /* If we're at the beginning of a block, ignore the previous name */
1813 strip_len = decode_varint(&cp);
@@ -2655,8 +2655,10 @@ static int ce_write_entry(struct hashfile *f, struct cache_entry *ce,
2655 hashwrite(f, ce->name, len);
2656 hashwrite(f, padding, align_padding_size(size, len));
2657 } else {
2658 - int common, to_remove, prefix_size;
2658 + int common, to_remove;
2659 + uint8_t prefix_size;
2660 unsigned char to_remove_vi[16];
2661 +
2662 for (common = 0;
2663 (common < previous_name->len &&
2664 ce->name[common] &&
varint.c
+3 -3
@@ -1,11 +1,11 @@
1 #include "git-compat-util.h"
2 #include "varint.h"
3
4 -uintmax_t decode_varint(const unsigned char **bufp)
4 +uint64_t decode_varint(const unsigned char **bufp)
5 {
6 const unsigned char *buf = *bufp;
7 unsigned char c = *buf++;
8 - uintmax_t val = c & 127;
8 + uint64_t val = c & 127;
9 while (c & 128) {
10 val += 1;
11 if (!val || MSB(val, 7))
@@ -17,7 +17,7 @@ uintmax_t decode_varint(const unsigned char **bufp)
17 return val;
18 }
19
20 -int encode_varint(uintmax_t value, unsigned char *buf)
20 +uint8_t encode_varint(uint64_t value, unsigned char *buf)
21 {
22 unsigned char varint[16];
23 unsigned pos = sizeof(varint) - 1;
varint.h
+2 -2
@@ -1,7 +1,7 @@
1 #ifndef VARINT_H
2 #define VARINT_H
3
4 -int encode_varint(uintmax_t, unsigned char *);
5 -uintmax_t decode_varint(const unsigned char **);
4 +uint8_t encode_varint(uint64_t, unsigned char *);
5 +uint64_t decode_varint(const unsigned char **);
6
7 #endif /* VARINT_H */