1use crate::errors::Result;
4use crate::mmap::MemoryMappedFile;
5use crate::utils::page_size;
6use std::marker::PhantomData;
7
8pub struct ChunkIterator<'a> {
26 mmap: &'a MemoryMappedFile,
27 chunk_size: usize,
28 current_offset: u64,
29 total_len: u64,
30 buffer: Vec<u8>,
32}
33
34impl<'a> ChunkIterator<'a> {
35 pub(crate) fn new(mmap: &'a MemoryMappedFile, chunk_size: usize) -> Result<Self> {
37 let total_len = mmap.current_len()?;
38 let buffer = Vec::with_capacity(chunk_size);
40 Ok(Self {
41 mmap,
42 chunk_size,
43 current_offset: 0,
44 total_len,
45 buffer,
46 })
47 }
48}
49
50impl<'a> Iterator for ChunkIterator<'a> {
51 type Item = Result<Vec<u8>>;
52
53 fn next(&mut self) -> Option<Self::Item> {
54 if self.current_offset >= self.total_len {
55 return None;
56 }
57
58 let remaining = self.total_len - self.current_offset;
59 let chunk_len = remaining.min(self.chunk_size as u64);
60
61 self.buffer.resize(chunk_len as usize, 0);
63
64 match self.mmap.read_into(self.current_offset, &mut self.buffer) {
66 Ok(()) => {
67 self.current_offset += chunk_len;
68 Some(Ok(self.buffer.clone()))
71 }
72 Err(e) => Some(Err(e)),
73 }
74 }
75
76 fn size_hint(&self) -> (usize, Option<usize>) {
77 let remaining = self.total_len.saturating_sub(self.current_offset);
78 let chunks = (remaining as usize).div_ceil(self.chunk_size);
79 (chunks, Some(chunks))
80 }
81}
82
83impl<'a> ExactSizeIterator for ChunkIterator<'a> {}
84
85pub struct PageIterator<'a> {
104 inner: ChunkIterator<'a>,
105}
106
107impl<'a> PageIterator<'a> {
108 pub(crate) fn new(mmap: &'a MemoryMappedFile) -> Result<Self> {
110 let ps = page_size();
111 Ok(Self {
112 inner: ChunkIterator::new(mmap, ps)?,
113 })
114 }
115}
116
117impl<'a> Iterator for PageIterator<'a> {
118 type Item = Result<Vec<u8>>;
119
120 fn next(&mut self) -> Option<Self::Item> {
121 self.inner.next()
122 }
123
124 fn size_hint(&self) -> (usize, Option<usize>) {
125 self.inner.size_hint()
126 }
127}
128
129impl<'a> ExactSizeIterator for PageIterator<'a> {}
130
131pub struct ChunkIteratorMut<'a> {
137 mmap: &'a MemoryMappedFile,
138 chunk_size: usize,
139 current_offset: u64,
140 total_len: u64,
141 _phantom: PhantomData<&'a mut [u8]>,
142}
143
144impl<'a> ChunkIteratorMut<'a> {
145 pub(crate) fn new(mmap: &'a MemoryMappedFile, chunk_size: usize) -> Result<Self> {
147 let total_len = mmap.current_len()?;
148 Ok(Self {
149 mmap,
150 chunk_size,
151 current_offset: 0,
152 total_len,
153 _phantom: PhantomData,
154 })
155 }
156
157 pub fn for_each_mut<F, E>(mut self, mut f: F) -> Result<std::result::Result<(), E>>
161 where
162 F: FnMut(u64, &mut [u8]) -> std::result::Result<(), E>,
163 {
164 while self.current_offset < self.total_len {
165 let remaining = self.total_len - self.current_offset;
166 let chunk_len = remaining.min(self.chunk_size as u64);
167
168 let mut guard = self.mmap.as_slice_mut(self.current_offset, chunk_len)?;
169 let slice = guard.as_mut();
170
171 match f(self.current_offset, slice) {
172 Ok(()) => {}
173 Err(e) => return Ok(Err(e)),
174 }
175
176 self.current_offset += chunk_len;
177 }
178 Ok(Ok(()))
179 }
180}
181
182impl MemoryMappedFile {
183 #[cfg(feature = "iterator")]
207 pub fn chunks(&self, chunk_size: usize) -> ChunkIterator<'_> {
208 ChunkIterator::new(self, chunk_size).expect("chunk iterator creation should not fail")
209 }
210
211 #[cfg(feature = "iterator")]
231 pub fn pages(&self) -> PageIterator<'_> {
232 PageIterator::new(self).expect("page iterator creation should not fail")
233 }
234
235 #[cfg(feature = "iterator")]
259 pub fn chunks_mut(&self, chunk_size: usize) -> ChunkIteratorMut<'_> {
260 ChunkIteratorMut::new(self, chunk_size)
261 .expect("mutable chunk iterator creation should not fail")
262 }
263}
264
265#[cfg(test)]
266mod tests {
267 use super::*;
268 use crate::create_mmap;
269 use std::fs;
270 use std::path::PathBuf;
271
272 fn tmp_path(name: &str) -> PathBuf {
273 let mut p = std::env::temp_dir();
274 p.push(format!(
275 "mmap_io_iterator_test_{}_{}",
276 name,
277 std::process::id()
278 ));
279 p
280 }
281
282 #[test]
283 #[cfg(feature = "iterator")]
284 fn test_chunk_iterator() {
285 let path = tmp_path("chunk_iter");
286 let _ = fs::remove_file(&path);
287
288 let mmap = create_mmap(&path, 10240).expect("create");
290 for i in 0..10 {
291 let data = vec![i as u8; 1024];
292 mmap.update_region(i * 1024, &data).expect("write");
293 }
294 mmap.flush().expect("flush");
295
296 let chunks: Vec<_> = mmap
298 .chunks(1024)
299 .collect::<Result<Vec<_>>>()
300 .expect("collect chunks");
301
302 assert_eq!(chunks.len(), 10);
303 for (i, chunk) in chunks.iter().enumerate() {
304 assert_eq!(chunk.len(), 1024);
305 assert!(chunk.iter().all(|&b| b == i as u8));
306 }
307
308 let chunks: Vec<_> = mmap
310 .chunks(3000)
311 .collect::<Result<Vec<_>>>()
312 .expect("collect chunks");
313
314 assert_eq!(chunks.len(), 4); assert_eq!(chunks[3].len(), 1240);
316
317 fs::remove_file(&path).expect("cleanup");
318 }
319
320 #[test]
321 #[cfg(feature = "iterator")]
322 fn test_page_iterator() {
323 let path = tmp_path("page_iter");
324 let _ = fs::remove_file(&path);
325
326 let ps = page_size();
327 let file_size = ps * 3 + 100; let mmap = create_mmap(&path, file_size as u64).expect("create");
330
331 let pages: Vec<_> = mmap
332 .pages()
333 .collect::<Result<Vec<_>>>()
334 .expect("collect pages");
335
336 assert_eq!(pages.len(), 4); assert_eq!(pages[0].len(), ps);
338 assert_eq!(pages[1].len(), ps);
339 assert_eq!(pages[2].len(), ps);
340 assert_eq!(pages[3].len(), 100);
341
342 fs::remove_file(&path).expect("cleanup");
343 }
344
345 #[test]
346 #[cfg(feature = "iterator")]
347 fn test_mutable_chunk_iterator() {
348 let path = tmp_path("mut_chunk_iter");
349 let _ = fs::remove_file(&path);
350
351 let mmap = create_mmap(&path, 4096).expect("create");
352
353 let result = mmap.chunks_mut(1024).for_each_mut(|offset, chunk| {
355 let value = (offset / 1024) as u8;
356 chunk.fill(value);
357 Ok::<(), std::io::Error>(())
358 });
359
360 assert!(result.is_ok());
361 assert!(result.unwrap().is_ok());
362
363 mmap.flush().expect("flush");
364
365 let mut buf = [0u8; 1024];
367 for i in 0..4 {
368 mmap.read_into(i * 1024, &mut buf).expect("read");
369 assert!(buf.iter().all(|&b| b == i as u8));
370 }
371
372 fs::remove_file(&path).expect("cleanup");
373 }
374
375 #[test]
376 #[cfg(feature = "iterator")]
377 fn test_iterator_size_hint() {
378 let path = tmp_path("size_hint");
379 let _ = fs::remove_file(&path);
380
381 let mmap = create_mmap(&path, 10000).expect("create");
382
383 let iter = mmap.chunks(1000);
384 assert_eq!(iter.size_hint(), (10, Some(10)));
385
386 let iter = mmap.chunks(3000);
387 assert_eq!(iter.size_hint(), (4, Some(4)));
388
389 fs::remove_file(&path).expect("cleanup");
390 }
391
392 #[test]
393 #[cfg(feature = "iterator")]
394 fn test_empty_file_iteration() {
395 let path = tmp_path("empty_iter");
396 let _ = fs::remove_file(&path);
397
398 let mmap = create_mmap(&path, 1).expect("create"); mmap.resize(1).expect("resize"); let chunks: Vec<_> = mmap
402 .chunks(1024)
403 .collect::<Result<Vec<_>>>()
404 .expect("collect");
405
406 assert_eq!(chunks.len(), 1);
407 assert_eq!(chunks[0].len(), 1);
408
409 fs::remove_file(&path).expect("cleanup");
410 }
411}