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) current_data_offset: Option<NonZeroU64>,
72}
73
74impl<'a, M: MemoryMap> Iterator for FieldDataIterator<'a, M> {
75    type Item = Result<ValueGuard<'a, DataObject<&'a [u8]>>>;
76
77    fn next(&mut self) -> Option<Self::Item> {
78        let data_offset = self.current_data_offset?;
79
80        match self.journal.data_ref(data_offset) {
81            Ok(data_guard) => {
82                // Get the next data offset before we return the guard
83                self.current_data_offset = data_guard.header.next_field_offset;
84                Some(Ok(data_guard))
85            }
86            Err(e) => {
87                self.current_data_offset = None;
88                Some(Err(e))
89            }
90        }
91    }
92}
93
94/// Iterator that walks through all DATA objects for a specific field and
95/// returns the object offset with each guard.
96pub struct FieldDataOffsetIterator<'a, M: MemoryMap> {
97    pub(super) journal: &'a JournalFile<M>,
98    pub(super) current_data_offset: Option<NonZeroU64>,
99}
100
101impl<'a, M: MemoryMap> Iterator for FieldDataOffsetIterator<'a, M> {
102    type Item = Result<(NonZeroU64, ValueGuard<'a, DataObject<&'a [u8]>>)>;
103
104    fn next(&mut self) -> Option<Self::Item> {
105        let data_offset = self.current_data_offset?;
106
107        match self.journal.data_ref(data_offset) {
108            Ok(data_guard) => {
109                self.current_data_offset = data_guard.header.next_field_offset;
110                Some(Ok((data_offset, data_guard)))
111            }
112            Err(e) => {
113                self.current_data_offset = None;
114                Some(Err(e))
115            }
116        }
117    }
118}
119
120/// Iterator that walks through all DATA objects for a specific entry
121pub struct EntryDataIterator<'a, M: MemoryMap> {
122    pub(super) journal: &'a JournalFile<M>,
123    pub(super) entry_offset: Option<NonZeroU64>,
124    pub(super) current_index: usize,
125    pub(super) total_items: usize,
126}
127
128impl<'a, M: MemoryMap> Iterator for EntryDataIterator<'a, M> {
129    type Item = Result<ValueGuard<'a, DataObject<&'a [u8]>>>;
130
131    fn next(&mut self) -> Option<Self::Item> {
132        let entry_offset = self.entry_offset?;
133
134        // If we've reached the end of the data indices, return None
135        if self.current_index >= self.total_items {
136            return None;
137        }
138
139        // Get the entry object to access the data offset
140        match self.journal.entry_ref(entry_offset) {
141            Ok(entry_guard) => {
142                let idx = self.current_index;
143                self.current_index += 1;
144
145                let data_offset = match &entry_guard.items {
146                    EntryItemsType::Regular(items) => {
147                        if idx >= items.len() {
148                            return None;
149                        }
150                        items[idx].object_offset
151                    }
152                    EntryItemsType::Compact(items) => {
153                        if idx >= items.len() {
154                            return None;
155                        }
156                        items[idx].object_offset as u64
157                    }
158                };
159
160                let data_offset = match NonZeroU64::new(data_offset) {
161                    Some(offset) => offset,
162                    None => {
163                        self.current_index = self.total_items;
164                        return Some(Err(JournalError::InvalidOffset));
165                    }
166                };
167
168                // Drop the entry guard before obtaining the data object
169                drop(entry_guard);
170
171                // Try to get the data object
172                match self.journal.data_ref(data_offset) {
173                    Ok(data_guard) => Some(Ok(data_guard)),
174                    Err(e) => {
175                        // If we can't read the data, return the error and stop iteration
176                        self.current_index = self.total_items;
177                        Some(Err(e))
178                    }
179                }
180            }
181            Err(e) => {
182                // If we can't read the entry, return the error and stop iteration
183                self.current_index = self.total_items;
184                Some(Err(e))
185            }
186        }
187    }
188}