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 fn len(&self) -> usize {
(0..self.columns()).filter(|&i| self.has(i)).count()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[cfg(test)]
pub(crate) mod tests {
use super::*;
pub(crate) fn names(n: usize) -> ColumnNames {
(0..n).map(|i| format!("c{i}").into_bytes()).collect()
}
#[test]
fn round_trips_every_column_including_absent_and_empty() {
let cols: Vec<Option<&[u8]>> =
vec![Some(&b"id7"[..]), None, Some(&b""[..]), Some(&b"a longer value"[..])];
let r = PackedRow::build(&names(cols.len()), &cols).expect("fits");
assert_eq!(r.columns(), 4);
for (i, want) in cols.iter().enumerate() {
assert_eq!(r.get(i), *want, "column {i}");
}
assert!(!r.has(1));
assert!(r.has(2));
assert_eq!(r.len(), 3);
}
#[test]
fn the_cost_scales_with_the_row_rather_than_sitting_on_a_floor() {
let three = PackedRow::build(&names(3), &[Some(&b"x"[..]); 3]).expect("fits");
let twelve = PackedRow::build(&names(12), &[Some(&b"x"[..]); 12]).expect("fits");
assert!(
twelve.heap_bytes() > three.heap_bytes(),
"a wider row must cost more, not the same: {} vs {}",
three.heap_bytes(),
twelve.heap_bytes()
);
let inner = core::mem::size_of::<PackedInner>();
assert_eq!(three.heap_bytes(), (2 + 1 + 3 * 2 + 3) + inner);
assert_eq!(twelve.heap_bytes(), (2 + 2 + 12 * 2 + 12) + inner);
}
#[test]
fn replacing_a_column_leaves_the_others_alone() {
let r =
PackedRow::build(&names(3), &[Some(&b"a"[..]), Some(&b"bb"[..]), Some(&b"ccc"[..])])
.expect("fits");
let r2 = r.with_column(1, Some(b"REPLACED")).expect("fits");
assert_eq!(r2.get(0), Some(&b"a"[..]));
assert_eq!(r2.get(1), Some(&b"REPLACED"[..]));
assert_eq!(r2.get(2), Some(&b"ccc"[..]));
let r3 = r.with_column(0, None).expect("fits");
assert!(!r3.has(0));
assert_eq!(r3.get(2), Some(&b"ccc"[..]));
}
#[test]
fn an_in_place_write_touches_exactly_one_column() {
let n = names(4);
let mut r = PackedRow::build(
&n,
&[Some(&b"aaa"[..]), Some(&b"bbb"[..]), Some(&b"ccc"[..]), Some(&b"ddd"[..])],
)
.expect("fits");
assert!(r.set_same_width(1, b"XXX"), "same width goes in place");
assert_eq!(r.get(0), Some(&b"aaa"[..]), "left neighbour untouched");
assert_eq!(r.get(1), Some(&b"XXX"[..]));
assert_eq!(r.get(2), Some(&b"ccc"[..]), "right neighbour untouched");
assert_eq!(r.get(3), Some(&b"ddd"[..]));
assert!(r.set_same_width(0, b"ZZZ"));
assert_eq!(r.get(0), Some(&b"ZZZ"[..]));
assert_eq!(r.get(1), Some(&b"XXX"[..]));
assert!(r.set_same_width(3, b"WWW"));
assert_eq!(r.get(2), Some(&b"ccc"[..]));
assert_eq!(r.get(3), Some(&b"WWW"[..]));
}
#[test]
fn an_in_place_write_refuses_anything_that_would_move_an_offset() {
let n = names(3);
let mut r =
PackedRow::build(&n, &[Some(&b"aa"[..]), None, Some(&b"cc"[..])]).expect("fits");
assert!(!r.set_same_width(0, b"aaa"), "wider must rebuild");
assert!(!r.set_same_width(0, b"a"), "narrower must rebuild");
assert!(!r.set_same_width(1, b"xx"), "an absent column must rebuild");
assert!(!r.set_same_width(9, b"xx"), "out of range");
assert_eq!(r.get(0), Some(&b"aa"[..]));
assert_eq!(r.get(2), Some(&b"cc"[..]));
assert!(!r.has(1));
}
#[test]
fn looks_a_column_up_by_the_name_the_wire_uses() {
let n: ColumnNames = vec![b"id".to_vec(), b"name".to_vec(), b"dept".to_vec()].into();
let r = PackedRow::build(&n, &[Some(&b"7"[..]), None, Some(&b"eng"[..])]).expect("fits");
assert_eq!(r.get_named(b"id"), Some(&b"7"[..]));
assert_eq!(r.get_named(b"dept"), Some(&b"eng"[..]));
assert_eq!(r.get_named(b"name"), None);
assert!(!r.has_named(b"name"));
assert_eq!(r.get_named(b"nosuch"), None);
assert!(!r.has_named(b"nosuch"));
}
#[test]
fn refuses_a_payload_it_cannot_address() {
let big = vec![0u8; PACKED_MAX + 1];
assert!(PackedRow::build(&names(1), &[Some(&big[..])]).is_none());
let just = vec![0u8; PACKED_MAX];
assert!(PackedRow::build(&names(1), &[Some(&just[..])]).is_some());
}
}
#[cfg(test)]
mod cost_tests {
use super::tests::names;
use super::*;
#[test]
fn a_packed_row_costs_less_than_the_table_it_replaces() {
const TABLE_REQUEST: usize = 16 * 48 + 16 + 16; const ARC_AND_MAP: usize = 16 + 56;
for (ncol, vlen) in [(3usize, 400usize), (7, 400), (12, 400)] {
let v = vec![b'x'; vlen / ncol];
let cols: Vec<Option<&[u8]>> = (0..ncol).map(|_| Some(&v[..])).collect();
let packed = PackedRow::build(&names(ncol), &cols).expect("fits").heap_bytes();
let today = TABLE_REQUEST + ARC_AND_MAP + vlen;
assert!(
packed * 2 < today,
"{ncol} columns: packed {packed} B is not less than half of today's {today} B"
);
}
}
#[test]
fn the_cost_is_not_flat_in_the_column_count() {
let v = [b'x'; 32];
let w = |n: usize| {
PackedRow::build(&names(n), &(0..n).map(|_| Some(&v[..])).collect::<Vec<_>>())
.expect("fits")
.heap_bytes()
};
let (a, b) = (w(3), w(12));
assert_eq!(b - a, 9 * (32 + 2) + 1, "growth is payload + ends + bitmap byte");
}
}
impl crate::Store {
#[doc(hidden)]
pub fn is_packed(&mut self, key: &[u8]) -> bool {
matches!(self.live_entry(key).map(|e| &e.value), Some(crate::Value::PackedRow(_)))
}
pub fn packed_rows_enabled(&self) -> bool {
self.packed_rows
}
pub fn set_packed_rows(&mut self, on: bool) {
self.packed_rows = on;
}
fn already_packed(&mut self, key: &[u8]) -> bool {
self.is_packed(key)
}
pub fn pack_row(&mut self, key: &[u8], names: &[Vec<u8>]) {
if self.already_packed(key) {
return;
}
let Ok(Some(pairs)) = self.hash_pairs(key) else { return };
if pairs.iter().any(|(f, _)| !names.iter().any(|n| n == f)) {
return;
}
let cols: Vec<Option<&[u8]>> = names
.iter()
.map(|n| pairs.iter().find(|(f, _)| f == n).map(|(_, v)| v.as_slice()))
.collect();
let shared: ColumnNames = names.to_vec().into();
let Some(row) = PackedRow::build(&shared, &cols) else { return };
if let Some(e) = self.live_entry_mut(key) {
e.value = crate::Value::PackedRow(row);
}
self.reweigh_entry(key);
}
}