Skip to main content

qualia_core_db/q42/volume/
cursor.rs

1//! Caller-buffered sequential SuperBlock cursor.
2
3use std::io;
4
5use super::super::{Q42Volume, QUINS_PER_BLOCK, SUPERBLOCK_HEADER, SUPERBLOCK_SIZE};
6
7/// Metadata for one decoded SuperBlock returned by [`Q42BlockCursor`].
8#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9pub struct Q42BlockMeta {
10    pub block_index: usize,
11    pub live_quins: usize,
12}
13
14/// Sequential, caller-buffered view of a Q42 volume's SuperBlocks.
15pub struct Q42BlockCursor<'a> {
16    volume: &'a Q42Volume,
17    next_index: usize,
18}
19
20impl<'a> Q42BlockCursor<'a> {
21    pub(super) fn new(volume: &'a Q42Volume) -> Self {
22        Self {
23            volume,
24            next_index: 0,
25        }
26    }
27
28    /// Decode the next block into `out`.  No block-sized allocation occurs in
29    /// this cursor or the underlying decompressor.
30    pub fn next_into(&mut self, out: &mut [u8]) -> io::Result<Option<Q42BlockMeta>> {
31        if self.next_index >= self.volume.block_count() as usize {
32            return Ok(None);
33        }
34        let block_index = self.next_index;
35        self.next_index += 1;
36        let decoded = self.volume.read_superblock_into(block_index, out)?;
37        if decoded != SUPERBLOCK_SIZE {
38            return Err(io::Error::new(
39                io::ErrorKind::InvalidData,
40                "Q42 block did not decode to a complete SuperBlock",
41            ));
42        }
43        let live_quins = u64::from_le_bytes(out[16..24].try_into().unwrap()) as usize;
44        if live_quins > QUINS_PER_BLOCK {
45            return Err(io::Error::new(
46                io::ErrorKind::InvalidData,
47                "Q42 SuperBlock declares more live Quins than its ledger holds",
48            ));
49        }
50        if SUPERBLOCK_HEADER + live_quins * crate::q42_volume::QUIN_SIZE > SUPERBLOCK_SIZE {
51            return Err(io::Error::new(
52                io::ErrorKind::InvalidData,
53                "Q42 SuperBlock live Quin count exceeds its decoded bounds",
54            ));
55        }
56        Ok(Some(Q42BlockMeta {
57            block_index,
58            live_quins,
59        }))
60    }
61
62    #[inline]
63    pub fn next_index(&self) -> usize {
64        self.next_index
65    }
66}
67
68impl Q42Volume {
69    /// Return a sequential cursor which decodes into a caller-owned buffer.
70    pub fn block_cursor(&self) -> Q42BlockCursor<'_> {
71        Q42BlockCursor::new(self)
72    }
73}