Skip to content

Commit 04d267d

Browse files
committed
Reworks logic for FlexUInt::read
1 parent aa84382 commit 04d267d

File tree

1 file changed

+33
-16
lines changed

1 file changed

+33
-16
lines changed

src/lazy/encoder/binary/v1_1/flex_uint.rs

+33-16
Original file line numberDiff line numberDiff line change
@@ -46,31 +46,48 @@ impl FlexUInt {
4646
/// an appropriate error message if reading fails.
4747
#[inline(always)]
4848
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;
5150
// We want to minimize the number of branches that happen in the common case. To do this,
5251
// we perform a single length check, making sure that the buffer contains enough data to
5352
// 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
5655
// 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+
}
6671
}
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)
6986
}
7087

7188
/// Helper method that reads a [`FlexUInt`] with 7 or fewer bytes of magnitude from the buffer.
7289
// Caller must confirm that `bytes` has at least 8 bytes.
73-
#[inline]
90+
#[inline(never)]
7491
fn read_small_flex_uint(bytes: &[u8]) -> FlexUInt {
7592
debug_assert!(bytes.len() >= 8);
7693
let num_encoded_bytes = bytes[0].trailing_zeros() as usize + 1;

0 commit comments

Comments
 (0)