1
0
Fork 0
mirror of https://codeberg.org/icewind/bitbuffer.git synced 2026-06-03 16:44:06 +02:00
This commit is contained in:
Robin Appelman 2019-12-25 23:30:49 +01:00
commit c28c83d5f4
2 changed files with 62 additions and 40 deletions

View file

@ -43,7 +43,7 @@ fn perf_be(b: &mut Bencher) {
} }
#[bench] #[bench]
fn perf_f32(b: &mut Bencher) { fn perf_f32_be(b: &mut Bencher) {
let data = vec![1u8; 1024 * 1024 * 10]; let data = vec![1u8; 1024 * 1024 * 10];
let buffer = BitBuffer::new(data, BigEndian); let buffer = BitBuffer::new(data, BigEndian);
b.iter(|| { b.iter(|| {
@ -63,6 +63,27 @@ fn perf_f32(b: &mut Bencher) {
}); });
} }
#[bench]
fn perf_f32_le(b: &mut Bencher) {
let data = vec![1u8; 1024 * 1024 * 10];
let buffer = BitBuffer::new(data, LittleEndian);
b.iter(|| {
let mut pos = 0;
let len = buffer.bit_len();
let mut result: f32 = 0.0;
loop {
if pos + 32 > len {
break;
}
let num = buffer.read_float::<f32>(pos).unwrap();
result += num;
pos += 32;
}
assert_eq!(result, 0.00000000000000000000000000000006170106);
test::black_box(result);
});
}
const F64_RESULT: f64 = 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010156250477904244; const F64_RESULT: f64 = 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010156250477904244;
#[bench] #[bench]

View file

@ -86,38 +86,43 @@ where
} }
fn read_usize_bytes(&self, byte_index: usize) -> [u8; USIZE_SIZE] { fn read_usize_bytes(&self, byte_index: usize) -> [u8; USIZE_SIZE] {
if byte_index + USIZE_SIZE <= self.bytes.len() { if cfg!(feature = "unsafe") {
self.bytes[byte_index..byte_index + USIZE_SIZE] use std::mem::transmute;
.try_into() // panic instead of accessing out of bounds data when the caller didn't do it's job bounds checking
.unwrap() // due to the magic of branch prediction and this check "always" passing, the cost for this
// is below the point of being measurable by `cargo bench`
unsafe {
let ptr = self.bytes.as_ptr().add(byte_index);
*transmute::<_, &[u8; USIZE_SIZE]>(ptr)
}
} else { } else {
let mut bytes = [0; USIZE_SIZE]; if byte_index + USIZE_SIZE <= self.bytes.len() {
let copy_bytes = self.bytes.len() - byte_index; self.bytes[byte_index..byte_index + USIZE_SIZE]
bytes[0..copy_bytes].copy_from_slice(&self.bytes[byte_index..byte_index + copy_bytes]); .try_into()
bytes .unwrap()
} else {
let mut bytes = [0; USIZE_SIZE];
let copy_bytes = self.bytes.len() - byte_index;
bytes[0..copy_bytes]
.copy_from_slice(&self.bytes[byte_index..byte_index + copy_bytes]);
bytes
}
} }
} }
/// note that only the bottom USIZE - 1 bytes are usable
fn read_shifted_usize(&self, byte_index: usize, shift: usize) -> usize {
let raw_bytes: [u8; USIZE_SIZE] = self.read_usize_bytes(byte_index);
let raw_usize: usize = usize::from_le_bytes(raw_bytes);
raw_usize >> shift
}
fn read_usize(&self, position: usize, count: usize) -> usize { fn read_usize(&self, position: usize, count: usize) -> usize {
let byte_index = position / 8; let byte_index = position / 8;
let bit_offset = position & 7; let bit_offset = position & 7;
let usize_bit_size = size_of::<usize>() * 8; let usize_bit_size = size_of::<usize>() * 8;
let bytes: [u8; USIZE_SIZE] = if cfg!(feature = "unsafe") { let bytes: [u8; USIZE_SIZE] = self.read_usize_bytes(byte_index);
use std::mem::transmute;
// panic instead of accessing out of bounds data when the caller didn't do it's job bounds checking
// due to the magic of branch prediction and this check "always" passing, the cost for this
// is below the point of being measurable by `cargo bench`
assert!(position + count <= self.bit_len());
unsafe {
// this is safe here because we already have checks that we don't read past the slice
let ptr = self.bytes.as_ptr().add(byte_index);
*transmute::<_, &[u8; USIZE_SIZE]>(ptr)
}
} else {
self.read_usize_bytes(byte_index)
};
let container = if E::is_le() { let container = if E::is_le() {
usize::from_le_bytes(bytes) usize::from_le_bytes(bytes)
@ -359,21 +364,21 @@ where
let mut data = Vec::with_capacity(byte_count); let mut data = Vec::with_capacity(byte_count);
let mut byte_left = byte_count; let mut byte_left = byte_count;
let max_read = size_of::<usize>() - 1;
let mut read_pos = position / 8; let mut read_pos = position / 8;
while byte_left > 0 { while byte_left > USIZE_SIZE - 1 {
let raw_bytes: [u8; USIZE_SIZE] = self.read_usize_bytes(read_pos); let bytes = self.read_shifted_usize(read_pos, shift).to_le_bytes();
let raw_usize: usize = usize::from_le_bytes(raw_bytes); let read_bytes = USIZE_SIZE - 1;
let shifted = raw_usize >> shift;
let bytes: [u8; USIZE_SIZE] = shifted.to_le_bytes();
let read_bytes = min(byte_left, USIZE_SIZE - 1);
let usable_bytes = &bytes[0..read_bytes]; let usable_bytes = &bytes[0..read_bytes];
data.extend_from_slice(usable_bytes); data.extend_from_slice(usable_bytes);
read_pos += read_bytes; read_pos += read_bytes;
byte_left -= read_bytes; byte_left -= read_bytes;
} }
let bytes = self.read_shifted_usize(read_pos, shift).to_le_bytes();
let usable_bytes = &bytes[0..byte_left];
data.extend_from_slice(usable_bytes);
Ok(data) Ok(data)
} }
@ -437,21 +442,17 @@ where
#[inline] #[inline]
fn read_string_bytes(&self, position: usize) -> Result<Vec<u8>> { fn read_string_bytes(&self, position: usize) -> Result<Vec<u8>> {
let shift = position & 7; let shift = position & 7;
if shift == 0 { if false && shift == 0 {
let byte_index = position / 8; let byte_index = position / 8;
Ok(self.bytes[byte_index..self.find_null_byte(byte_index)].to_vec()) Ok(self.bytes[byte_index..self.find_null_byte(byte_index)].to_vec())
} else { } else {
let mut acc = Vec::with_capacity(32); let mut acc = Vec::with_capacity(32);
let mut pos = position; let mut byte_index = position / 8;
loop { loop {
// note: if less then a usize worth of data is left in the buffer, read_usize_bytes // note: if less then a usize worth of data is left in the buffer, read_usize_bytes
// will automatically pad with null bytes, triggering the loop termination // will automatically pad with null bytes, triggering the loop termination
// thus no separate logic for dealing with the end of the bytes is required // thus no separate logic for dealing with the end of the bytes is required
let shifted = self.read_shifted_usize(byte_index, shift);
let byte_index = pos / 8;
let raw_bytes: [u8; USIZE_SIZE] = self.read_usize_bytes(byte_index);
let raw_usize: usize = usize::from_le_bytes(raw_bytes);
let shifted = raw_usize >> shift;
let has_null = contains_zero_byte_non_top(shifted); let has_null = contains_zero_byte_non_top(shifted);
let bytes: [u8; USIZE_SIZE] = shifted.to_le_bytes(); let bytes: [u8; USIZE_SIZE] = shifted.to_le_bytes();
@ -468,7 +469,7 @@ where
acc.extend_from_slice(&usable_bytes[0..USIZE_SIZE - 1]); acc.extend_from_slice(&usable_bytes[0..USIZE_SIZE - 1]);
pos += (USIZE_SIZE - 1) * 8; byte_index += USIZE_SIZE - 1;
} }
} }
} }