Skip to main content

journal_core/file/
file_iterators.rs

1use super::file::JournalFile;
2use super::mmap::MemoryMap;
3use super::object::*;
4use crate::error::{JournalError, Result};
5use crate::file::value_guard::ValueGuard;
6use std::num::NonZeroU64;
7
8/// Iterator that walks through all field objects in the field hash table
9pub struct FieldIterator<'a, M: MemoryMap> {
10    pub(super) journal: &'a JournalFile<M>,
11    pub(super) field_hash_table: Option<FieldHashTable<&'a [u8]>>,
12    pub(super) current_bucket_index: usize,
13    pub(super) next_field_offset: Option<NonZeroU64>,
14}
15
16impl<M: MemoryMap> FieldIterator<'_, M> {
17    /// Advances to the next non-empty bucket
18    pub(super) fn advance_to_next_nonempty_bucket(&mut self) {
19        // If we don't have a hash table, there's nothing to iterate
20        let Some(hash_table) = &self.field_hash_table else {
21            return;
22        };
23
24        let items = &hash_table.items;
25
26        // Find the next non-empty bucket
27        while self.current_bucket_index < items.len() {
28            let bucket = items[self.current_bucket_index];
29            if bucket.head_hash_offset.is_some() {
30                self.next_field_offset = bucket.head_hash_offset;
31                return;
32            }
33            self.current_bucket_index += 1;
34        }
35
36        // No more non-empty buckets
37        self.next_field_offset = None;
38    }
39}
40
41impl<'a, M: MemoryMap> Iterator for FieldIterator<'a, M> {
42    type Item = Result<ValueGuard<'a, FieldObject<&'a [u8]>>>;
43
44    fn next(&mut self) -> Option<Self::Item> {
45        let offset = self.next_field_offset?;
46
47        match self.journal.field_ref(offset) {
48            Ok(field_guard) => {
49                // Get the next field offset before we return the guard
50                self.next_field_offset = field_guard.header.next_hash_offset;
51
52                // If we've reached the end of the chain, move to the next bucket
53                if self.next_field_offset.is_none() {
54                    self.current_bucket_index += 1;
55                    self.advance_to_next_nonempty_bucket();
56                }
57
58                Some(Ok(field_guard))
59            }
60            Err(e) => {
61                self.next_field_offset = None;
62                Some(Err(e))
63            }
64        }
65    }
66}
67
68/// Iterator that walks through all DATA objects for a specific field
69pub struct FieldDataIterator<'a, M: MemoryMap> {
70    pub(super) journal: &'a JournalFile<M>,
71    pub(super) head_data_offset: Option<NonZeroU64>,
72    pub(super) current_data_offset: Option<NonZeroU64>,
73}
74
75impl<M: MemoryMap> FieldDataIterator<'_, M> {
76    pub(super) fn restart(&mut self) {
77        self.current_data_offset = self.head_data_offset;
78    }
79}
80
81impl<'a, M: MemoryMap> Iterator for FieldDataIterator<'a, M> {
82    type Item = Result<ValueGuard<'a, DataObject<&'a [u8]>>>;
83
84    fn next(&mut self) -> Option<Self::Item> {
85        let data_offset = self.current_data_offset?;
86
87        match self.journal.data_ref(data_offset) {
88            Ok(data_guard) => {
89                // Get the next data offset before we return the guard
90                self.current_data_offset = data_guard.header.next_field_offset;
91                Some(Ok(data_guard))
92            }
93            Err(e) => {
94                self.current_data_offset = None;
95                Some(Err(e))
96            }
97        }
98    }
99}
100
101/// Iterator that walks through all DATA objects for a specific field and
102/// returns the object offset with each guard.
103pub struct FieldDataOffsetIterator<'a, M: MemoryMap> {
104    pub(super) journal: &'a JournalFile<M>,
105    pub(super) current_data_offset: Option<NonZeroU64>,
106}
107
108impl<'a, M: MemoryMap> Iterator for FieldDataOffsetIterator<'a, M> {
109    type Item = Result<(NonZeroU64, ValueGuard<'a, DataObject<&'a [u8]>>)>;
110
111    fn next(&mut self) -> Option<Self::Item> {
112        let data_offset = self.current_data_offset?;
113
114        match self.journal.data_ref(data_offset) {
115            Ok(data_guard) => {
116                self.current_data_offset = data_guard.header.next_field_offset;
117                Some(Ok((data_offset, data_guard)))
118            }
119            Err(e) => {
120                self.current_data_offset = None;
121                Some(Err(e))
122            }
123        }
124    }
125}
126
127/// Iterator that walks through all DATA objects for a specific entry
128pub struct EntryDataIterator<'a, M: MemoryMap> {
129    pub(super) journal: &'a JournalFile<M>,
130    pub(super) entry_offset: Option<NonZeroU64>,
131    pub(super) current_index: usize,
132    pub(super) total_items: usize,
133}
134
135impl<'a, M: MemoryMap> Iterator for EntryDataIterator<'a, M> {
136    type Item = Result<ValueGuard<'a, DataObject<&'a [u8]>>>;
137
138    fn next(&mut self) -> Option<Self::Item> {
139        let entry_offset = self.entry_offset?;
140
141        // If we've reached the end of the data indices, return None
142        if self.current_index >= self.total_items {
143            return None;
144        }
145
146        // Get the entry object to access the data offset
147        match self.journal.entry_ref(entry_offset) {
148            Ok(entry_guard) => {
149                let idx = self.current_index;
150                self.current_index += 1;
151
152                let data_offset = match &entry_guard.items {
153                    EntryItemsType::Regular(items) => {
154                        if idx >= items.len() {
155                            return None;
156                        }
157                        items[idx].object_offset
158                    }
159                    EntryItemsType::Compact(items) => {
160                        if idx >= items.len() {
161                            return None;
162                        }
163                        items[idx].object_offset as u64
164                    }
165                };
166
167                let data_offset = match NonZeroU64::new(data_offset) {
168                    Some(offset) => offset,
169                    None => {
170                        self.current_index = self.total_items;
171                        return Some(Err(JournalError::InvalidOffset));
172                    }
173                };
174
175                // Drop the entry guard before obtaining the data object
176                drop(entry_guard);
177
178                // Try to get the data object
179                match self.journal.data_ref(data_offset) {
180                    Ok(data_guard) => Some(Ok(data_guard)),
181                    Err(e) => {
182                        // If we can't read the data, return the error and stop iteration
183                        self.current_index = self.total_items;
184                        Some(Err(e))
185                    }
186                }
187            }
188            Err(e) => {
189                // If we can't read the entry, return the error and stop iteration
190                self.current_index = self.total_items;
191                Some(Err(e))
192            }
193        }
194    }
195}