| // SPDX-License-Identifier: GPL-2.0 |
| |
| #include <linux/bitops.h> |
| #include <linux/math.h> |
| #include <linux/string.h> |
| #include <asm/unaligned.h> |
| |
| #ifdef CONFIG_VALGRIND |
| #include <valgrind/memcheck.h> |
| #endif |
| |
| #include "varint.h" |
| |
| /** |
| * bch2_varint_encode - encode a variable length integer |
| * @out: destination to encode to |
| * @v: unsigned integer to encode |
| * Returns: size in bytes of the encoded integer - at most 9 bytes |
| */ |
| int bch2_varint_encode(u8 *out, u64 v) |
| { |
| unsigned bits = fls64(v|1); |
| unsigned bytes = DIV_ROUND_UP(bits, 7); |
| __le64 v_le; |
| |
| if (likely(bytes < 9)) { |
| v <<= bytes; |
| v |= ~(~0 << (bytes - 1)); |
| v_le = cpu_to_le64(v); |
| memcpy(out, &v_le, bytes); |
| } else { |
| *out++ = 255; |
| bytes = 9; |
| put_unaligned_le64(v, out); |
| } |
| |
| return bytes; |
| } |
| |
| /** |
| * bch2_varint_decode - encode a variable length integer |
| * @in: varint to decode |
| * @end: end of buffer to decode from |
| * @out: on success, decoded integer |
| * Returns: size in bytes of the decoded integer - or -1 on failure (would |
| * have read past the end of the buffer) |
| */ |
| int bch2_varint_decode(const u8 *in, const u8 *end, u64 *out) |
| { |
| unsigned bytes = likely(in < end) |
| ? ffz(*in & 255) + 1 |
| : 1; |
| u64 v; |
| |
| if (unlikely(in + bytes > end)) |
| return -1; |
| |
| if (likely(bytes < 9)) { |
| __le64 v_le = 0; |
| |
| memcpy(&v_le, in, bytes); |
| v = le64_to_cpu(v_le); |
| v >>= bytes; |
| } else { |
| v = get_unaligned_le64(++in); |
| } |
| |
| *out = v; |
| return bytes; |
| } |
| |
| /** |
| * bch2_varint_encode_fast - fast version of bch2_varint_encode |
| * @out: destination to encode to |
| * @v: unsigned integer to encode |
| * Returns: size in bytes of the encoded integer - at most 9 bytes |
| * |
| * This version assumes it's always safe to write 8 bytes to @out, even if the |
| * encoded integer would be smaller. |
| */ |
| int bch2_varint_encode_fast(u8 *out, u64 v) |
| { |
| unsigned bits = fls64(v|1); |
| unsigned bytes = DIV_ROUND_UP(bits, 7); |
| |
| if (likely(bytes < 9)) { |
| v <<= bytes; |
| v |= ~(~0 << (bytes - 1)); |
| } else { |
| *out++ = 255; |
| bytes = 9; |
| } |
| |
| put_unaligned_le64(v, out); |
| return bytes; |
| } |
| |
| /** |
| * bch2_varint_decode_fast - fast version of bch2_varint_decode |
| * @in: varint to decode |
| * @end: end of buffer to decode from |
| * @out: on success, decoded integer |
| * Returns: size in bytes of the decoded integer - or -1 on failure (would |
| * have read past the end of the buffer) |
| * |
| * This version assumes that it is safe to read at most 8 bytes past the end of |
| * @end (we still return an error if the varint extends past @end). |
| */ |
| int bch2_varint_decode_fast(const u8 *in, const u8 *end, u64 *out) |
| { |
| #ifdef CONFIG_VALGRIND |
| VALGRIND_MAKE_MEM_DEFINED(in, 8); |
| #endif |
| u64 v = get_unaligned_le64(in); |
| unsigned bytes = ffz(*in) + 1; |
| |
| if (unlikely(in + bytes > end)) |
| return -1; |
| |
| if (likely(bytes < 9)) { |
| v >>= bytes; |
| v &= ~(~0ULL << (7 * bytes)); |
| } else { |
| v = get_unaligned_le64(++in); |
| } |
| |
| *out = v; |
| return bytes; |
| } |