pub const PACKED_MAX: usize = u16::MAX as usize;
#[cfg(not(feature = "std"))]
use crate::nostd_prelude::*;
#[cfg(not(feature = "std"))]
use alloc::vec;
pub type ColumnNames = alloc::sync::Arc<[Vec<u8>]>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PackedRow(Box<PackedInner>);
#[derive(Debug, Clone, PartialEq, Eq)]
struct PackedInner {
cols: ColumnNames,
buf: Box<[u8]>,
}
impl PackedRow {
pub fn build(names: &ColumnNames, cols: &[Option<&[u8]>]) -> Option<Self> {
debug_assert_eq!(names.len(), cols.len(), "one value slot per declared column");
let ncol = u16::try_from(cols.len()).ok()?;
let total: usize = cols.iter().flatten().map(|v| v.len()).sum();
if total > PACKED_MAX {
return None;
}
let bitmap = ncol.div_ceil(8) as usize;
let header = 2 + bitmap + cols.len() * 2;
let mut buf = vec![0u8; header + total];
buf[..2].copy_from_slice(&ncol.to_le_bytes());
let mut end = 0usize;
for (i, c) in cols.iter().enumerate() {
if let Some(v) = c {
buf[2 + i / 8] |= 1 << (i % 8);
buf[header + end..header + end + v.len()].copy_from_slice(v);
end += v.len();
}
let at = 2 + bitmap + i * 2;
buf[at..at + 2].copy_from_slice(&(end as u16).to_le_bytes());
}
Some(PackedRow(Box::new(PackedInner { cols: names.clone(), buf: buf.into_boxed_slice() })))
}
pub fn columns(&self) -> usize {
u16::from_le_bytes([self.0.buf[0], self.0.buf[1]]) as usize
}
pub fn has(&self, i: usize) -> bool {
i < self.columns() && self.0.buf[2 + i / 8] & (1 << (i % 8)) != 0
}
pub fn get_named(&self, field: &[u8]) -> Option<&[u8]> {
let i = self.0.cols.iter().position(|c| c == field)?;
self.get(i)
}
pub fn has_named(&self, field: &[u8]) -> bool {
self.0.cols.iter().position(|c| c == field).is_some_and(|i| self.has(i))
}
pub fn get(&self, i: usize) -> Option<&[u8]> {
if !self.has(i) {
return None;
}
let bitmap = (self.columns() as u16).div_ceil(8) as usize;
let header = 2 + bitmap + self.columns() * 2;
let end_at = |j: usize| {
let at = 2 + bitmap + j * 2;
u16::from_le_bytes([self.0.buf[at], self.0.buf[at + 1]]) as usize
};
let start = if i == 0 { 0 } else { end_at(i - 1) };
Some(&self.0.buf[header + start..header + end_at(i)])
}
pub fn set_same_width(&mut self, i: usize, v: &[u8]) -> bool {
let Some(old) = self.get(i) else { return false };
if old.len() != v.len() {
return false;
}
let bitmap = (self.columns() as u16).div_ceil(8) as usize;
let header = 2 + bitmap + self.columns() * 2;
let start = if i == 0 {
0
} else {
let at = 2 + bitmap + (i - 1) * 2;
u16::from_le_bytes([self.0.buf[at], self.0.buf[at + 1]]) as usize
};
self.0.buf[header + start..header + start + v.len()].copy_from_slice(v);
true
}
pub fn with_column(&self, i: usize, v: Option<&[u8]>) -> Option<Self> {
let mut cols: Vec<Option<&[u8]>> = (0..self.columns()).map(|j| self.get(j)).collect();
*cols.get_mut(i)? = v;
PackedRow::build(&self.0.cols, &cols)
}
pub fn names(&self) -> &ColumnNames {
&self.0.cols
}
pub fn fields(&self) -> impl Iterator<Item = (&[u8], &[u8])> {
(0..self.columns())
.filter_map(move |i| Some((self.0.cols.get(i)?.as_slice(), self.get(i)?)))
}
pub fn heap_bytes(&self) -> usize {
self.0.buf.len() + core::mem::size_of::<PackedInner>()
}
pub(crate) fn buffer(&self) -> &[u8] {
&self.0.buf
}
pub(crate) fn footprint(&self) -> u64 {
let inner = kevy_map::malloc_footprint(core::mem::size_of::<PackedInner>());
(inner + kevy_map::malloc_footprint(self.0.buf.len())) as u64
}
pub fn len(&self) -> usize {
(0..self.columns()).filter(|&i| self.has(i)).count()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
mod store_ops;
#[cfg(test)]
mod tests;