pub const MASK_U8_REV: [u8; 9] = [
0b1111_1111,
0b111_1111,
0b11_1111,
0b1_1111,
0b1111,
0b111,
0b11,
0b1,
0,
];
pub const MASK_U8_REV_BIT: [u8; 9] = [
0, 0b10000000, 0b11000000, 0b11100000, 0b11110000, 0b11111000, 0b11111100, 0b11111110,
0b11111111,
];
pub const CLEAR_U8: [u8; 9] = [
0b1111_1111,
0b1111_1110,
0b1111_1100,
0b1111_1000,
0b1111_0000,
0b1110_0000,
0b1100_0000,
0b1000_0000,
0b0000_0000,
];
#[macro_export]
macro_rules! fast_copy_code {
($target: ident, $target_start: ident, $src: ident, $src_start: ident, $src_stop:ident, $($ty:ty),*) => {
let target_byte = (*$target_start / 8) as isize;
let target_start_remainder = *$target_start % 8;
let src_start_remainder = $src_start % 8;
let src_start_byte = ($src_start / 8) as isize;
let src_stop_byte = ($src_stop - 1) / 8;
let total_bytes = src_stop_byte + 1 - src_start_byte as usize;
if target_start_remainder < src_start_remainder {
let lsh = (src_start_remainder - target_start_remainder) % 8;
$(
if total_bytes >= std::mem::size_of::<$ty>() {
let mut number: $ty = (*($src.offset(src_start_byte as isize) as *const $ty)).to_be() << lsh;
let hb = (&mut number as *mut $ty as *mut u8).offset(std::mem::size_of::<$ty>() as isize - 1);
*hb &= MASK_U8_REV[target_start_remainder];
*hb |= (*$target.offset(target_byte)) & MASK_U8_REV_BIT[target_start_remainder];
*($target.offset(target_byte) as *mut $ty) = number.to_be();
let bits_copied = std::mem::size_of::<$ty>() * 8 - lsh - target_start_remainder;
*$target_start += bits_copied;
$src_start += bits_copied;
continue;
}
)*
} else {
let rsh = (target_start_remainder - src_start_remainder) % 8;
$(
if total_bytes >= std::mem::size_of::<$ty>() {
let mut number: $ty = (*($src.offset(src_start_byte as isize) as *const $ty)).to_be() >> rsh;
let hb = (&mut number as *mut $ty as *mut u8).offset(std::mem::size_of::<$ty>() as isize - 1);
*hb &= MASK_U8_REV[target_start_remainder];
*hb |= (*$target.offset(target_byte)) & MASK_U8_REV_BIT[target_start_remainder];
*($target.offset(target_byte as isize) as *mut $ty) = number.to_be();
let bits_copied = std::mem::size_of::<$ty>() * 8 - target_start_remainder;
*$target_start += bits_copied;
$src_start += bits_copied;
continue;
}
)*
}
}
}
#[macro_export]
macro_rules! bit_copy {
(
$target: ident,
$target_start: ident,
$src: ident,
$src_start: ident,
$bits: ident,
$($ty:ty),*
) => {
let src_stop = $src_start + $bits;
while $src_start < src_stop {
let target_byte = (*$target_start / 8) as isize;
let target_start_remainder = *$target_start % 8;
let src_start_remainder = $src_start % 8;
let src_start_byte = ($src_start / 8) as isize;
let src_stop_byte = (src_stop - 1) / 8;
let total_bytes = src_stop_byte + 1 - src_start_byte as usize;
if target_start_remainder < src_start_remainder {
let lsh = (src_start_remainder - target_start_remainder) % 8;
$(
if total_bytes >= std::mem::size_of::<$ty>() {
let mut number: $ty = (*($src.offset(src_start_byte as isize) as *const $ty)).to_be() << lsh;
let hb = (&mut number as *mut $ty as *mut u8).offset(std::mem::size_of::<$ty>() as isize - 1);
*hb &= MASK_U8_REV[target_start_remainder];
*hb |= (*$target.offset(target_byte)) & MASK_U8_REV_BIT[target_start_remainder];
*($target.offset(target_byte) as *mut $ty) = number.to_be();
let bits_copied = std::mem::size_of::<$ty>() * 8 - lsh - target_start_remainder;
*$target_start += bits_copied;
$src_start += bits_copied;
continue;
}
)*
} else {
let rsh = (target_start_remainder - src_start_remainder) % 8;
$(
if total_bytes >= std::mem::size_of::<$ty>() {
let mut number: $ty = (*($src.offset(src_start_byte as isize) as *const $ty)).to_be() >> rsh;
let hb = (&mut number as *mut $ty as *mut u8).offset(std::mem::size_of::<$ty>() as isize - 1);
*hb &= MASK_U8_REV[target_start_remainder];
*hb |= (*$target.offset(target_byte)) & MASK_U8_REV_BIT[target_start_remainder];
*($target.offset(target_byte as isize) as *mut $ty) = number.to_be();
let bits_copied = std::mem::size_of::<$ty>() * 8 - target_start_remainder;
*$target_start += bits_copied;
$src_start += bits_copied;
continue;
}
)*
}
}
if $src_start >= src_stop {
*$target.offset((*$target_start as isize - 1) / 8) &= CLEAR_U8[$src_start - src_stop];
}
};
}
#[inline(always)]
pub unsafe fn bit_copy(
target: *mut u8,
target_start: &mut usize,
src: *const u8,
mut src_start: usize,
bits: usize,
) {
let src_stop = src_start + bits;
while src_start < src_stop {
fast_copy_code!(
target,
target_start,
src,
src_start,
src_stop,
u128,
u64,
u32,
u16,
u8
);
}
if src_start >= src_stop {
*target.offset((*target_start as isize - 1) / 8) &= CLEAR_U8[src_start - src_stop];
}
}
#[test]
fn test_bit_copy_slice() {
let mut target = [0u8];
let src = [0x00, 0x00, 0x00, 0xff];
unsafe {
bit_copy(target.as_mut_ptr(), &mut 0, src.as_ptr(), 28, 4);
}
assert_eq!(0xf0, target[0]);
}
#[test]
fn test_bit_copy_slice_be() {
let mut target = [0xff; 20];
let src = [
0b1010_1010,
0b1111_0000,
0b0000_1111,
0b1100_1100,
0b0011_0011,
0b1010_1010,
0b1111_0000,
0b0000_1111,
0b1100_1100,
0b0011_0011,
0b1010_1010,
0b1111_0000,
0b0000_1111,
0b1100_1100,
0b0011_0011,
0b1010_1010,
0b1111_0000,
0b0000_1111,
0b1100_1100,
0b0011_0011,
];
unsafe {
bit_copy(target.as_mut_ptr(), &mut 4, src.as_ptr(), 0, 15 * 8);
}
assert_eq!(target[0], 0b11111010);
assert_eq!(target[1], 0b10101111);
assert_eq!(target[14], 0b11000011);
assert_eq!(target[15], 0b00110000);
let mut target = [0xfc; 20];
let src = [
0xff, 0x01, 0xff, 0x02, 0xff, 0x03, 0xff, 0x04, 0xff, 0x05, 0xff, 0x06, 0xff, 0x07, 0xff,
0x08, 0xff, 0xff,
];
unsafe {
bit_copy(target.as_mut_ptr(), &mut 6, src.as_ptr(), 1, 16 * 8 + 1);
}
assert_eq!(target[0], 0b11111111);
assert_eq!(target[1], 0b11111000);
assert_eq!(target[15], 0b11111000);
assert_eq!(target[16], 0b01000111);
assert_eq!(target[17], 0b11000000);
}