8
8
// option. This file may not be copied, modified, or distributed
9
9
// except according to those terms.
10
10
11
+ use std:: cmp;
12
+
11
13
#[ inline]
12
14
fn write_to_vec ( vec : & mut Vec < u8 > , position : usize , byte : u8 ) {
13
15
if position == vec. len ( ) {
@@ -18,56 +20,95 @@ fn write_to_vec(vec: &mut Vec<u8>, position: usize, byte: u8) {
18
20
}
19
21
20
22
#[ inline]
21
- /// encodes an integer using unsigned leb128 encoding and stores
22
- /// the result using a callback function.
23
- ///
24
- /// The callback `write` is called once for each position
25
- /// that is to be written to with the byte to be encoded
26
- /// at that position.
27
- pub fn write_unsigned_leb128_to < W > ( mut value : u128 , mut write : W ) -> usize
28
- where W : FnMut ( usize , u8 )
29
- {
30
- let mut position = 0 ;
31
- loop {
32
- let mut byte = ( value & 0x7F ) as u8 ;
33
- value >>= 7 ;
34
- if value != 0 {
35
- byte |= 0x80 ;
36
- }
37
-
38
- write ( position, byte) ;
39
- position += 1 ;
23
+ fn write_slice_to_vec ( output : & mut Vec < u8 > , start_position : usize , input : & [ u8 ] ) {
24
+ let input_len = input. len ( ) ;
25
+ let capacity = output. len ( ) - start_position;
26
+ let first_half = cmp:: min ( capacity, input_len) ;
40
27
41
- if value == 0 {
42
- break ;
43
- }
28
+ if first_half > 0 {
29
+ ( & mut output[ start_position..] ) . copy_from_slice ( & input[ .. first_half] ) ;
44
30
}
45
31
46
- position
32
+ if first_half < input_len {
33
+ output. extend_from_slice ( & input[ first_half..] ) ;
34
+ }
47
35
}
48
36
49
- pub fn write_unsigned_leb128 ( out : & mut Vec < u8 > , start_position : usize , value : u128 ) -> usize {
50
- write_unsigned_leb128_to ( value, |i, v| write_to_vec ( out, start_position+i, v) )
37
+ #[ cfg( target_pointer_width = "32" ) ]
38
+ const USIZE_LEB128_SIZE : usize = 5 ;
39
+ #[ cfg( target_pointer_width = "64" ) ]
40
+ const USIZE_LEB128_SIZE : usize = 10 ;
41
+
42
+ macro_rules! leb128_size {
43
+ ( u16 ) => ( 3 ) ;
44
+ ( u32 ) => ( 5 ) ;
45
+ ( u64 ) => ( 10 ) ;
46
+ ( u128 ) => ( 19 ) ;
47
+ ( usize ) => ( USIZE_LEB128_SIZE ) ;
51
48
}
52
49
53
- #[ inline]
54
- pub fn read_unsigned_leb128 ( data : & [ u8 ] , start_position : usize ) -> ( u128 , usize ) {
55
- let mut result = 0 ;
56
- let mut shift = 0 ;
57
- let mut position = start_position;
58
- loop {
59
- let byte = data[ position] ;
60
- position += 1 ;
61
- result |= ( ( byte & 0x7F ) as u128 ) << shift;
62
- if ( byte & 0x80 ) == 0 {
63
- break ;
50
+ macro_rules! impl_write_unsigned_leb128 {
51
+ ( $fn_name: ident, $int_ty: ident) => (
52
+ #[ inline]
53
+ pub fn $fn_name( out: & mut Vec <u8 >, start_position: usize , mut value: $int_ty) -> usize {
54
+ let mut encoded = [ 0u8 ; leb128_size!( $int_ty) ] ;
55
+
56
+ for i in 0 .. leb128_size!( $int_ty) {
57
+ encoded[ i] = ( value as u8 ) & 0b0111_1111 ;
58
+ value = value >> 7 ;
59
+
60
+ if value == 0 {
61
+ let bytes_written = i + 1 ;
62
+ write_slice_to_vec( out, start_position, & encoded[ 0 .. bytes_written] ) ;
63
+ return bytes_written
64
+ } else {
65
+ encoded[ i] |= 0b1000_0000 ;
66
+ }
67
+ }
68
+
69
+ unreachable!( )
64
70
}
65
- shift += 7 ;
66
- }
71
+ )
72
+ }
67
73
68
- ( result, position - start_position)
74
+ impl_write_unsigned_leb128 ! ( write_u16_leb128, u16 ) ;
75
+ impl_write_unsigned_leb128 ! ( write_u32_leb128, u32 ) ;
76
+ impl_write_unsigned_leb128 ! ( write_u64_leb128, u64 ) ;
77
+ impl_write_unsigned_leb128 ! ( write_u128_leb128, u128 ) ;
78
+ impl_write_unsigned_leb128 ! ( write_usize_leb128, usize ) ;
79
+
80
+ macro_rules! impl_read_unsigned_leb128 {
81
+ ( $fn_name: ident, $int_ty: ident) => (
82
+ #[ inline]
83
+ pub fn $fn_name( data: & [ u8 ] , start_position: usize ) -> ( $int_ty, usize ) {
84
+ // Copy things into a fixed size buffer so we can skip the bounds check
85
+ let mut bytes = [ 0u8 ; leb128_size!( $int_ty) ] ;
86
+ let copy_len = cmp:: min( data. len( ) - start_position, leb128_size!( $int_ty) ) ;
87
+ ( & mut bytes[ 0 .. copy_len] )
88
+ . copy_from_slice( & data[ start_position .. start_position + copy_len] ) ;
89
+
90
+ let mut result = 0 ;
91
+
92
+ for i in 0 .. leb128_size!( $int_ty) {
93
+ let byte = bytes[ i] ;
94
+ result |= ( ( byte & 0b0111_1111 ) as $int_ty) << i * 7 ;
95
+
96
+ if ( byte & 0b1000_0000 ) == 0 {
97
+ return ( result, i + 1 ) ;
98
+ }
99
+ }
100
+
101
+ unreachable!( )
102
+ }
103
+ )
69
104
}
70
105
106
+ impl_read_unsigned_leb128 ! ( read_u16_leb128, u16 ) ;
107
+ impl_read_unsigned_leb128 ! ( read_u32_leb128, u32 ) ;
108
+ impl_read_unsigned_leb128 ! ( read_u64_leb128, u64 ) ;
109
+ impl_read_unsigned_leb128 ! ( read_u128_leb128, u128 ) ;
110
+ impl_read_unsigned_leb128 ! ( read_usize_leb128, usize ) ;
111
+
71
112
#[ inline]
72
113
/// encodes an integer using signed leb128 encoding and stores
73
114
/// the result using a callback function.
@@ -130,26 +171,36 @@ pub fn read_signed_leb128(data: &[u8], start_position: usize) -> (i128, usize) {
130
171
( result, position - start_position)
131
172
}
132
173
133
- #[ test]
134
- fn test_unsigned_leb128 ( ) {
135
- let mut stream = Vec :: with_capacity ( 10000 ) ;
136
-
137
- for x in 0 ..62 {
138
- let pos = stream. len ( ) ;
139
- let bytes_written = write_unsigned_leb128 ( & mut stream, pos, 3 << x) ;
140
- assert_eq ! ( stream. len( ) , pos + bytes_written) ;
141
- }
142
-
143
- let mut position = 0 ;
144
- for x in 0 ..62 {
145
- let expected = 3 << x;
146
- let ( actual, bytes_read) = read_unsigned_leb128 ( & stream, position) ;
147
- assert_eq ! ( expected, actual) ;
148
- position += bytes_read;
149
- }
150
- assert_eq ! ( stream. len( ) , position) ;
174
+ macro_rules! impl_test_unsigned_leb128 {
175
+ ( $test_name: ident, $write_fn_name: ident, $read_fn_name: ident, $int_ty: ident) => (
176
+ #[ test]
177
+ fn $test_name( ) {
178
+ let mut stream = Vec :: new( ) ;
179
+
180
+ for x in 0 ..62 {
181
+ let pos = stream. len( ) ;
182
+ let bytes_written = $write_fn_name( & mut stream, pos, ( 3u64 << x) as $int_ty) ;
183
+ assert_eq!( stream. len( ) , pos + bytes_written) ;
184
+ }
185
+
186
+ let mut position = 0 ;
187
+ for x in 0 ..62 {
188
+ let expected = ( 3u64 << x) as $int_ty;
189
+ let ( actual, bytes_read) = $read_fn_name( & stream, position) ;
190
+ assert_eq!( expected, actual) ;
191
+ position += bytes_read;
192
+ }
193
+ assert_eq!( stream. len( ) , position) ;
194
+ }
195
+ )
151
196
}
152
197
198
+ impl_test_unsigned_leb128 ! ( test_u16_leb128, write_u16_leb128, read_u16_leb128, u16 ) ;
199
+ impl_test_unsigned_leb128 ! ( test_u32_leb128, write_u32_leb128, read_u32_leb128, u32 ) ;
200
+ impl_test_unsigned_leb128 ! ( test_u64_leb128, write_u64_leb128, read_u64_leb128, u64 ) ;
201
+ impl_test_unsigned_leb128 ! ( test_u128_leb128, write_u128_leb128, read_u128_leb128, u128 ) ;
202
+ impl_test_unsigned_leb128 ! ( test_usize_leb128, write_usize_leb128, read_usize_leb128, usize ) ;
203
+
153
204
#[ test]
154
205
fn test_signed_leb128 ( ) {
155
206
let values: Vec < _ > = ( -500 ..500 ) . map ( |i| i * 0x12345789ABCDEF ) . collect ( ) ;
0 commit comments