1use rudb_common::{Error, Result};
45
46pub const VALUES: usize = 1024;
48
49const ORDER: [usize; 8] = [0, 4, 2, 6, 1, 5, 3, 7];
51
52mod sealed {
53 pub trait Sealed {}
54 impl Sealed for u8 {}
55 impl Sealed for u16 {}
56 impl Sealed for u32 {}
57 impl Sealed for u64 {}
58}
59
60pub trait Packable: sealed::Sealed + Copy + Ord + std::fmt::Debug {
66 const WIDTH: usize;
68 const LANES: usize = VALUES / Self::WIDTH;
70
71 fn to_u64(self) -> u64;
73 fn from_u64(value: u64) -> Self;
75}
76
77macro_rules! impl_packable {
78 ($($ty:ty),*) => {$(
79 impl Packable for $ty {
80 const WIDTH: usize = <$ty>::BITS as usize;
81
82 #[inline]
83 fn to_u64(self) -> u64 {
84 u64::from(self)
85 }
86
87 #[inline]
88 fn from_u64(value: u64) -> Self {
89 value as $ty
90 }
91 }
92 )*};
93}
94
95impl_packable!(u8, u16, u32, u64);
96
97#[inline]
99const fn low_mask(bits: usize) -> u64 {
100 if bits >= 64 { u64::MAX } else { (1u64 << bits) - 1 }
101}
102
103#[inline]
105const fn shift_right(value: u64, bits: usize) -> u64 {
106 if bits >= 64 { 0 } else { value >> bits }
107}
108
109#[inline]
120#[must_use]
121pub fn source_index<T: Packable>(row: usize, lane: usize) -> usize {
122 assert!(row < T::WIDTH, "row {row} is outside a {} bit type", T::WIDTH);
123 assert!(lane < T::LANES, "lane {lane} is outside {} lanes", T::LANES);
124 let group_size = T::WIDTH / 8;
125 let group = row / group_size;
126 let offset = row % group_size;
127 ((offset * 8) + ORDER[group]) * T::LANES + lane
128}
129
130pub fn transpose<T: Packable>(input: &[T], output: &mut [T]) -> Result<()> {
136 check_vector_len(input.len(), "input")?;
137 check_vector_len(output.len(), "output")?;
138 for row in 0..T::WIDTH {
139 for lane in 0..T::LANES {
140 output[row * T::LANES + lane] = input[source_index::<T>(row, lane)];
141 }
142 }
143 Ok(())
144}
145
146pub fn untranspose<T: Packable>(input: &[T], output: &mut [T]) -> Result<()> {
152 check_vector_len(input.len(), "input")?;
153 check_vector_len(output.len(), "output")?;
154 for row in 0..T::WIDTH {
155 for lane in 0..T::LANES {
156 output[source_index::<T>(row, lane)] = input[row * T::LANES + lane];
157 }
158 }
159 Ok(())
160}
161
162#[must_use]
167pub fn packed_len<T: Packable>(width: usize) -> usize {
168 width * T::LANES
169}
170
171#[must_use]
174pub fn required_width<T: Packable>(values: &[T]) -> usize {
175 let max = values.iter().copied().max().map_or(0, T::to_u64);
176 (64 - max.leading_zeros()) as usize
177}
178
179pub fn pack_transposed<T: Packable>(input: &[T], width: usize, output: &mut [T]) -> Result<()> {
190 check_vector_len(input.len(), "input")?;
191 check_width::<T>(width)?;
192 if output.len() != packed_len::<T>(width) {
193 return Err(Error::internal(format!(
194 "a {width} bit packed vector is {} words, not {}",
195 packed_len::<T>(width),
196 output.len()
197 )));
198 }
199 if width == 0 {
200 return check_all_zero(input);
204 }
205
206 let mask = low_mask(width);
207 let lanes = T::LANES;
208 for lane in 0..lanes {
209 let mut filled = 0usize;
211 let mut accumulator = 0u64;
212 let mut word = 0usize;
213 for row in 0..T::WIDTH {
214 let value = input[row * lanes + lane].to_u64();
215 if value & !mask != 0 {
216 return Err(Error::internal(format!("value {value} does not fit in {width} bits")));
217 }
218 accumulator |= value << filled;
219 filled += width;
220 if filled >= T::WIDTH {
221 output[word * lanes + lane] = T::from_u64(accumulator & low_mask(T::WIDTH));
222 word += 1;
223 let consumed = width - (filled - T::WIDTH);
226 filled -= T::WIDTH;
227 accumulator = shift_right(value, consumed);
228 }
229 }
230 debug_assert_eq!(filled, 0, "a packed lane always ends on a word boundary");
231 }
232 Ok(())
233}
234
235pub fn unpack_transposed<T: Packable>(input: &[T], width: usize, output: &mut [T]) -> Result<()> {
242 check_width::<T>(width)?;
243 check_vector_len(output.len(), "output")?;
244 if input.len() != packed_len::<T>(width) {
245 return Err(Error::internal(format!(
246 "a {width} bit packed vector is {} words, not {}",
247 packed_len::<T>(width),
248 input.len()
249 )));
250 }
251 if width == 0 {
252 output.fill(T::from_u64(0));
253 return Ok(());
254 }
255
256 let mask = low_mask(width);
257 let lanes = T::LANES;
258 for lane in 0..lanes {
259 let mut available = 0usize;
261 let mut buffer = 0u64;
262 let mut word = 0usize;
263 for row in 0..T::WIDTH {
264 let value = if available >= width {
265 let value = buffer & mask;
266 buffer = shift_right(buffer, width);
267 available -= width;
268 value
269 } else {
270 let next = input[word * lanes + lane].to_u64();
271 word += 1;
272 let taken = width - available;
273 let value = buffer | ((next & low_mask(taken)) << available);
274 buffer = shift_right(next, taken);
275 available = T::WIDTH - taken;
276 value
277 };
278 output[row * lanes + lane] = T::from_u64(value);
279 }
280 }
281 Ok(())
282}
283
284pub fn pack<T: Packable>(input: &[T], width: usize, output: &mut [T]) -> Result<()> {
294 check_vector_len(input.len(), "input")?;
295 let mut transposed = vec![T::from_u64(0); VALUES];
296 transpose(input, &mut transposed)?;
297 pack_transposed(&transposed, width, output)
298}
299
300pub fn unpack<T: Packable>(input: &[T], width: usize, output: &mut [T]) -> Result<()> {
306 check_vector_len(output.len(), "output")?;
307 let mut transposed = vec![T::from_u64(0); VALUES];
308 unpack_transposed(input, width, &mut transposed)?;
309 untranspose(&transposed, output)
310}
311
312#[must_use]
314pub fn tail_len(count: usize, width: usize) -> usize {
315 (count * width).div_ceil(8)
316}
317
318pub fn pack_tail(values: &[u64], width: usize, output: &mut Vec<u8>) -> Result<()> {
336 check_tail(values.len(), width)?;
337 if width == 0 {
338 return check_all_zero(values);
339 }
340 let mask = low_mask(width);
341 let mut accumulator: u128 = 0;
344 let mut filled = 0usize;
345 for value in values {
346 if value & !mask != 0 {
347 return Err(Error::internal(format!("value {value} does not fit in {width} bits")));
348 }
349 accumulator |= u128::from(*value) << filled;
350 filled += width;
351 while filled >= 8 {
352 output.push((accumulator & 0xff) as u8);
353 accumulator >>= 8;
354 filled -= 8;
355 }
356 }
357 if filled > 0 {
358 output.push((accumulator & 0xff) as u8);
359 }
360 Ok(())
361}
362
363pub fn unpack_tail(input: &[u8], width: usize, count: usize) -> Result<Vec<u64>> {
370 check_tail(count, width)?;
371 if width == 0 {
372 return Ok(vec![0; count]);
373 }
374 if input.len() < tail_len(count, width) {
375 return Err(Error::internal(format!(
376 "{count} values at {width} bits need {} bytes and there are {}",
377 tail_len(count, width),
378 input.len()
379 )));
380 }
381 let mask = u128::from(low_mask(width));
382 let mut values = Vec::with_capacity(count);
383 let mut accumulator: u128 = 0;
384 let mut available = 0usize;
385 let mut at = 0usize;
386 for _ in 0..count {
387 while available < width {
388 accumulator |= u128::from(input[at]) << available;
389 at += 1;
390 available += 8;
391 }
392 values.push((accumulator & mask) as u64);
393 accumulator >>= width;
394 available -= width;
395 }
396 Ok(values)
397}
398
399fn check_tail(count: usize, width: usize) -> Result<()> {
400 if count >= VALUES {
401 return Err(Error::internal(format!(
402 "{count} values is a whole unit and belongs in the transposed layout"
403 )));
404 }
405 if width > 64 {
406 return Err(Error::internal(format!("{width} bits does not fit in 64")));
407 }
408 Ok(())
409}
410
411fn check_vector_len(len: usize, what: &str) -> Result<()> {
412 if len == VALUES {
413 Ok(())
414 } else {
415 Err(Error::internal(format!("{what} is {len} values, and a packed unit is {VALUES}")))
416 }
417}
418
419fn check_width<T: Packable>(width: usize) -> Result<()> {
420 if width <= T::WIDTH {
421 Ok(())
422 } else {
423 Err(Error::internal(format!("{width} bits does not fit in a {} bit type", T::WIDTH)))
424 }
425}
426
427fn check_all_zero<T: Packable>(input: &[T]) -> Result<()> {
428 match input.iter().position(|value| value.to_u64() != 0) {
429 None => Ok(()),
430 Some(index) => Err(Error::internal(format!(
431 "a zero bit vector cannot hold {:?} at {index}",
432 input[index]
433 ))),
434 }
435}
436
437#[cfg(test)]
438mod tests {
439 use super::*;
440
441 struct Random(u64);
444
445 impl Random {
446 fn new() -> Self {
447 Self(0x2545_f491_4f6c_dd1d)
448 }
449
450 fn next(&mut self) -> u64 {
451 self.0 ^= self.0 << 13;
452 self.0 ^= self.0 >> 7;
453 self.0 ^= self.0 << 17;
454 self.0
455 }
456 }
457
458 fn sample<T: Packable>(width: usize) -> Vec<T> {
459 let mut random = Random::new();
460 (0..VALUES).map(|_| T::from_u64(random.next() & low_mask(width))).collect()
461 }
462
463 fn round_trip<T: Packable>(width: usize) {
464 let values = sample::<T>(width);
465 let mut packed = vec![T::from_u64(0); packed_len::<T>(width)];
466 pack(&values, width, &mut packed).unwrap();
467 let mut back = vec![T::from_u64(0); VALUES];
468 unpack(&packed, width, &mut back).unwrap();
469 assert_eq!(back, values, "{width} bits of a {} bit type", T::WIDTH);
470 }
471
472 #[test]
473 fn every_width_of_every_type_round_trips() {
474 for width in 0..=8 {
475 round_trip::<u8>(width);
476 }
477 for width in 0..=16 {
478 round_trip::<u16>(width);
479 }
480 for width in 0..=32 {
481 round_trip::<u32>(width);
482 }
483 for width in 0..=64 {
484 round_trip::<u64>(width);
485 }
486 }
487
488 #[test]
489 fn the_transposed_form_also_round_trips_without_being_reordered() {
490 let values = sample::<u32>(19);
493 let mut transposed = vec![0u32; VALUES];
494 transpose(&values, &mut transposed).unwrap();
495 let mut packed = vec![0u32; packed_len::<u32>(19)];
496 pack_transposed(&transposed, 19, &mut packed).unwrap();
497 let mut back = vec![0u32; VALUES];
498 unpack_transposed(&packed, 19, &mut back).unwrap();
499 assert_eq!(back, transposed);
500 }
501
502 #[test]
503 fn the_permutation_is_a_bijection() {
504 fn check<T: Packable>() {
507 let mut seen = vec![false; VALUES];
508 for row in 0..T::WIDTH {
509 for lane in 0..T::LANES {
510 let index = source_index::<T>(row, lane);
511 assert!(!seen[index], "{index} is written twice for {} bits", T::WIDTH);
512 seen[index] = true;
513 }
514 }
515 assert!(seen.into_iter().all(|hit| hit));
516 }
517 check::<u8>();
518 check::<u16>();
519 check::<u32>();
520 check::<u64>();
521 }
522
523 #[test]
524 fn transposing_is_not_the_identity() {
525 let values: Vec<u32> = (0..VALUES).map(|index| index as u32).collect();
527 let mut transposed = vec![0u32; VALUES];
528 transpose(&values, &mut transposed).unwrap();
529 assert_ne!(transposed, values);
530 let mut back = vec![0u32; VALUES];
531 untranspose(&transposed, &mut back).unwrap();
532 assert_eq!(back, values);
533 }
534
535 #[test]
536 fn a_full_width_pack_is_the_data_itself() {
537 let values = sample::<u64>(64);
540 let mut transposed = vec![0u64; VALUES];
541 transpose(&values, &mut transposed).unwrap();
542 let mut packed = vec![0u64; packed_len::<u64>(64)];
543 pack_transposed(&transposed, 64, &mut packed).unwrap();
544 assert_eq!(packed, transposed);
545 }
546
547 #[test]
548 fn a_zero_width_vector_stores_nothing_and_reads_back_as_zeros() {
549 let values = vec![0u32; VALUES];
550 assert_eq!(required_width(&values), 0);
551 let mut packed = Vec::new();
552 pack(&values, 0, &mut packed).unwrap();
553 let mut back = vec![7u32; VALUES];
554 unpack(&packed, 0, &mut back).unwrap();
555 assert_eq!(back, values);
556 }
557
558 #[test]
559 fn required_width_is_the_bits_of_the_largest_value() {
560 assert_eq!(required_width::<u32>(&[]), 0);
561 assert_eq!(required_width::<u32>(&[0, 0]), 0);
562 assert_eq!(required_width::<u32>(&[1]), 1);
563 assert_eq!(required_width::<u32>(&[255, 3]), 8);
564 assert_eq!(required_width::<u32>(&[256]), 9);
565 assert_eq!(required_width::<u64>(&[u64::MAX]), 64);
566 }
567
568 #[test]
569 fn a_value_too_wide_for_the_width_is_an_error_rather_than_silent_truncation() {
570 let mut values = vec![0u32; VALUES];
571 values[500] = 8;
572 let mut transposed = vec![0u32; VALUES];
573 transpose(&values, &mut transposed).unwrap();
574 let mut packed = vec![0u32; packed_len::<u32>(3)];
575 let error = pack_transposed(&transposed, 3, &mut packed).unwrap_err();
576 assert!(error.message().contains("does not fit in 3 bits"), "{error}");
577 }
578
579 #[test]
580 fn a_wrong_sized_buffer_is_an_error() {
581 let values = vec![0u32; VALUES];
582 let mut packed = vec![0u32; 3];
583 let error = pack(&values, 5, &mut packed).unwrap_err();
584 assert!(error.message().contains("words"), "{error}");
585
586 let short = vec![0u32; 7];
587 let mut output = vec![0u32; VALUES];
588 let error = unpack(&short, 5, &mut output).unwrap_err();
589 assert!(error.message().contains("words"), "{error}");
590 }
591
592 #[test]
593 fn a_nonzero_value_at_zero_width_is_an_error() {
594 let mut values = vec![0u32; VALUES];
595 values[9] = 1;
596 let mut packed = Vec::new();
597 let error = pack(&values, 0, &mut packed).unwrap_err();
598 assert!(error.message().contains("zero bit vector"), "{error}");
599 }
600
601 #[test]
602 fn packing_at_a_width_the_type_cannot_hold_is_an_error() {
603 let values = vec![0u16; VALUES];
604 let mut packed = vec![0u16; 17 * 64];
605 let error = pack(&values, 17, &mut packed).unwrap_err();
606 assert!(error.message().contains("16 bit type"), "{error}");
607 }
608
609 #[test]
610 fn a_tail_round_trips_at_every_width_and_every_length() {
611 let mut random = Random::new();
612 for width in [0usize, 1, 3, 7, 8, 13, 31, 32, 33, 63, 64] {
613 for count in [0usize, 1, 2, 7, 8, 9, 100, 1023] {
614 let values: Vec<u64> =
615 (0..count).map(|_| random.next() & low_mask(width)).collect();
616 let mut bytes = Vec::new();
617 pack_tail(&values, width, &mut bytes).unwrap();
618 assert_eq!(bytes.len(), tail_len(count, width), "{count} at {width}");
619 assert_eq!(unpack_tail(&bytes, width, count).unwrap(), values);
620 }
621 }
622 }
623
624 #[test]
625 fn a_tail_costs_its_own_values_and_not_a_whole_unit() {
626 let values = vec![(1u64 << 39) + 1; 3];
628 let mut bytes = Vec::new();
629 pack_tail(&values, 40, &mut bytes).unwrap();
630 assert_eq!(bytes.len(), 15);
631 assert_eq!(packed_len::<u64>(40) * 8, 5120);
632 }
633
634 #[test]
635 fn a_whole_unit_is_refused_by_the_tail_packer() {
636 let values = vec![0u64; VALUES];
637 let error = pack_tail(&values, 4, &mut Vec::new()).unwrap_err();
638 assert!(error.message().contains("whole unit"), "{error}");
639 }
640
641 #[test]
642 fn a_short_tail_buffer_is_an_error() {
643 let error = unpack_tail(&[0, 0], 8, 5).unwrap_err();
644 assert!(error.message().contains("need 5 bytes"), "{error}");
645 }
646
647 #[test]
648 fn the_packed_size_is_the_same_as_the_naive_layout() {
649 for width in 0..=32 {
650 assert_eq!(packed_len::<u32>(width) * 32, width * VALUES);
651 }
652 }
653}