#![cfg(target_pointer_width = "64")]
use std::io::{Read, Write};
use anyhow::{anyhow, Result};
use num_traits::ToPrimitive;
use crate::bit_vectors::BitVector;
use crate::int_vectors::prelude::*;
use crate::{utils, Serializable};
#[derive(Default, Clone, PartialEq, Eq)]
pub struct CompactVector {
chunks: BitVector,
len: usize,
width: usize,
}
impl CompactVector {
pub fn new(width: usize) -> Result<Self> {
if !(1..=64).contains(&width) {
return Err(anyhow!("width must be in 1..=64, but got {width}."));
}
Ok(Self {
chunks: BitVector::default(),
len: 0,
width,
})
}
pub fn with_capacity(capa: usize, width: usize) -> Result<Self> {
if !(1..=64).contains(&width) {
return Err(anyhow!("width must be in 1..=64, but got {width}."));
}
Ok(Self {
chunks: BitVector::with_capacity(capa * width),
len: 0,
width,
})
}
pub fn from_int(val: usize, len: usize, width: usize) -> Result<Self> {
if !(1..=64).contains(&width) {
return Err(anyhow!("width must be in 1..=64, but got {width}."));
}
if width < 64 && val >> width != 0 {
return Err(anyhow!(
"val must fit in width={width} bits, but got {val}."
));
}
let mut cv = Self::with_capacity(len, width).unwrap();
for _ in 0..len {
cv.push_int(val).unwrap();
}
Ok(cv)
}
pub fn from_slice<T>(vals: &[T]) -> Result<Self>
where
T: ToPrimitive,
{
if vals.is_empty() {
return Ok(Self::default());
}
let mut max_int = 0;
for x in vals {
max_int =
max_int.max(x.to_usize().ok_or_else(|| {
anyhow!("vals must consist only of values castable into usize.")
})?);
}
let mut cv = Self::with_capacity(vals.len(), utils::needed_bits(max_int))?;
for x in vals {
cv.push_int(x.to_usize().unwrap()).unwrap();
}
Ok(cv)
}
pub fn get_int(&self, pos: usize) -> Option<usize> {
self.chunks.get_bits(pos * self.width, self.width)
}
#[inline(always)]
pub fn set_int(&mut self, pos: usize, val: usize) -> Result<()> {
if self.len() <= pos {
return Err(anyhow!(
"pos must be no greater than self.len()={}, but got {pos}.",
self.len()
));
}
if self.width() != 64 && val >> self.width() != 0 {
return Err(anyhow!(
"val must fit in self.width()={} bits, but got {val}.",
self.width()
));
}
self.chunks
.set_bits(pos * self.width, val, self.width)
.unwrap();
Ok(())
}
#[inline(always)]
pub fn push_int(&mut self, val: usize) -> Result<()> {
if self.width() != 64 && val >> self.width() != 0 {
return Err(anyhow!(
"val must fit in self.width()={} bits, but got {val}.",
self.width()
));
}
self.chunks.push_bits(val, self.width).unwrap();
self.len += 1;
Ok(())
}
pub fn extend<I>(&mut self, vals: I) -> Result<()>
where
I: IntoIterator<Item = usize>,
{
for x in vals {
self.push_int(x)?;
}
Ok(())
}
pub const fn iter(&self) -> Iter {
Iter::new(self)
}
#[inline(always)]
pub const fn len(&self) -> usize {
self.len
}
#[inline(always)]
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn capacity(&self) -> usize {
self.chunks.capacity() / self.width()
}
#[inline(always)]
pub const fn width(&self) -> usize {
self.width
}
pub const fn bit_vector(&self) -> &BitVector {
&self.chunks
}
pub fn into_bit_vector(self) -> BitVector {
self.chunks
}
}
impl Build for CompactVector {
fn build_from_slice<T>(vals: &[T]) -> Result<Self>
where
T: ToPrimitive,
Self: Sized,
{
Self::from_slice(vals)
}
}
impl NumVals for CompactVector {
fn num_vals(&self) -> usize {
self.len()
}
}
impl Access for CompactVector {
fn access(&self, pos: usize) -> Option<usize> {
self.get_int(pos)
}
}
pub struct Iter<'a> {
cv: &'a CompactVector,
pos: usize,
}
impl<'a> Iter<'a> {
pub const fn new(cv: &'a CompactVector) -> Self {
Self { cv, pos: 0 }
}
}
impl Iterator for Iter<'_> {
type Item = usize;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
if self.pos < self.cv.len() {
let x = self.cv.access(self.pos).unwrap();
self.pos += 1;
Some(x)
} else {
None
}
}
#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.cv.len(), Some(self.cv.len()))
}
}
impl std::fmt::Debug for CompactVector {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut ints = vec![0; self.len()];
for (i, b) in ints.iter_mut().enumerate() {
*b = self.access(i).unwrap();
}
f.debug_struct("CompactVector")
.field("ints", &ints)
.field("len", &self.len)
.field("width", &self.width)
.finish()
}
}
impl Serializable for CompactVector {
fn serialize_into<W: Write>(&self, mut writer: W) -> Result<usize> {
let mut mem = self.chunks.serialize_into(&mut writer)?;
mem += self.len.serialize_into(&mut writer)?;
mem += self.width.serialize_into(&mut writer)?;
Ok(mem)
}
fn deserialize_from<R: Read>(mut reader: R) -> Result<Self> {
let chunks = BitVector::deserialize_from(&mut reader)?;
let len = usize::deserialize_from(&mut reader)?;
let width = usize::deserialize_from(&mut reader)?;
Ok(Self { chunks, len, width })
}
fn size_in_bytes(&self) -> usize {
self.chunks.size_in_bytes() + usize::size_of().unwrap() * 2
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_oob_0() {
let e = CompactVector::new(0);
assert_eq!(
e.err().map(|x| x.to_string()),
Some("width must be in 1..=64, but got 0.".to_string())
);
}
#[test]
fn test_new_oob_65() {
let e = CompactVector::new(65);
assert_eq!(
e.err().map(|x| x.to_string()),
Some("width must be in 1..=64, but got 65.".to_string())
);
}
#[test]
fn test_with_capacity_oob_0() {
let e = CompactVector::with_capacity(0, 0);
assert_eq!(
e.err().map(|x| x.to_string()),
Some("width must be in 1..=64, but got 0.".to_string())
);
}
#[test]
fn test_with_capacity_oob_65() {
let e = CompactVector::with_capacity(0, 65);
assert_eq!(
e.err().map(|x| x.to_string()),
Some("width must be in 1..=64, but got 65.".to_string())
);
}
#[test]
fn test_from_int_oob_0() {
let e = CompactVector::from_int(0, 0, 0);
assert_eq!(
e.err().map(|x| x.to_string()),
Some("width must be in 1..=64, but got 0.".to_string())
);
}
#[test]
fn test_from_int_oob_65() {
let e = CompactVector::from_int(0, 0, 65);
assert_eq!(
e.err().map(|x| x.to_string()),
Some("width must be in 1..=64, but got 65.".to_string())
);
}
#[test]
fn test_from_int_unfit() {
let e = CompactVector::from_int(4, 0, 2);
assert_eq!(
e.err().map(|x| x.to_string()),
Some("val must fit in width=2 bits, but got 4.".to_string())
);
}
#[test]
fn test_from_slice_uncastable() {
let e = CompactVector::from_slice(&[u128::MAX]);
assert_eq!(
e.err().map(|x| x.to_string()),
Some("vals must consist only of values castable into usize.".to_string())
);
}
#[test]
fn test_set_int_oob() {
let mut cv = CompactVector::from_int(0, 1, 2).unwrap();
let e = cv.set_int(1, 1);
assert_eq!(
e.err().map(|x| x.to_string()),
Some("pos must be no greater than self.len()=1, but got 1.".to_string())
);
}
#[test]
fn test_set_int_unfit() {
let mut cv = CompactVector::from_int(0, 1, 2).unwrap();
let e = cv.set_int(0, 4);
assert_eq!(
e.err().map(|x| x.to_string()),
Some("val must fit in self.width()=2 bits, but got 4.".to_string())
);
}
#[test]
fn test_push_int_unfit() {
let mut cv = CompactVector::new(2).unwrap();
let e = cv.push_int(4);
assert_eq!(
e.err().map(|x| x.to_string()),
Some("val must fit in self.width()=2 bits, but got 4.".to_string())
);
}
#[test]
fn test_extend_unfit() {
let mut cv = CompactVector::new(2).unwrap();
let e = cv.extend([4]);
assert_eq!(
e.err().map(|x| x.to_string()),
Some("val must fit in self.width()=2 bits, but got 4.".to_string())
);
}
#[test]
fn test_64b() {
let mut cv = CompactVector::new(64).unwrap();
cv.push_int(42).unwrap();
assert_eq!(cv.get_int(0), Some(42));
cv.set_int(0, 334).unwrap();
assert_eq!(cv.get_int(0), Some(334));
}
#[test]
fn test_64b_from_int() {
let cv = CompactVector::from_int(42, 1, 64).unwrap();
assert_eq!(cv.get_int(0), Some(42));
}
#[test]
fn test_serialize() {
let mut bytes = vec![];
let cv = CompactVector::from_slice(&[7, 334, 1, 2]).unwrap();
let size = cv.serialize_into(&mut bytes).unwrap();
let other = CompactVector::deserialize_from(&bytes[..]).unwrap();
assert_eq!(cv, other);
assert_eq!(size, bytes.len());
assert_eq!(size, cv.size_in_bytes());
}
}