Packs multiple bytes into one. All the bytes must be < `FACTOR`. Factors 2,4,16 are bit packing. Factors 3,6 are arithmetic coding.
(bytes: &[u8], out: &mut Vec<u8>)
| 389 | /// Packs multiple bytes into one. All the bytes must be < `FACTOR`. |
| 390 | /// Factors 2,4,16 are bit packing. Factors 3,6 are arithmetic coding. |
| 391 | fn pack_arithmetic<const FACTOR: usize>(bytes: &[u8], out: &mut Vec<u8>) { |
| 392 | debug_assert!(bytes.iter().all(|&v| v < FACTOR as u8)); |
| 393 | let divisor = factor_to_divisor::<FACTOR>(); |
| 394 | |
| 395 | let floor = bytes.len() / divisor; |
| 396 | let ceil = (bytes.len() + (divisor - 1)) / divisor; |
| 397 | |
| 398 | out.reserve(ceil); |
| 399 | let packed = &mut out.spare_capacity_mut()[..ceil]; |
| 400 | |
| 401 | for i in 0..floor { |
| 402 | unsafe { |
| 403 | packed.get_unchecked_mut(i).write(if FACTOR == 2 && BMI2 { |
| 404 | #[cfg(not(all(target_arch = "x86_64", target_feature = "bmi2")))] |
| 405 | unreachable!(); |
| 406 | #[cfg(all(target_arch = "x86_64", target_feature = "bmi2"))] |
| 407 | { |
| 408 | // Could use on any pow2 FACTOR, but only 2 is faster (target-cpu=native). |
| 409 | let chunk = (bytes.as_ptr() as *const u8 as *const [u8; 8]).add(i); |
| 410 | let chunk = u64::from_le_bytes(*chunk); |
| 411 | std::arch::x86_64::_pext_u64(chunk, 0x0101010101010101) as u8 |
| 412 | } |
| 413 | } else { |
| 414 | let mut acc = 0; |
| 415 | for byte_index in 0..divisor { |
| 416 | let byte = *bytes.get_unchecked(i * divisor + byte_index); |
| 417 | acc += byte * (FACTOR as u8).pow(byte_index as u32); |
| 418 | } |
| 419 | acc |
| 420 | }); |
| 421 | } |
| 422 | } |
| 423 | if floor < ceil { |
| 424 | let mut acc = 0; |
| 425 | for &v in bytes[floor * divisor..].iter().rev() { |
| 426 | acc *= FACTOR as u8; |
| 427 | acc += v; |
| 428 | } |
| 429 | packed[floor].write(acc); |
| 430 | } |
| 431 | // Safety: `ceil` elements after len were initialized by loops above. |
| 432 | unsafe { out.set_len(out.len() + ceil) }; |
| 433 | } |
| 434 | |
| 435 | /// Opposite of `pack_arithmetic`. `out` will be overwritten with the unpacked bytes. |
| 436 | fn unpack_arithmetic<const FACTOR: usize>( |