@@ -46,31 +46,48 @@ impl FlexUInt {
46
46
/// an appropriate error message if reading fails.
47
47
#[ inline( always) ]
48
48
pub fn read ( input : & [ u8 ] , offset : usize ) -> IonResult < FlexUInt > {
49
- const COMMON_CASE_INPUT_BYTES_NEEDED : usize = 8 ;
50
-
49
+ const COMMON_CASE_INPUT_BYTES_NEEDED : usize = 4 ;
51
50
// We want to minimize the number of branches that happen in the common case. To do this,
52
51
// we perform a single length check, making sure that the buffer contains enough data to
53
52
// represent a FlexUInt whose continuation bits fit in a single byte (i.e. one with 7 or
54
- // fewer bytes of magnitude). If the buffer doesn't have at least 8 bytes in it or the
55
- // FlexUInt we find requires more than 8 bytes to represent, we'll fall back to the general
53
+ // fewer bytes of magnitude). If the buffer doesn't have at least 4 bytes in it or the
54
+ // FlexUInt we find requires more than 4 bytes to represent, we'll fall back to the general
56
55
// case.
57
- if input. len ( ) < COMMON_CASE_INPUT_BYTES_NEEDED || input[ 0 ] == 0 {
58
- // Calling `read_flex_primitive_as_uint_no_inline` keeps this method small enough that
59
- // the code for the common case can be inlined.
60
- return Self :: read_flex_primitive_as_uint_no_inline (
61
- input,
62
- offset,
63
- "reading a FlexUInt" ,
64
- false ,
65
- ) ;
56
+ if input. len ( ) >= COMMON_CASE_INPUT_BYTES_NEEDED {
57
+ ' common_case: {
58
+ let num_encoded_bytes = input[ 0 ] . trailing_zeros ( ) as usize + 1 ;
59
+ // By branching on particular values, we make the value of `num_encoded_bytes` in their
60
+ // corresponding arm `const`. This allows us to use `read_n_bytes` to optimize for those
61
+ // sizes.
62
+ let flex_uint = match num_encoded_bytes {
63
+ 1 => Self :: read_n_bytes :: < 1 > ( input) ,
64
+ 2 => Self :: read_n_bytes :: < 2 > ( input) ,
65
+ 3 => Self :: read_n_bytes :: < 3 > ( input) ,
66
+ 4 => Self :: read_n_bytes :: < 4 > ( input) ,
67
+ _ => break ' common_case,
68
+ } ;
69
+ return Ok ( flex_uint) ;
70
+ }
66
71
}
67
- let flex_uint = Self :: read_small_flex_uint ( input) ;
68
- Ok ( flex_uint)
72
+ // Calling `read_flex_primitive_as_uint_no_inline` keeps this method small enough that
73
+ // the code for the common case can be inlined.
74
+ Self :: read_flex_primitive_as_uint_no_inline ( input, offset, "reading a FlexUInt" , false )
75
+ }
76
+
77
+ #[ inline]
78
+ pub fn read_n_bytes < const NUM_BYTES : usize > ( bytes : & [ u8 ] ) -> FlexUInt {
79
+ let input: [ u8 ; NUM_BYTES ] = * ( bytes. first_chunk :: < NUM_BYTES > ( ) . unwrap ( ) ) ;
80
+ let mut buffer = [ 0u8 ; size_of :: < u64 > ( ) ] ;
81
+ * buffer. first_chunk_mut :: < NUM_BYTES > ( ) . unwrap ( ) = input;
82
+ let value = u64:: from_le_bytes ( buffer)
83
+ . checked_shr ( NUM_BYTES as u32 )
84
+ . unwrap_or ( 0 ) ;
85
+ FlexUInt :: new ( NUM_BYTES , value)
69
86
}
70
87
71
88
/// Helper method that reads a [`FlexUInt`] with 7 or fewer bytes of magnitude from the buffer.
72
89
// Caller must confirm that `bytes` has at least 8 bytes.
73
- #[ inline]
90
+ #[ inline( never ) ]
74
91
fn read_small_flex_uint ( bytes : & [ u8 ] ) -> FlexUInt {
75
92
debug_assert ! ( bytes. len( ) >= 8 ) ;
76
93
let num_encoded_bytes = bytes[ 0 ] . trailing_zeros ( ) as usize + 1 ;
0 commit comments