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