]> git.lizzy.rs Git - rust.git/blob - src/libserialize/leb128.rs
Suggest defining type parameter when appropriate
[rust.git] / src / libserialize / leb128.rs
1 #[inline]
2 pub fn write_to_vec(vec: &mut Vec<u8>, byte: u8) {
3     vec.push(byte);
4 }
5
6 #[cfg(target_pointer_width = "32")]
7 const USIZE_LEB128_SIZE: usize = 5;
8 #[cfg(target_pointer_width = "64")]
9 const USIZE_LEB128_SIZE: usize = 10;
10
11 macro_rules! leb128_size {
12     (u16) => {
13         3
14     };
15     (u32) => {
16         5
17     };
18     (u64) => {
19         10
20     };
21     (u128) => {
22         19
23     };
24     (usize) => {
25         USIZE_LEB128_SIZE
26     };
27 }
28
29 macro_rules! impl_write_unsigned_leb128 {
30     ($fn_name:ident, $int_ty:ident) => {
31         #[inline]
32         pub fn $fn_name(out: &mut Vec<u8>, mut value: $int_ty) {
33             for _ in 0..leb128_size!($int_ty) {
34                 let mut byte = (value & 0x7F) as u8;
35                 value >>= 7;
36                 if value != 0 {
37                     byte |= 0x80;
38                 }
39
40                 write_to_vec(out, byte);
41
42                 if value == 0 {
43                     break;
44                 }
45             }
46         }
47     };
48 }
49
50 impl_write_unsigned_leb128!(write_u16_leb128, u16);
51 impl_write_unsigned_leb128!(write_u32_leb128, u32);
52 impl_write_unsigned_leb128!(write_u64_leb128, u64);
53 impl_write_unsigned_leb128!(write_u128_leb128, u128);
54 impl_write_unsigned_leb128!(write_usize_leb128, usize);
55
56 macro_rules! impl_read_unsigned_leb128 {
57     ($fn_name:ident, $int_ty:ident) => {
58         #[inline]
59         pub fn $fn_name(slice: &[u8]) -> ($int_ty, usize) {
60             let mut result: $int_ty = 0;
61             let mut shift = 0;
62             let mut position = 0;
63
64             for _ in 0..leb128_size!($int_ty) {
65                 let byte = unsafe { *slice.get_unchecked(position) };
66                 position += 1;
67                 result |= ((byte & 0x7F) as $int_ty) << shift;
68                 if (byte & 0x80) == 0 {
69                     break;
70                 }
71                 shift += 7;
72             }
73
74             // Do a single bounds check at the end instead of for every byte.
75             assert!(position <= slice.len());
76
77             (result, position)
78         }
79     };
80 }
81
82 impl_read_unsigned_leb128!(read_u16_leb128, u16);
83 impl_read_unsigned_leb128!(read_u32_leb128, u32);
84 impl_read_unsigned_leb128!(read_u64_leb128, u64);
85 impl_read_unsigned_leb128!(read_u128_leb128, u128);
86 impl_read_unsigned_leb128!(read_usize_leb128, usize);
87
88 #[inline]
89 /// encodes an integer using signed leb128 encoding and stores
90 /// the result using a callback function.
91 ///
92 /// The callback `write` is called once for each position
93 /// that is to be written to with the byte to be encoded
94 /// at that position.
95 pub fn write_signed_leb128_to<W>(mut value: i128, mut write: W)
96 where
97     W: FnMut(u8),
98 {
99     loop {
100         let mut byte = (value as u8) & 0x7f;
101         value >>= 7;
102         let more =
103             !(((value == 0) && ((byte & 0x40) == 0)) || ((value == -1) && ((byte & 0x40) != 0)));
104
105         if more {
106             byte |= 0x80; // Mark this byte to show that more bytes will follow.
107         }
108
109         write(byte);
110
111         if !more {
112             break;
113         }
114     }
115 }
116
117 #[inline]
118 pub fn write_signed_leb128(out: &mut Vec<u8>, value: i128) {
119     write_signed_leb128_to(value, |v| write_to_vec(out, v))
120 }
121
122 #[inline]
123 pub fn read_signed_leb128(data: &[u8], start_position: usize) -> (i128, usize) {
124     let mut result = 0;
125     let mut shift = 0;
126     let mut position = start_position;
127     let mut byte;
128
129     loop {
130         byte = data[position];
131         position += 1;
132         result |= i128::from(byte & 0x7F) << shift;
133         shift += 7;
134
135         if (byte & 0x80) == 0 {
136             break;
137         }
138     }
139
140     if (shift < 64) && ((byte & 0x40) != 0) {
141         // sign extend
142         result |= -(1 << shift);
143     }
144
145     (result, position - start_position)
146 }