mod anchored;
mod header;
mod read;
mod write;
use std::ops::RangeInclusive;
use crate::*;
pub(crate) use anchored::*;
pub(crate) use header::*;
pub static DEFAULT_SLOT_CAPACITY: usize = 500;
pub static STORAGE_SLOT_SIG: [u8; 8] = [166u8, 177u8, 188u8, 199u8, 199u8, 188u8, 177u8, 166u8];
#[derive(Debug)]
pub struct Slot {
pub lenghts: Vec<u64>,
pub capacity: u64,
pub crc: [u8; 4],
}
impl Slot {
pub fn new(lenghts: Vec<u64>, capacity: u64, crc: [u8; 4]) -> Self {
Self {
lenghts,
capacity,
crc,
}
}
pub fn expand(&self) -> (Option<u64>, Option<usize>, [u8; 4]) {
(
self.get_free_slot_offset(),
self.get_free_slot_index(),
self.crc,
)
}
pub fn width(&self) -> u64 {
if self.is_full() {
return self.lenghts.iter().sum();
}
let Some(free_pos) = self.lenghts.iter().position(|ln| ln == &0) else {
return self.lenghts.iter().sum();
};
self.lenghts[..free_pos].iter().sum()
}
pub fn iter(&self) -> SlotIterator<'_> {
SlotIterator::new(self)
}
pub fn get_slot_offset(&self, nth: usize) -> Option<u64> {
if nth >= self.lenghts.len() {
return None;
}
Some(self.lenghts[..nth].iter().sum::<u64>() + self.size())
}
pub fn is_empty(&self, nth: usize) -> Result<bool, Error> {
self.lenghts
.get(nth)
.map(|l| l == &0)
.ok_or(Error::OutOfBounds(self.lenghts.len(), nth))
}
pub fn count(&self) -> usize {
self.lenghts.iter().filter(|&&ln| ln > 0).count()
}
pub fn is_used(&self, idx: usize) -> bool {
self.lenghts.get(idx).is_some_and(|ln| *ln > 0)
}
pub fn offset_of(&self, idx: usize) -> Option<u64> {
if idx == 0 {
return Some(0);
}
if idx >= self.lenghts.len() || !self.is_used(idx) {
return None;
}
Some(self.lenghts[..idx].iter().sum::<u64>())
}
pub fn is_full(&self) -> bool {
if let Some(ln) = self.lenghts.last()
&& ln > &0
{
true
} else {
false
}
}
pub fn get_free_slot_offset(&self) -> Option<u64> {
if self.is_full() {
return None;
}
let free_pos = self.lenghts.iter().position(|ln| ln == &0)?;
Some(self.lenghts[..free_pos].iter().sum::<u64>() + self.size())
}
pub fn get_free_slot_index(&self) -> Option<usize> {
if self.is_full() {
return None;
}
self.lenghts.iter().position(|ln| ln == &0)
}
pub fn overwrite_crc(&mut self) {
self.crc = self.crc();
}
pub fn insert(&mut self, length: u64) -> Result<(), Error> {
let free_pos = self
.lenghts
.iter()
.position(|ln| ln == &0)
.ok_or(Error::CannotInsertIntoSlot)?;
self.lenghts[free_pos] = length;
self.overwrite_crc();
Ok(())
}
}
impl Default for Slot {
fn default() -> Self {
let mut slot = Self::new(
vec![0u64; DEFAULT_SLOT_CAPACITY],
DEFAULT_SLOT_CAPACITY as u64,
[0u8; 4],
);
slot.overwrite_crc();
slot
}
}
impl Size for Slot {
fn size(&self) -> u64 {
SlotHeader::ssize()
+ self.capacity * std::mem::size_of::<u64>() as u64
+ std::mem::size_of::<u32>() as u64
}
}
impl CrcU32 for Slot {
fn crc(&self) -> [u8; 4] {
let mut hasher = crc32fast::Hasher::new();
hasher.update(&self.capacity.to_le_bytes());
hasher.update(
&self
.lenghts
.iter()
.flat_map(|ln| ln.to_le_bytes())
.collect::<Vec<u8>>(),
);
hasher.finalize().to_le_bytes()
}
}
pub struct SlotIterator<'a> {
slot: &'a Slot,
next: usize,
offset: u64,
}
impl<'a> SlotIterator<'a> {
pub fn new(slot: &'a Slot) -> Self {
SlotIterator {
slot,
next: 0,
offset: 0,
}
}
}
impl Iterator for SlotIterator<'_> {
type Item = RangeInclusive<u64>;
fn next(&mut self) -> Option<Self::Item> {
let ln = self.slot.lenghts.get(self.next)?;
if ln == &0 {
return None;
}
let range = RangeInclusive::new(
self.offset + self.slot.size(),
self.offset + *ln + self.slot.size(),
);
self.next += 1;
self.offset += *ln;
Some(range)
}
}
#[cfg(test)]
mod tests {
use super::Slot;
use crate::{CrcU32, Error, STORAGE_SLOT_SIG, Size, StaticSize};
#[test]
fn slot_default_shape_and_crc_are_valid() {
let slot = Slot::default();
assert_eq!(slot.lenghts.len(), crate::DEFAULT_SLOT_CAPACITY);
assert_eq!(slot.capacity as usize, crate::DEFAULT_SLOT_CAPACITY);
assert_eq!(slot.count(), 0);
assert!(!slot.is_full());
assert_eq!(slot.crc, slot.crc());
assert_eq!(
slot.size(),
crate::storage::slot::SlotHeader::ssize()
+ slot.capacity * std::mem::size_of::<u64>() as u64
+ std::mem::size_of::<u32>() as u64
);
let _ = STORAGE_SLOT_SIG; }
#[test]
fn slot_insert_offsets_iter_expand_and_helpers() {
let mut slot = Slot::new(vec![0, 0, 0], 3, [0; 4]);
slot.overwrite_crc();
assert_eq!(slot.get_free_slot_index(), Some(0));
assert_eq!(slot.get_free_slot_offset(), Some(slot.size()));
assert_eq!(slot.get_slot_offset(0), Some(slot.size()));
assert_eq!(slot.offset_of(0), Some(0));
assert!(!slot.is_used(0));
assert_eq!(slot.width(), 0);
slot.insert(10).expect("insert first chunk");
slot.insert(20).expect("insert second chunk");
assert_eq!(slot.count(), 2);
assert!(slot.is_used(0));
assert!(slot.is_used(1));
assert!(!slot.is_used(2));
assert_eq!(slot.offset_of(1), Some(10));
assert_eq!(slot.get_slot_offset(1), Some(slot.size() + 10));
assert_eq!(slot.width(), 30);
let ranges: Vec<_> = slot.iter().collect();
assert_eq!(ranges.len(), 2);
assert_eq!(*ranges[0].start(), slot.size());
assert_eq!(*ranges[0].end(), slot.size() + 10);
assert_eq!(*ranges[1].start(), slot.size() + 10);
assert_eq!(*ranges[1].end(), slot.size() + 30);
let (free_off, free_idx, crc) = slot.expand();
assert_eq!(free_idx, Some(2));
assert_eq!(free_off, Some(slot.size() + 30));
assert_eq!(crc, slot.crc);
slot.insert(5).expect("insert third chunk");
assert!(slot.is_full());
assert_eq!(slot.get_free_slot_index(), None);
assert_eq!(slot.get_free_slot_offset(), None);
assert!(matches!(slot.insert(1), Err(Error::CannotInsertIntoSlot)));
}
#[test]
fn slot_empty_and_bounds_checks() {
let slot = Slot::new(vec![11, 0], 2, [0; 4]);
assert!(matches!(slot.is_empty(0), Ok(false)));
assert!(matches!(slot.is_empty(1), Ok(true)));
assert!(matches!(slot.is_empty(2), Err(Error::OutOfBounds(2, 2))));
assert_eq!(slot.get_slot_offset(2), None);
assert_eq!(slot.offset_of(2), None);
}
}