]> git.sesse.net Git - bcachefs-tools-debian/blob - libbcachefs/varint.c
New upstream snapshot
[bcachefs-tools-debian] / libbcachefs / varint.c
1 // SPDX-License-Identifier: GPL-2.0
2
3 #include <linux/bitops.h>
4 #include <linux/string.h>
5 #include <asm/unaligned.h>
6
7 #ifdef CONFIG_VALGRIND
8 #include <valgrind/memcheck.h>
9 #endif
10
11 #include "varint.h"
12
13 /**
14  * bch2_varint_encode - encode a variable length integer
15  * @out - destination to encode to
16  * @v   - unsigned integer to encode
17  *
18  * Returns the size in bytes of the encoded integer - at most 9 bytes
19  */
20 int bch2_varint_encode(u8 *out, u64 v)
21 {
22         unsigned bits = fls64(v|1);
23         unsigned bytes = DIV_ROUND_UP(bits, 7);
24
25         if (likely(bytes < 9)) {
26                 v <<= bytes;
27                 v |= ~(~0 << (bytes - 1));
28                 v = cpu_to_le64(v);
29                 memcpy(out, &v, bytes);
30         } else {
31                 *out++ = 255;
32                 bytes = 9;
33                 put_unaligned_le64(v, out);
34         }
35
36         return bytes;
37 }
38
39 /**
40  * bch2_varint_decode - encode a variable length integer
41  * @in  - varint to decode
42  * @end - end of buffer to decode from
43  * @out - on success, decoded integer
44  *
45  * Returns the size in bytes of the decoded integer - or -1 on failure (would
46  * have read past the end of the buffer)
47  */
48 int bch2_varint_decode(const u8 *in, const u8 *end, u64 *out)
49 {
50         unsigned bytes = likely(in < end)
51                 ? ffz(*in & 255) + 1
52                 : 1;
53         u64 v;
54
55         if (unlikely(in + bytes > end))
56                 return -1;
57
58         if (likely(bytes < 9)) {
59                 v = 0;
60                 memcpy(&v, in, bytes);
61                 v = le64_to_cpu(v);
62                 v >>= bytes;
63         } else {
64                 v = get_unaligned_le64(++in);
65         }
66
67         *out = v;
68         return bytes;
69 }
70
71 /**
72  * bch2_varint_encode_fast - fast version of bch2_varint_encode
73  *
74  * This version assumes it's always safe to write 8 bytes to @out, even if the
75  * encoded integer would be smaller.
76  */
77 int bch2_varint_encode_fast(u8 *out, u64 v)
78 {
79         unsigned bits = fls64(v|1);
80         unsigned bytes = DIV_ROUND_UP(bits, 7);
81
82         if (likely(bytes < 9)) {
83                 v <<= bytes;
84                 v |= ~(~0 << (bytes - 1));
85         } else {
86                 *out++ = 255;
87                 bytes = 9;
88         }
89
90         put_unaligned_le64(v, out);
91         return bytes;
92 }
93
94 /**
95  * bch2_varint_decode_fast - fast version of bch2_varint_decode
96  *
97  * This version assumes that it is safe to read at most 8 bytes past the end of
98  * @end (we still return an error if the varint extends past @end).
99  */
100 int bch2_varint_decode_fast(const u8 *in, const u8 *end, u64 *out)
101 {
102 #ifdef CONFIG_VALGRIND
103         VALGRIND_MAKE_MEM_DEFINED(in, 8);
104 #endif
105         u64 v = get_unaligned_le64(in);
106         unsigned bytes = ffz(*in) + 1;
107
108         if (unlikely(in + bytes > end))
109                 return -1;
110
111         if (likely(bytes < 9)) {
112                 v >>= bytes;
113                 v &= ~(~0ULL << (7 * bytes));
114         } else {
115                 v = get_unaligned_le64(++in);
116         }
117
118         *out = v;
119         return bytes;
120 }