ra_ap_rustc_serialize/
leb128.rs

1// This code is very hot and uses lots of arithmetic, avoid overflow checks for performance.
2// See https://github.com/rust-lang/rust/pull/119440#issuecomment-1874255727
3use crate::int_overflow::DebugStrictAdd;
4use crate::opaque::MemDecoder;
5use crate::serialize::Decoder;
6
7/// Returns the length of the longest LEB128 encoding for `T`, assuming `T` is an integer type
8pub const fn max_leb128_len<T>() -> usize {
9    // The longest LEB128 encoding for an integer uses 7 bits per byte.
10    (std::mem::size_of::<T>() * 8 + 6) / 7
11}
12
13/// Returns the length of the longest LEB128 encoding of all supported integer types.
14pub const fn largest_max_leb128_len() -> usize {
15    max_leb128_len::<u128>()
16}
17
18macro_rules! impl_write_unsigned_leb128 {
19    ($fn_name:ident, $int_ty:ty) => {
20        #[inline]
21        pub fn $fn_name(out: &mut [u8; max_leb128_len::<$int_ty>()], mut value: $int_ty) -> usize {
22            let mut i = 0;
23
24            loop {
25                if value < 0x80 {
26                    unsafe {
27                        *out.get_unchecked_mut(i) = value as u8;
28                    }
29
30                    i = i.debug_strict_add(1);
31                    break;
32                } else {
33                    unsafe {
34                        *out.get_unchecked_mut(i) = ((value & 0x7f) | 0x80) as u8;
35                    }
36
37                    value >>= 7;
38                    i = i.debug_strict_add(1);
39                }
40            }
41
42            i
43        }
44    };
45}
46
47impl_write_unsigned_leb128!(write_u16_leb128, u16);
48impl_write_unsigned_leb128!(write_u32_leb128, u32);
49impl_write_unsigned_leb128!(write_u64_leb128, u64);
50impl_write_unsigned_leb128!(write_u128_leb128, u128);
51impl_write_unsigned_leb128!(write_usize_leb128, usize);
52
53macro_rules! impl_read_unsigned_leb128 {
54    ($fn_name:ident, $int_ty:ty) => {
55        #[inline]
56        pub fn $fn_name(decoder: &mut MemDecoder<'_>) -> $int_ty {
57            // The first iteration of this loop is unpeeled. This is a
58            // performance win because this code is hot and integer values less
59            // than 128 are very common, typically occurring 50-80% or more of
60            // the time, even for u64 and u128.
61            let byte = decoder.read_u8();
62            if (byte & 0x80) == 0 {
63                return byte as $int_ty;
64            }
65            let mut result = (byte & 0x7F) as $int_ty;
66            let mut shift = 7;
67            loop {
68                let byte = decoder.read_u8();
69                if (byte & 0x80) == 0 {
70                    result |= (byte as $int_ty) << shift;
71                    return result;
72                } else {
73                    result |= ((byte & 0x7F) as $int_ty) << shift;
74                }
75                shift = shift.debug_strict_add(7);
76            }
77        }
78    };
79}
80
81impl_read_unsigned_leb128!(read_u16_leb128, u16);
82impl_read_unsigned_leb128!(read_u32_leb128, u32);
83impl_read_unsigned_leb128!(read_u64_leb128, u64);
84impl_read_unsigned_leb128!(read_u128_leb128, u128);
85impl_read_unsigned_leb128!(read_usize_leb128, usize);
86
87macro_rules! impl_write_signed_leb128 {
88    ($fn_name:ident, $int_ty:ty) => {
89        #[inline]
90        pub fn $fn_name(out: &mut [u8; max_leb128_len::<$int_ty>()], mut value: $int_ty) -> usize {
91            let mut i = 0;
92
93            loop {
94                let mut byte = (value as u8) & 0x7f;
95                value >>= 7;
96                let more = !(((value == 0) && ((byte & 0x40) == 0))
97                    || ((value == -1) && ((byte & 0x40) != 0)));
98
99                if more {
100                    byte |= 0x80; // Mark this byte to show that more bytes will follow.
101                }
102
103                unsafe {
104                    *out.get_unchecked_mut(i) = byte;
105                }
106
107                i = i.debug_strict_add(1);
108
109                if !more {
110                    break;
111                }
112            }
113
114            i
115        }
116    };
117}
118
119impl_write_signed_leb128!(write_i16_leb128, i16);
120impl_write_signed_leb128!(write_i32_leb128, i32);
121impl_write_signed_leb128!(write_i64_leb128, i64);
122impl_write_signed_leb128!(write_i128_leb128, i128);
123impl_write_signed_leb128!(write_isize_leb128, isize);
124
125macro_rules! impl_read_signed_leb128 {
126    ($fn_name:ident, $int_ty:ty) => {
127        #[inline]
128        pub fn $fn_name(decoder: &mut MemDecoder<'_>) -> $int_ty {
129            let mut result = 0;
130            let mut shift = 0;
131            let mut byte;
132
133            loop {
134                byte = decoder.read_u8();
135                result |= <$int_ty>::from(byte & 0x7F) << shift;
136                shift = shift.debug_strict_add(7);
137
138                if (byte & 0x80) == 0 {
139                    break;
140                }
141            }
142
143            if (shift < <$int_ty>::BITS) && ((byte & 0x40) != 0) {
144                // sign extend
145                result |= (!0 << shift);
146            }
147
148            result
149        }
150    };
151}
152
153impl_read_signed_leb128!(read_i16_leb128, i16);
154impl_read_signed_leb128!(read_i32_leb128, i32);
155impl_read_signed_leb128!(read_i64_leb128, i64);
156impl_read_signed_leb128!(read_i128_leb128, i128);
157impl_read_signed_leb128!(read_isize_leb128, isize);