Skip to main content

umadb_core/
slice_reader.rs

1use crate::common::{PageID, Position, Tsn};
2use crate::tags_tree_nodes::{TAG_HASH_LEN, get_tag_key_width};
3use umadb_dcb::{DcbError, DcbResult};
4use uuid::Uuid;
5
6/// A zero-copy, advancing reader for byte slices.
7pub struct SliceReader<'a> {
8    slice: &'a [u8],
9}
10
11impl<'a> SliceReader<'a> {
12    pub fn new(slice: &'a [u8]) -> Self {
13        Self { slice }
14    }
15
16    /// Safely takes `len` bytes from the front and advances the internal slice.
17    pub fn read_bytes(&mut self, len: usize) -> DcbResult<&'a [u8]> {
18        if self.slice.len() < len {
19            return Err(DcbError::DeserializationError(format!(
20                "Unexpected end of data. Needed {len} bytes, {} remaining",
21                self.slice.len()
22            )));
23        }
24        let (head, tail) = self.slice.split_at(len);
25        self.slice = tail;
26        Ok(head)
27    }
28
29    pub fn read_u8(&mut self) -> DcbResult<u8> {
30        let b = self.read_bytes(1)?;
31        Ok(b[0])
32    }
33
34    pub fn read_u16(&mut self) -> DcbResult<u16> {
35        let b = self.read_bytes(2)?;
36        Ok(u16::from_le_bytes(b.try_into().unwrap()))
37    }
38
39    pub fn read_u32(&mut self) -> DcbResult<u32> {
40        let b = self.read_bytes(4)?;
41        Ok(u32::from_le_bytes(b.try_into().unwrap()))
42    }
43
44    pub fn read_u64(&mut self) -> DcbResult<u64> {
45        let b = self.read_bytes(8)?;
46        Ok(u64::from_le_bytes(b.try_into().unwrap()))
47    }
48
49    pub fn read_uuid(&mut self) -> DcbResult<Uuid> {
50        let bytes = self.read_bytes(16)?;
51        Uuid::from_slice(bytes)
52            .map_err(|e| DcbError::DeserializationError(format!("Invalid UUID sequence: {e}")))
53    }
54
55    pub fn read_page_id(&mut self) -> DcbResult<PageID> {
56        Ok(PageID(self.read_u64()?))
57    }
58
59    pub fn read_position(&mut self) -> DcbResult<Position> {
60        Ok(Position(self.read_u64()?))
61    }
62
63    pub fn read_tsn(&mut self) -> DcbResult<Tsn> {
64        Ok(Tsn(self.read_u64()?))
65    }
66
67    /// Safely reads `len` bytes and validates them as a zero-copy UTF-8 string reference.
68    pub fn read_str(&mut self, len: usize) -> DcbResult<&'a str> {
69        let bytes = self.read_bytes(len)?;
70        std::str::from_utf8(bytes)
71            .map_err(|_| DcbError::DeserializationError("Invalid UTF-8 sequence".to_string()))
72    }
73
74    /// Safely reads `len` bytes and allocates them into an owned String.
75    pub fn read_string(&mut self, len: usize) -> DcbResult<String> {
76        // Calls our zero-copy method, then allocates if successful
77        let s = self.read_str(len)?;
78        Ok(s.to_string())
79    }
80
81    /// Reads a dynamically sized tag hash from disk and zero-pads it to the in-memory size.
82    pub fn read_tag_hash(&mut self) -> DcbResult<[u8; TAG_HASH_LEN]> {
83        let keyw = get_tag_key_width();
84        let key_bytes = self.read_bytes(keyw)?;
85
86        let mut key = [0u8; TAG_HASH_LEN];
87        // Copy the on-disk width and leave the rest as zeros
88        key[..keyw].copy_from_slice(key_bytes);
89
90        Ok(key)
91    }
92
93    /// Returns the number of unread bytes remaining.
94    pub fn remaining(&self) -> usize {
95        self.slice.len()
96    }
97}