#[cfg(feature = "serde")]
mod serde_impl;
use std::{
alloc::{self, Layout},
ptr::NonNull,
};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum BitWidth {
U8 = 1,
U16 = 2,
U24 = 3,
U32 = 4,
}
impl BitWidth {
#[inline(always)]
const fn elem_size(&self) -> usize {
match self {
BitWidth::U8 => 1,
BitWidth::U16 => 2,
BitWidth::U24 => 3,
BitWidth::U32 => 4,
}
}
#[inline(always)]
const fn width_for(v: u32) -> Self {
if v <= u8::MAX as u32 {
BitWidth::U8
} else if v <= u16::MAX as u32 {
BitWidth::U16
} else if v <= 0xFFFFFF {
BitWidth::U24
} else {
BitWidth::U32
}
}
}
impl Ord for BitWidth {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
(*self as u8).cmp(&(*other as u8))
}
}
impl PartialOrd for BitWidth {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some((*self as u8).cmp(&(*other as u8)))
}
}
pub struct CompactVec {
ptr: NonNull<u8>,
len: usize,
cap: usize,
width: BitWidth,
}
impl Clone for CompactVec {
fn clone(&self) -> Self {
let mut new_cv = CompactVec::new();
if self.len > 0 {
let es = self.width.elem_size();
new_cv.width = self.width; new_cv.grow_alloc(self.cap);
unsafe {
std::ptr::copy_nonoverlapping(
self.ptr.as_ptr(),
new_cv.ptr.as_ptr(),
self.len * es,
);
}
new_cv.len = self.len;
}
new_cv
}
fn clone_from(&mut self, source: &Self) {
if self.cap >= source.len && self.width == source.width {
let es = self.width.elem_size();
unsafe {
std::ptr::copy_nonoverlapping(
source.ptr.as_ptr(),
self.ptr.as_ptr(),
source.len * es,
);
}
self.len = source.len;
} else {
*self = source.clone();
}
}
}
unsafe impl Send for CompactVec {}
unsafe impl Sync for CompactVec {}
impl CompactVec {
pub const fn new() -> Self {
Self {
ptr: NonNull::dangling(),
len: 0,
cap: 0,
width: BitWidth::U8,
}
}
pub fn with_capacity(cap: usize) -> Self {
let mut cv = Self::new();
if cap > 0 {
cv.grow_alloc(cap);
}
cv
}
pub const fn width(&self) -> BitWidth {
self.width
}
#[inline]
pub const fn len(&self) -> usize {
self.len
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub const fn capacity(&self) -> usize {
self.cap
}
#[inline]
pub const fn max_value(&self) -> u32 {
match self.width {
BitWidth::U8 => u8::MAX as u32,
BitWidth::U16 => u16::MAX as u32,
BitWidth::U24 => 0xFFFFFF,
BitWidth::U32 => u32::MAX,
}
}
#[inline]
pub const fn width_bits(&self) -> u32 {
match self.width {
BitWidth::U8 => 8,
BitWidth::U16 => 16,
BitWidth::U24 => 24,
BitWidth::U32 => 32,
}
}
#[inline]
pub fn get(&self, idx: usize) -> Option<u32> {
if idx >= self.len {
return None;
}
Some(unsafe { self.read_at(idx) })
}
#[inline]
pub unsafe fn get_unchecked(&self, idx: usize) -> u32 {
unsafe { self.read_at(idx) }
}
#[inline]
pub fn push(&mut self, value: u32) {
let needed = BitWidth::width_for(value);
let is_full = self.len == self.cap;
if self.needs_upgrade(needed) {
let target_cap = if is_full {
if self.cap == 0 { 4 } else { self.cap * 2 }
} else {
self.cap
};
self.upgrade_to(needed, target_cap);
} else if is_full {
let new_cap = if self.cap == 0 { 4 } else { self.cap * 2 };
self.grow_alloc(new_cap);
}
unsafe {
self.write_at(self.len, value);
}
self.len += 1;
}
pub fn set(&mut self, idx: usize, value: u32) {
assert!(idx < self.len, "index out of bounds");
let needed = BitWidth::width_for(value);
if self.needs_upgrade(needed) {
self.upgrade(needed);
}
unsafe {
self.write_at(idx, value);
}
}
#[inline]
pub fn pop(&mut self) -> Option<u32> {
if self.len == 0 {
return None;
}
self.len -= 1;
Some(unsafe { self.read_at(self.len) })
}
pub fn remove(&mut self, index: usize) -> u32 {
assert!(index < self.len, "index out of bounds");
let removed_val = unsafe { self.read_at(index) };
if index < self.len - 1 {
let es = self.width.elem_size();
unsafe {
let ptr = self.ptr.as_ptr();
let dst = ptr.add(index * es);
let src = dst.add(es);
let count = (self.len - index - 1) * es;
std::ptr::copy(src, dst, count);
}
}
self.len -= 1;
removed_val
}
pub const fn clear(&mut self) {
self.len = 0;
}
pub fn truncate(&mut self, new_len: usize) {
if new_len < self.len {
self.len = new_len;
}
}
pub fn dedup(&mut self) {
if self.len < 2 {
return;
}
let mut write = 1usize;
let mut prev = unsafe { self.read_at(0) };
for read in 1..self.len {
let curr = unsafe { self.read_at(read) };
if curr != prev {
if write != read {
unsafe { self.write_at(write, curr) };
}
prev = curr;
write += 1;
}
}
self.len = write;
}
pub fn retain<F: FnMut(u32) -> bool>(&mut self, mut f: F) {
let mut write_at = 0usize;
for read_at in 0..self.len {
let v = unsafe { self.read_at(read_at) };
if f(v) {
if write_at != read_at {
unsafe { self.write_at(write_at, v) };
}
write_at += 1;
}
}
self.len = write_at;
}
pub fn extend_from_compact_vec(&mut self, other: &CompactVec) {
if other.is_empty() {
return;
}
let target_width = self.width.max(other.width);
let needed_len = self.len + other.len;
let new_cap = needed_len
.checked_next_power_of_two()
.unwrap_or(needed_len)
.max(self.cap);
if self.needs_upgrade(target_width) {
self.upgrade_to(target_width, new_cap);
} else if needed_len > self.cap {
self.grow_alloc(new_cap);
}
if other.width == self.width {
let es = self.width.elem_size();
unsafe {
core::ptr::copy_nonoverlapping(
other.ptr.as_ptr(),
self.ptr.as_ptr().add(self.len * es),
other.len * es,
);
}
} else {
for i in 0..other.len {
let v = unsafe { other.read_at(i) };
unsafe { self.write_at(self.len + i, v) };
}
}
self.len += other.len;
}
pub fn clear_and_shrink_width(&mut self) {
if self.cap > 0 {
let layout = Layout::array::<u8>(self.cap * self.width.elem_size()).unwrap();
unsafe { alloc::dealloc(self.ptr.as_ptr(), layout) };
self.ptr = NonNull::dangling();
self.cap = 0;
}
self.len = 0;
self.width = BitWidth::U8;
}
pub fn shrink_to_fit(&mut self) {
let mut min_needed = BitWidth::U8;
for i in 0..self.len {
let val = unsafe { self.read_at(i) };
let width = BitWidth::width_for(val);
if width > min_needed {
min_needed = width;
}
if min_needed == BitWidth::U32 {
break;
}
}
if self.cap > self.len || min_needed < self.width {
if self.len == 0 {
if self.cap > 0 {
let es = self.width.elem_size();
let layout = Layout::array::<u8>(self.cap * es).unwrap();
unsafe { alloc::dealloc(self.ptr.as_ptr(), layout) };
self.ptr = NonNull::dangling();
self.cap = 0;
self.width = BitWidth::U8;
}
} else {
self.upgrade_to(min_needed, self.len);
}
}
}
#[inline]
pub fn to_vec(self) -> Vec<u32> {
self.iter().collect()
}
pub fn iter(&self) -> Iter<'_> {
Iter {
cv: self,
start: 0,
end: self.len,
}
}
pub fn binary_search(&self, value: u32) -> Result<usize, usize> {
use core::cmp::Ordering;
let mut lo = 0usize;
let mut hi = self.len;
while lo < hi {
let mid = lo + (hi - lo) / 2;
let v = unsafe { self.read_at(mid) };
match v.cmp(&value) {
Ordering::Equal => return Ok(mid),
Ordering::Less => lo = mid + 1,
Ordering::Greater => hi = mid,
}
}
Err(lo)
}
pub fn sort_unstable(&mut self) {
if self.len < 2 {
return;
}
match self.width {
BitWidth::U8 => {
let slice = unsafe { core::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) };
slice.sort_unstable();
}
_ => {
let mut tmp: Vec<u32> = (0..self.len).map(|i| unsafe { self.read_at(i) }).collect();
tmp.sort_unstable();
for (i, v) in tmp.into_iter().enumerate() {
unsafe { self.write_at(i, v) };
}
}
}
}
pub fn sort(&mut self) {
if self.len < 2 {
return;
}
match self.width {
BitWidth::U8 => {
let slice = unsafe { core::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) };
slice.sort();
}
_ => {
let mut tmp: Vec<u32> = (0..self.len).map(|i| unsafe { self.read_at(i) }).collect();
tmp.sort();
for (i, v) in tmp.into_iter().enumerate() {
unsafe { self.write_at(i, v) };
}
}
}
}
#[inline]
pub fn byte_len(&self) -> usize {
self.len * self.width.elem_size()
}
#[inline]
pub fn byte_capacity(&self) -> usize {
self.cap * self.width.elem_size()
}
pub fn from_sorted_iter<I: IntoIterator<Item = u32>>(iter: I) -> Self {
let values: Vec<u32> = iter.into_iter().collect();
Self::from_sorted_slice(&values)
}
pub fn from_sorted_slice(slice: &[u32]) -> Self {
if slice.is_empty() {
return Self::new();
}
debug_assert!(
slice.windows(2).all(|w| w[0] <= w[1]),
"from_sorted_slice: input must be sorted in ascending order"
);
let max_val = slice[slice.len() - 1];
let width = BitWidth::width_for(max_val);
let mut cv = Self::new();
cv.width = width;
cv.grow_alloc(slice.len());
for (i, &v) in slice.iter().enumerate() {
unsafe { cv.write_at(i, v) };
}
cv.len = slice.len();
cv
}
fn grow_alloc(&mut self, new_cap: usize) {
let es = self.width.elem_size();
let new_layout = Layout::array::<u8>(new_cap * es).unwrap();
let new_ptr = if self.cap == 0 {
unsafe { alloc::alloc(new_layout) }
} else {
let old_layout = Layout::array::<u8>(self.cap * es).unwrap();
unsafe { alloc::realloc(self.ptr.as_ptr(), old_layout, new_cap * es) }
};
self.ptr = NonNull::new(new_ptr).expect("allocation failed");
self.cap = new_cap;
}
fn upgrade_to(&mut self, target: BitWidth, new_cap: usize) {
let new_es = target.elem_size();
let new_layout = Layout::array::<u8>(new_cap * new_es).unwrap();
let new_ptr = NonNull::new(unsafe { alloc::alloc(new_layout) }).expect("allocation failed");
for i in 0..self.len {
unsafe {
let v = self.read_at(i);
write_val(new_ptr.as_ptr().cast(), i, v, target);
}
}
if self.cap > 0 {
let old_layout = Layout::array::<u8>(self.cap * self.width.elem_size()).unwrap();
unsafe {
alloc::dealloc(self.ptr.as_ptr(), old_layout);
}
}
self.ptr = new_ptr.cast();
self.cap = new_cap;
self.width = target;
}
#[inline]
fn needs_upgrade(&self, needed: BitWidth) -> bool {
self.width < needed
}
fn upgrade(&mut self, target: BitWidth) {
self.upgrade_to(target, self.cap);
}
#[inline(always)]
unsafe fn read_at(&self, idx: usize) -> u32 {
unsafe { read_val(self.ptr.as_ptr(), idx, self.width) }
}
#[inline(always)]
unsafe fn write_at(&mut self, idx: usize, v: u32) {
unsafe { write_val(self.ptr.as_ptr(), idx, v, self.width) }
}
}
#[inline(always)]
const unsafe fn read_val(ptr: *const u8, idx: usize, width: BitWidth) -> u32 {
match width {
BitWidth::U8 => unsafe { *ptr.add(idx) as u32 },
BitWidth::U16 => {
let p = unsafe { ptr.add(idx * 2) } as *const u16;
unsafe { p.read_unaligned() as u32 }
}
BitWidth::U24 => {
let p = unsafe { ptr.add(idx * 3) };
let mut bytes = [0u8; 4];
unsafe {
std::ptr::copy_nonoverlapping(p, bytes.as_mut_ptr(), 3);
}
u32::from_le_bytes(bytes)
}
BitWidth::U32 => {
let p = unsafe { ptr.add(idx * 4) } as *const u32;
unsafe { p.read_unaligned() }
}
}
}
#[inline(always)]
const unsafe fn write_val(ptr: *mut u8, idx: usize, v: u32, width: BitWidth) {
match width {
BitWidth::U8 => unsafe { *ptr.add(idx) = v as u8 },
BitWidth::U16 => {
let p = unsafe { ptr.add(idx * 2) } as *mut u16;
unsafe { p.write_unaligned(v as u16) };
}
BitWidth::U24 => {
let p = unsafe { ptr.add(idx * 3) };
let bytes = (v as u32).to_le_bytes();
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr(), p, 3);
}
}
BitWidth::U32 => {
let p = unsafe { ptr.add(idx * 4) } as *mut u32;
unsafe { p.write_unaligned(v) };
}
}
}
impl Drop for CompactVec {
fn drop(&mut self) {
if self.cap > 0 {
let layout = Layout::array::<u8>(self.cap * self.width.elem_size()).unwrap();
unsafe {
alloc::dealloc(self.ptr.as_ptr(), layout);
}
}
}
}
pub struct Iter<'a> {
cv: &'a CompactVec,
start: usize, end: usize, }
impl<'a> Iterator for Iter<'a> {
type Item = u32;
#[inline]
fn next(&mut self) -> Option<u32> {
if self.start >= self.end {
return None;
}
let v = unsafe { self.cv.read_at(self.start) };
self.start += 1;
Some(v)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let rem = self.end - self.start;
(rem, Some(rem))
}
}
impl ExactSizeIterator for Iter<'_> {}
impl DoubleEndedIterator for Iter<'_> {
#[inline]
fn next_back(&mut self) -> Option<u32> {
if self.start >= self.end {
return None;
}
self.end -= 1;
Some(unsafe { self.cv.read_at(self.end) })
}
}
pub struct IntoIter {
cv: CompactVec,
start: usize, end: usize, }
impl Iterator for IntoIter {
type Item = u32;
#[inline]
fn next(&mut self) -> Option<u32> {
if self.start >= self.end {
return None;
}
let v = unsafe { self.cv.read_at(self.start) };
self.start += 1;
Some(v)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let rem = self.end - self.start;
(rem, Some(rem))
}
}
impl DoubleEndedIterator for IntoIter {
#[inline]
fn next_back(&mut self) -> Option<u32> {
if self.start >= self.end {
return None;
}
self.end -= 1;
Some(unsafe { self.cv.read_at(self.end) })
}
}
impl ExactSizeIterator for IntoIter {}
impl IntoIterator for CompactVec {
type Item = u32;
type IntoIter = IntoIter;
fn into_iter(self) -> IntoIter {
let end = self.len;
IntoIter {
cv: self,
start: 0,
end,
}
}
}
impl Default for CompactVec {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for CompactVec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
impl FromIterator<u32> for CompactVec {
fn from_iter<I: IntoIterator<Item = u32>>(iter: I) -> Self {
let mut cv = Self::new();
for v in iter {
cv.push(v);
}
cv
}
}
impl Extend<u32> for CompactVec {
fn extend<T: IntoIterator<Item = u32>>(&mut self, iter: T) {
for v in iter {
self.push(v);
}
}
}
impl<'a> IntoIterator for &'a CompactVec {
type Item = u32;
type IntoIter = Iter<'a>;
fn into_iter(self) -> Iter<'a> {
self.iter()
}
}
impl From<CompactVec> for Vec<u32> {
fn from(value: CompactVec) -> Self {
value.into_iter().collect()
}
}
impl PartialEq for CompactVec {
fn eq(&self, other: &Self) -> bool {
if self.len != other.len {
return false;
}
for i in 0..self.len {
let a = unsafe { self.read_at(i) };
let b = unsafe { other.read_at(i) };
if a != b {
return false;
}
}
true
}
}
impl Eq for CompactVec {}
impl PartialEq<Vec<u32>> for CompactVec {
fn eq(&self, other: &Vec<u32>) -> bool {
if self.len != other.len() {
return false;
}
for (i, &b) in other.iter().enumerate() {
let a = unsafe { self.read_at(i) };
if a != b {
return false;
}
}
true
}
}
impl PartialEq<CompactVec> for Vec<u32> {
fn eq(&self, other: &CompactVec) -> bool {
other == self
}
}
impl From<Vec<u32>> for CompactVec {
fn from(v: Vec<u32>) -> Self {
v.into_iter().collect()
}
}
impl AsRef<[u8]> for CompactVec {
fn as_ref(&self) -> &[u8] {
if self.len == 0 {
return &[];
}
unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.byte_len()) }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn starts_u8_and_stores_small_values() {
let mut cv = CompactVec::new();
cv.push(0);
cv.push(255);
assert_eq!(cv.width_bits(), 8);
assert_eq!(cv.get(0), Some(0));
assert_eq!(cv.get(1), Some(255));
assert_eq!(cv.len(), 2);
}
#[test]
fn upgrades_u8_to_u16() {
let mut cv = CompactVec::new();
cv.push(10);
assert_eq!(cv.width_bits(), 8);
cv.push(256);
assert_eq!(cv.width_bits(), 16);
assert_eq!(cv.get(0), Some(10));
assert_eq!(cv.get(1), Some(256));
}
#[test]
fn upgrades_u16_to_u24() {
let mut cv = CompactVec::new();
cv.push(1000);
assert_eq!(cv.width_bits(), 16);
cv.push(u16::MAX as u32 + 1);
assert_eq!(cv.width_bits(), 24);
assert_eq!(cv.get(0), Some(1000));
assert_eq!(cv.get(1), Some(u16::MAX as u32 + 1));
}
#[test]
fn upgrades_u8_directly_to_u24() {
let mut cv = CompactVec::new();
cv.push(1);
cv.push(100_000);
assert_eq!(cv.width_bits(), 24);
assert_eq!(cv.get(0), Some(1));
assert_eq!(cv.get(1), Some(100_000));
}
#[test]
fn push_and_pop() {
let mut cv = CompactVec::new();
for i in 0..100u32 {
cv.push(i);
}
assert_eq!(cv.len(), 100);
for i in (0..100u32).rev() {
assert_eq!(cv.pop(), Some(i));
}
assert!(cv.is_empty());
assert_eq!(cv.pop(), None);
}
#[test]
fn set_upgrades_if_needed() {
let mut cv = CompactVec::new();
cv.push(1u32);
cv.push(2u32);
assert_eq!(cv.width_bits(), 8);
cv.set(0, 70_000);
assert_eq!(cv.width_bits(), 24);
assert_eq!(cv.get(0), Some(70_000));
assert_eq!(cv.get(1), Some(2));
}
#[test]
fn out_of_bounds_returns_none() {
let cv = CompactVec::new();
assert_eq!(cv.get(0), None);
let mut cv2 = CompactVec::new();
cv2.push(42);
assert_eq!(cv2.get(1), None);
}
#[test]
fn iter_matches_pushes() {
let vals: Vec<u32> = vec![0, 1, 255, 256, 65535, 65536, u32::MAX];
let cv: CompactVec = vals.iter().copied().collect();
let out: Vec<u32> = cv.iter().collect();
assert_eq!(vals, out);
assert_eq!(cv.width_bits(), 32);
}
#[test]
fn with_capacity_no_realloc_for_small_set() {
let mut cv = CompactVec::with_capacity(64);
assert!(cv.capacity() >= 64);
for i in 0..64u32 {
cv.push(i);
}
assert_eq!(cv.len(), 64);
assert_eq!(cv.width_bits(), 8);
}
#[test]
fn from_iterator() {
let cv: CompactVec = (0u32..257).collect();
assert_eq!(cv.len(), 257);
assert_eq!(cv.width_bits(), 16);
for i in 0u32..257 {
assert_eq!(cv.get(i as usize), Some(i));
}
}
#[test]
fn large_u32_values() {
let mut cv = CompactVec::new();
cv.push(u32::MAX);
assert_eq!(cv.width_bits(), 32);
assert_eq!(cv.get(0), Some(u32::MAX));
cv.push(0);
cv.push(u32::MAX - 1);
assert_eq!(cv.get(1), Some(0));
assert_eq!(cv.get(2), Some(u32::MAX - 1));
}
#[test]
fn debug_format() {
let cv: CompactVec = vec![1u32, 2, 3].into_iter().collect();
assert_eq!(format!("{cv:?}"), "[1, 2, 3]");
}
#[test]
fn exact_size_iterator() {
let cv: CompactVec = (0u32..10).collect();
let mut it = cv.iter();
assert_eq!(it.len(), 10);
it.next();
assert_eq!(it.len(), 9);
}
#[test]
fn get_unchecked_matches_get() {
let cv: CompactVec = vec![10u32, 20, 300, 70_000].into_iter().collect();
for i in 0..cv.len() {
assert_eq!(unsafe { cv.get_unchecked(i) }, cv.get(i).unwrap());
}
}
#[test]
fn memory_u8_is_quarter_of_u32() {
let mut cv = CompactVec::new();
for i in 0u32..=255 {
cv.push(i);
}
assert_eq!(cv.width_bits(), 8);
let used_bytes = cv.capacity();
let u32_equivalent = cv.capacity() * 4;
assert_eq!(used_bytes * 4, u32_equivalent);
}
#[test]
fn u8_storage_is_4x_denser_than_vec_u32() {
let n = 1024usize;
let mut cv = CompactVec::with_capacity(n);
for i in 0..n {
cv.push(i as u32 % 256);
}
assert_eq!(cv.width_bits(), 8);
let compact_bytes = cv.byte_capacity();
let naive_bytes = cv.capacity() * 4;
assert_eq!(compact_bytes * 4, naive_bytes);
}
#[test]
fn u16_storage_is_2x_denser_than_vec_u32() {
let n = 1024usize;
let mut cv = CompactVec::with_capacity(n);
for i in 0..n {
cv.push(256 + (i as u32 % (65535 - 256)));
}
assert_eq!(cv.width_bits(), 16);
let compact_bytes = cv.byte_capacity();
let naive_bytes = cv.capacity() * 4;
assert_eq!(compact_bytes * 2, naive_bytes);
}
#[test]
fn test_u24_boundary_values() {
let mut cv = CompactVec::new();
let values = vec![
0x000000, 0x000001, 0x00FFFF, 0x010000, 0xABCDEF, 0xFFFFFE, 0xFFFFFF,
];
for &v in &values {
cv.push(v);
}
assert_eq!(cv.width_bits(), 24);
for (i, &expected) in values.iter().enumerate() {
assert_eq!(cv.get(i), Some(expected), "Failed at index {i}");
}
}
#[test]
fn test_u24_to_u32_transition() {
let mut cv = CompactVec::new();
cv.push(0xFFFFFF);
assert_eq!(cv.width_bits(), 24);
cv.push(0x1000000);
assert_eq!(cv.width_bits(), 32);
assert_eq!(cv.get(0), Some(0xFFFFFF));
assert_eq!(cv.get(1), Some(0x1000000));
}
#[test]
fn test_u24_random_access_consistency() {
let mut cv = CompactVec::new();
cv.push(0x112233);
cv.push(0x445566);
cv.push(0x778899);
assert_eq!(cv.get(0), Some(0x112233));
assert_eq!(cv.get(1), Some(0x445566));
assert_eq!(cv.get(2), Some(0x778899));
}
#[test]
fn test_u24_set_overwrite() {
let mut cv = CompactVec::new();
cv.push(0x123456);
cv.push(0x654321);
cv.set(0, 0xABCDEF);
assert_eq!(cv.get(0), Some(0xABCDEF));
assert_eq!(cv.get(1), Some(0x654321));
}
#[test]
fn test_shrink_capacity_only() {
let mut cv = CompactVec::new();
for i in 0..10 {
cv.push(i);
}
let initial_cap = cv.capacity();
assert!(initial_cap >= 10);
cv.shrink_to_fit();
assert_eq!(cv.len(), 10);
assert_eq!(cv.capacity(), 10);
assert_eq!(cv.width(), BitWidth::U8);
}
#[test]
fn test_shrink_width_and_capacity() {
let mut cv = CompactVec::new();
cv.push(1);
cv.push(100000);
assert!(cv.width() > BitWidth::U8);
cv.pop();
let high_width = cv.width();
cv.shrink_to_fit();
assert_eq!(cv.len(), 1);
assert_eq!(cv.capacity(), 1);
assert!(cv.width() < high_width);
assert_eq!(cv.width(), BitWidth::U8);
assert_eq!(cv.get(0), Some(1));
}
#[test]
fn test_shrink_empty() {
let mut cv = CompactVec::with_capacity(100);
assert_eq!(cv.capacity(), 100);
cv.shrink_to_fit();
assert_eq!(cv.len(), 0);
assert_eq!(cv.capacity(), 0);
assert_eq!(cv.width(), BitWidth::U8);
}
#[test]
fn test_shrink_no_op() {
let mut cv = CompactVec::new();
cv.push(10);
cv.shrink_to_fit();
let cap_before = cv.capacity();
let width_before = cv.width();
cv.shrink_to_fit();
assert_eq!(cv.capacity(), cap_before);
assert_eq!(cv.width(), width_before);
assert_eq!(cv.get(0), Some(10));
}
#[test]
fn clone_u32_correct_allocation() {
let mut cv = CompactVec::new();
cv.push(u32::MAX); cv.push(1); let cloned = cv.clone();
assert_eq!(cloned.get(0), Some(u32::MAX));
assert_eq!(cloned.get(1), Some(1));
assert_eq!(cloned.width(), BitWidth::U32);
}
#[test]
fn clear_and_shrink_width_then_refill() {
let mut cv = CompactVec::new();
cv.push(u32::MAX);
while cv.len() < cv.capacity() {
cv.push(0);
}
let cap = cv.capacity();
cv.clear_and_shrink_width();
assert_eq!(cv.capacity(), 0, "cap must be reset to 0");
assert_eq!(cv.width(), BitWidth::U8);
for i in 0..cap as u32 {
cv.push(i % 256);
}
cv.push(42);
assert_eq!(cv.len(), cap + 1);
}
#[test]
fn partial_eq_same_width() {
let a: CompactVec = vec![1u32, 2, 3].into_iter().collect();
let b: CompactVec = vec![1u32, 2, 3].into_iter().collect();
assert_eq!(a, b);
}
#[test]
fn partial_eq_different_widths() {
let mut a = CompactVec::new();
a.push(1);
let mut b = CompactVec::new();
b.push(1);
b.push(u32::MAX);
b.pop();
assert_eq!(a, b);
}
#[test]
fn partial_eq_with_vec() {
let cv: CompactVec = vec![10u32, 20, 30].into_iter().collect();
assert_eq!(cv, vec![10u32, 20, 30]);
assert_ne!(cv, vec![10u32, 20]);
}
#[test]
fn from_vec_u32() {
let v = vec![0u32, 255, 256, 65536];
let cv = CompactVec::from(v.clone());
assert_eq!(cv, v);
}
#[test]
fn truncate_basic() {
let mut cv: CompactVec = (0u32..10).collect();
cv.truncate(4);
assert_eq!(cv.len(), 4);
assert_eq!(cv.get(3), Some(3));
assert_eq!(cv.get(4), None);
}
#[test]
fn truncate_noop_when_larger() {
let mut cv: CompactVec = (0u32..5).collect();
cv.truncate(100);
assert_eq!(cv.len(), 5);
}
#[test]
fn truncate_to_zero() {
let mut cv: CompactVec = (0u32..8).collect();
cv.truncate(0);
assert!(cv.is_empty());
assert!(cv.capacity() >= 8);
}
#[test]
fn truncate_preserves_width() {
let mut cv: CompactVec = vec![1u32, 70_000].into_iter().collect();
assert_eq!(cv.width_bits(), 24);
cv.truncate(1);
assert_eq!(cv.width_bits(), 24); assert_eq!(cv.get(0), Some(1));
}
#[test]
fn dedup_removes_consecutive() {
let mut cv: CompactVec = vec![1u32, 1, 2, 3, 3, 3, 4].into_iter().collect();
cv.dedup();
let v: Vec<u32> = cv.iter().collect();
assert_eq!(v, [1, 2, 3, 4]);
}
#[test]
fn dedup_leaves_non_adjacent() {
let mut cv: CompactVec = vec![1u32, 2, 1].into_iter().collect();
cv.dedup();
assert_eq!(cv.len(), 3);
}
#[test]
fn dedup_all_same() {
let mut cv: CompactVec = vec![7u32; 100].into_iter().collect();
cv.dedup();
assert_eq!(cv.len(), 1);
assert_eq!(cv.get(0), Some(7));
}
#[test]
fn dedup_empty_and_single() {
let mut cv = CompactVec::new();
cv.dedup();
assert!(cv.is_empty());
let mut cv2: CompactVec = vec![42u32].into_iter().collect();
cv2.dedup();
assert_eq!(cv2.len(), 1);
}
#[test]
fn dedup_on_u32_width() {
let mut cv: CompactVec = vec![u32::MAX, u32::MAX, 1, 1, 2].into_iter().collect();
cv.dedup();
let v: Vec<u32> = cv.iter().collect();
assert_eq!(v, [u32::MAX, 1, 2]);
assert_eq!(cv.width_bits(), 32);
}
#[test]
fn retain_evens() {
let mut cv: CompactVec = (0u32..10).collect();
cv.retain(|v| v % 2 == 0);
let v: Vec<u32> = cv.iter().collect();
assert_eq!(v, [0, 2, 4, 6, 8]);
}
#[test]
fn retain_keep_all() {
let mut cv: CompactVec = (0u32..5).collect();
cv.retain(|_| true);
assert_eq!(cv.len(), 5);
}
#[test]
fn retain_keep_none() {
let mut cv: CompactVec = (0u32..5).collect();
cv.retain(|_| false);
assert!(cv.is_empty());
}
#[test]
fn retain_empty() {
let mut cv = CompactVec::new();
cv.retain(|_| true);
assert!(cv.is_empty());
}
#[test]
fn retain_preserves_order_and_values() {
let mut cv: CompactVec = vec![10u32, 200, 3000, 40, 500].into_iter().collect();
cv.retain(|v| v > 100);
let v: Vec<u32> = cv.iter().collect();
assert_eq!(v, [200, 3000, 500]);
}
#[test]
fn extend_same_width_bulk_copy() {
let mut a: CompactVec = vec![1u32, 2, 3].into_iter().collect();
let b: CompactVec = vec![4u32, 5, 6].into_iter().collect();
assert_eq!(a.width_bits(), 8);
assert_eq!(b.width_bits(), 8);
a.extend_from_compact_vec(&b);
assert_eq!(a.len(), 6);
let v: Vec<u32> = a.iter().collect();
assert_eq!(v, [1, 2, 3, 4, 5, 6]);
assert_eq!(a.width_bits(), 8);
}
#[test]
fn extend_upgrades_self_to_other_width() {
let mut a: CompactVec = vec![1u32, 2].into_iter().collect(); let b: CompactVec = vec![300u32, 400].into_iter().collect();
a.extend_from_compact_vec(&b);
assert_eq!(a.width_bits(), 16);
assert_eq!(a.len(), 4);
assert_eq!(a.get(0), Some(1));
assert_eq!(a.get(2), Some(300));
}
#[test]
fn extend_self_wider_upcasts_other() {
let mut a: CompactVec = vec![300u32, 400].into_iter().collect(); let b: CompactVec = vec![1u32, 2].into_iter().collect();
a.extend_from_compact_vec(&b);
assert_eq!(a.width_bits(), 16); assert_eq!(a.len(), 4);
assert_eq!(a.get(2), Some(1));
assert_eq!(a.get(3), Some(2));
}
#[test]
fn extend_into_empty_self() {
let mut a = CompactVec::new();
let b: CompactVec = vec![10u32, 20, 30].into_iter().collect();
a.extend_from_compact_vec(&b);
assert_eq!(a.len(), 3);
assert_eq!(a.get(2), Some(30));
}
#[test]
fn extend_from_empty_other_noop() {
let mut a: CompactVec = vec![1u32, 2].into_iter().collect();
a.extend_from_compact_vec(&CompactVec::new());
assert_eq!(a.len(), 2);
}
#[test]
fn binary_search_found() {
let cv: CompactVec = vec![10u32, 20, 30, 40, 50].into_iter().collect();
assert_eq!(cv.binary_search(10), Ok(0));
assert_eq!(cv.binary_search(30), Ok(2));
assert_eq!(cv.binary_search(50), Ok(4));
}
#[test]
fn binary_search_not_found() {
let cv: CompactVec = vec![10u32, 20, 30, 40, 50].into_iter().collect();
assert_eq!(cv.binary_search(0), Err(0));
assert_eq!(cv.binary_search(25), Err(2));
assert_eq!(cv.binary_search(99), Err(5));
}
#[test]
fn binary_search_empty() {
let cv = CompactVec::new();
assert_eq!(cv.binary_search(0), Err(0));
}
#[test]
fn binary_search_u32_width() {
let cv: CompactVec = vec![0u32, u32::MAX / 2, u32::MAX].into_iter().collect();
assert_eq!(cv.binary_search(u32::MAX), Ok(2));
assert_eq!(cv.binary_search(1), Err(1));
}
#[test]
fn binary_search_insert_position_is_sorted() {
let mut cv: CompactVec = vec![2u32, 5, 8, 11].into_iter().collect();
let Err(pos) = cv.binary_search(7) else {
panic!()
};
cv.push(7);
cv.sort_unstable();
assert_eq!(cv.get(pos), Some(7));
}
#[test]
fn sort_unstable_u8() {
let mut cv: CompactVec = vec![3u32, 1, 4, 1, 5, 9, 2, 6].into_iter().collect();
assert_eq!(cv.width_bits(), 8);
cv.sort_unstable();
let v: Vec<u32> = cv.iter().collect();
assert_eq!(v, [1, 1, 2, 3, 4, 5, 6, 9]);
}
#[test]
fn sort_unstable_u16() {
let mut cv: CompactVec = vec![1000u32, 300, 500, 256].into_iter().collect();
assert_eq!(cv.width_bits(), 16);
cv.sort_unstable();
let v: Vec<u32> = cv.iter().collect();
assert_eq!(v, [256, 300, 500, 1000]);
}
#[test]
fn sort_unstable_u32() {
let mut cv: CompactVec = vec![u32::MAX, 0u32, u32::MAX / 2].into_iter().collect();
cv.sort_unstable();
assert_eq!(cv.get(0), Some(0));
assert_eq!(cv.get(2), Some(u32::MAX));
}
#[test]
fn sort_stable_preserves_equal() {
let mut cv: CompactVec = vec![5u32, 2, 2, 1].into_iter().collect();
cv.sort();
let v: Vec<u32> = cv.iter().collect();
assert_eq!(v, [1, 2, 2, 5]);
}
#[test]
fn sort_already_sorted_is_noop() {
let mut cv: CompactVec = (0u32..10).collect();
cv.sort_unstable();
let v: Vec<u32> = cv.iter().collect();
assert_eq!(v, (0u32..10).collect::<Vec<_>>());
}
#[test]
fn sort_single_element() {
let mut cv: CompactVec = vec![42u32].into_iter().collect();
cv.sort_unstable();
assert_eq!(cv.get(0), Some(42));
}
#[test]
fn into_iter_forward() {
let cv: CompactVec = vec![10u32, 20, 30].into_iter().collect();
let v: Vec<u32> = cv.into_iter().collect();
assert_eq!(v, [10, 20, 30]);
}
#[test]
fn into_iter_reverse() {
let cv: CompactVec = vec![10u32, 20, 30].into_iter().collect();
let v: Vec<u32> = cv.into_iter().rev().collect();
assert_eq!(v, [30, 20, 10]);
}
#[test]
fn into_iter_exact_size() {
let cv: CompactVec = (0u32..7).collect();
let mut it = cv.into_iter();
assert_eq!(it.len(), 7);
it.next();
assert_eq!(it.len(), 6);
}
#[test]
fn into_iter_partial_drop_frees_memory() {
let cv: CompactVec = (0u32..100).collect();
let mut it = cv.into_iter();
assert_eq!(it.next(), Some(0));
drop(it); }
#[test]
fn into_iter_sum() {
let cv: CompactVec = (0u32..=100).collect();
let sum: u32 = cv.into_iter().sum();
assert_eq!(sum, 5050);
}
#[test]
fn byte_len_matches_width() {
let mut cv = CompactVec::new();
assert_eq!(cv.byte_len(), 0);
for i in 0u32..4 {
cv.push(i);
}
assert_eq!(cv.width_bits(), 8);
assert_eq!(cv.byte_len(), 4);
cv.push(300);
assert_eq!(cv.width_bits(), 16);
assert_eq!(cv.byte_len(), 10); }
#[test]
fn byte_capacity_equals_cap_times_elem_size() {
let cv = CompactVec::with_capacity(8);
assert_eq!(cv.byte_capacity(), cv.capacity() * 1); }
#[test]
fn as_ref_u8_slice() {
let cv: CompactVec = vec![1u32, 2, 3].into_iter().collect();
let bytes: &[u8] = cv.as_ref();
assert_eq!(bytes, &[1u8, 2, 3]);
}
#[test]
fn as_ref_u16_little_endian() {
let mut cv = CompactVec::new();
cv.push(256u32); cv.push(1u32); assert_eq!(cv.width_bits(), 16);
let bytes: &[u8] = cv.as_ref();
assert_eq!(bytes, &[0x00, 0x01, 0x01, 0x00]);
}
#[test]
fn as_ref_empty_is_empty_slice() {
let cv = CompactVec::new();
let bytes: &[u8] = cv.as_ref();
assert!(bytes.is_empty());
}
#[test]
fn from_sorted_iter_u8() {
let cv = CompactVec::from_sorted_iter(0u32..256);
assert_eq!(cv.width_bits(), 8);
assert_eq!(cv.len(), 256);
assert_eq!(cv.get(255), Some(255));
}
#[test]
fn from_sorted_iter_u24() {
let cv = CompactVec::from_sorted_iter([1u32, 1000, 100_000]);
assert_eq!(cv.width_bits(), 24);
assert_eq!(cv.len(), 3);
assert_eq!(cv.get(2), Some(100_000));
}
#[test]
fn from_sorted_iter_u32() {
let cv = CompactVec::from_sorted_iter([0u32, u32::MAX]);
assert_eq!(cv.width_bits(), 32);
}
#[test]
fn from_sorted_iter_empty() {
let cv = CompactVec::from_sorted_iter(core::iter::empty::<u32>());
assert!(cv.is_empty());
}
#[test]
fn from_sorted_slice_matches_from_sorted_iter() {
let data: Vec<u32> = (0u32..100).map(|i| i * 3).collect();
let a = CompactVec::from_sorted_slice(&data);
let b = CompactVec::from_sorted_iter(data.iter().copied());
assert_eq!(a, b);
}
#[test]
fn from_sorted_vs_from_iter_same_values() {
let data: Vec<u32> = (0u32..=255).collect();
let sorted = CompactVec::from_sorted_slice(&data);
let regular: CompactVec = data.into_iter().collect();
assert_eq!(sorted, regular);
}
#[test]
fn borrow_iter_rev() {
let cv: CompactVec = vec![1u32, 2, 3, 4, 5].into_iter().collect();
let v: Vec<u32> = cv.iter().rev().collect();
assert_eq!(v, [5, 4, 3, 2, 1]);
}
#[test]
fn borrow_iter_double_ended_mixed() {
let cv: CompactVec = vec![1u32, 2, 3, 4, 5].into_iter().collect();
let mut it = cv.iter();
assert_eq!(it.next(), Some(1));
assert_eq!(it.next_back(), Some(5));
assert_eq!(it.next(), Some(2));
assert_eq!(it.next_back(), Some(4));
assert_eq!(it.next(), Some(3));
assert_eq!(it.next(), None);
}
}