Skip to main content

lxdb_engine/
iter.rs

1use std::{iter::FusedIterator, marker::PhantomData};
2
3use lxdb_format::{AdjacencyRecord, BinaryRecord, FormatError, RelationRecord, TokenRecord};
4
5/// Iterates over fixed-size binary records without allocating a collection.
6#[derive(Debug, Clone)]
7pub struct RecordIter<'a, T> {
8    bytes: &'a [u8],
9    cursor: usize,
10    marker: PhantomData<T>,
11}
12
13impl<'a, T> RecordIter<'a, T>
14where
15    T: BinaryRecord,
16{
17    pub(crate) const fn new(bytes: &'a [u8]) -> Self {
18        Self { bytes, cursor: 0, marker: PhantomData }
19    }
20
21    fn remaining(&self) -> usize {
22        (self.bytes.len() - self.cursor) / T::SIZE
23    }
24}
25
26impl<T> Iterator for RecordIter<'_, T>
27where
28    T: BinaryRecord,
29{
30    type Item = Result<T, FormatError>;
31
32    fn next(&mut self) -> Option<Self::Item> {
33        if self.cursor >= self.bytes.len() {
34            return None;
35        }
36
37        let end = self.cursor + T::SIZE;
38
39        let record = T::decode(&self.bytes[self.cursor..end]);
40
41        self.cursor = end;
42
43        Some(record)
44    }
45
46    fn size_hint(&self) -> (usize, Option<usize>) {
47        let remaining = self.remaining();
48
49        (remaining, Some(remaining))
50    }
51}
52
53impl<T> ExactSizeIterator for RecordIter<'_, T>
54where
55    T: BinaryRecord,
56{
57    fn len(&self) -> usize {
58        self.remaining()
59    }
60}
61
62impl<T> FusedIterator for RecordIter<'_, T> where T: BinaryRecord {}
63
64pub type TokenRecordIter<'a> = RecordIter<'a, TokenRecord>;
65
66pub type RelationRecordIter<'a> = RecordIter<'a, RelationRecord>;
67
68pub type AdjacencyRecordIter<'a> = RecordIter<'a, AdjacencyRecord>;
69
70#[cfg(test)]
71mod tests {
72    use lxdb_format::{RelationRecord, TokenRecord};
73
74    use super::RecordIter;
75
76    #[test]
77    fn iterates_over_token_records() {
78        let first = TokenRecord::new(0, 0, 4);
79
80        let second = TokenRecord::new(1, 4, 8);
81
82        let mut bytes = Vec::new();
83
84        bytes.extend_from_slice(&first.encode());
85        bytes.extend_from_slice(&second.encode());
86
87        let mut records = RecordIter::<TokenRecord>::new(&bytes);
88
89        assert_eq!(records.len(), 2);
90
91        let decoded_first =
92            records.next().expect("first record should exist").expect("first record should decode");
93
94        assert_eq!(decoded_first, first);
95        assert_eq!(records.len(), 1);
96
97        let decoded_second = records
98            .next()
99            .expect("second record should exist")
100            .expect("second record should decode");
101
102        assert_eq!(decoded_second, second);
103        assert_eq!(records.len(), 0);
104        assert!(records.next().is_none());
105        assert!(records.next().is_none());
106    }
107
108    #[test]
109    fn uses_the_same_iterator_for_relation_records() {
110        let relation = RelationRecord::new(7, 2, 5, 0.75);
111
112        let bytes = relation.encode();
113
114        let mut records = RecordIter::<RelationRecord>::new(&bytes);
115
116        let decoded = records
117            .next()
118            .expect("relation record should exist")
119            .expect("relation record should decode");
120
121        assert_eq!(decoded.id(), relation.id());
122        assert_eq!(decoded.source(), relation.source());
123        assert_eq!(decoded.target(), relation.target());
124
125        assert_eq!(decoded.weight().to_bits(), relation.weight().to_bits(),);
126
127        assert!(records.next().is_none());
128    }
129
130    #[test]
131    fn empty_record_iterator_is_fused() {
132        let mut records = RecordIter::<TokenRecord>::new(&[]);
133
134        assert_eq!(records.len(), 0);
135        assert!(records.next().is_none());
136        assert!(records.next().is_none());
137    }
138}