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}
31
32impl<'a> ChunkIterator<'a> {
33 pub(crate) fn new(mmap: &'a MemoryMappedFile, chunk_size: usize) -> Result<Self> {
35 let total_len = mmap.current_len()?;
36 Ok(Self {
37 mmap,
38 chunk_size,
39 current_offset: 0,
40 total_len,
41 })
42 }
43}
44
45impl<'a> Iterator for ChunkIterator<'a> {
46 type Item = Result<Vec<u8>>;
47
48 fn next(&mut self) -> Option<Self::Item> {
49 if self.current_offset >= self.total_len {
50 return None;
51 }
52
53 let remaining = self.total_len - self.current_offset;
54 let chunk_len = remaining.min(self.chunk_size as u64);
55
56 let mut buffer = vec![0u8; chunk_len as usize];
58 match self.mmap.read_into(self.current_offset, &mut buffer) {
59 Ok(()) => {
60 self.current_offset += chunk_len;
61 Some(Ok(buffer))
62 }
63 Err(e) => Some(Err(e)),
64 }
65 }
66
67 fn size_hint(&self) -> (usize, Option<usize>) {
68 let remaining = self.total_len.saturating_sub(self.current_offset);
69 let chunks = (remaining as usize).div_ceil(self.chunk_size);
70 (chunks, Some(chunks))
71 }
72}
73
74impl<'a> ExactSizeIterator for ChunkIterator<'a> {}
75
76pub struct PageIterator<'a> {
95 inner: ChunkIterator<'a>,
96}
97
98impl<'a> PageIterator<'a> {
99 pub(crate) fn new(mmap: &'a MemoryMappedFile) -> Result<Self> {
101 let ps = page_size();
102 Ok(Self {
103 inner: ChunkIterator::new(mmap, ps)?,
104 })
105 }
106}
107
108impl<'a> Iterator for PageIterator<'a> {
109 type Item = Result<Vec<u8>>;
110
111 fn next(&mut self) -> Option<Self::Item> {
112 self.inner.next()
113 }
114
115 fn size_hint(&self) -> (usize, Option<usize>) {
116 self.inner.size_hint()
117 }
118}
119
120impl<'a> ExactSizeIterator for PageIterator<'a> {}
121
122pub struct ChunkIteratorMut<'a> {
128 mmap: &'a MemoryMappedFile,
129 chunk_size: usize,
130 current_offset: u64,
131 total_len: u64,
132 _phantom: PhantomData<&'a mut [u8]>,
133}
134
135impl<'a> ChunkIteratorMut<'a> {
136 pub(crate) fn new(mmap: &'a MemoryMappedFile, chunk_size: usize) -> Result<Self> {
138 let total_len = mmap.current_len()?;
139 Ok(Self {
140 mmap,
141 chunk_size,
142 current_offset: 0,
143 total_len,
144 _phantom: PhantomData,
145 })
146 }
147
148 pub fn for_each_mut<F, E>(mut self, mut f: F) -> Result<std::result::Result<(), E>>
152 where
153 F: FnMut(u64, &mut [u8]) -> std::result::Result<(), E>,
154 {
155 while self.current_offset < self.total_len {
156 let remaining = self.total_len - self.current_offset;
157 let chunk_len = remaining.min(self.chunk_size as u64);
158
159 let mut guard = self.mmap.as_slice_mut(self.current_offset, chunk_len)?;
160 let slice = guard.as_mut();
161
162 match f(self.current_offset, slice) {
163 Ok(()) => {},
164 Err(e) => return Ok(Err(e)),
165 }
166
167 self.current_offset += chunk_len;
168 }
169 Ok(Ok(()))
170 }
171}
172
173impl MemoryMappedFile {
174 #[cfg(feature = "iterator")]
198 pub fn chunks(&self, chunk_size: usize) -> ChunkIterator<'_> {
199 ChunkIterator::new(self, chunk_size).expect("chunk iterator creation should not fail")
200 }
201
202 #[cfg(feature = "iterator")]
222 pub fn pages(&self) -> PageIterator<'_> {
223 PageIterator::new(self).expect("page iterator creation should not fail")
224 }
225
226 #[cfg(feature = "iterator")]
250 pub fn chunks_mut(&self, chunk_size: usize) -> ChunkIteratorMut<'_> {
251 ChunkIteratorMut::new(self, chunk_size).expect("mutable chunk iterator creation should not fail")
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258 use crate::create_mmap;
259 use std::fs;
260 use std::path::PathBuf;
261
262 fn tmp_path(name: &str) -> PathBuf {
263 let mut p = std::env::temp_dir();
264 p.push(format!("mmap_io_iterator_test_{}_{}", name, std::process::id()));
265 p
266 }
267
268 #[test]
269 #[cfg(feature = "iterator")]
270 fn test_chunk_iterator() {
271 let path = tmp_path("chunk_iter");
272 let _ = fs::remove_file(&path);
273
274 let mmap = create_mmap(&path, 10240).expect("create");
276 for i in 0..10 {
277 let data = vec![i as u8; 1024];
278 mmap.update_region(i * 1024, &data).expect("write");
279 }
280 mmap.flush().expect("flush");
281
282 let chunks: Vec<_> = mmap.chunks(1024)
284 .collect::<Result<Vec<_>>>()
285 .expect("collect chunks");
286
287 assert_eq!(chunks.len(), 10);
288 for (i, chunk) in chunks.iter().enumerate() {
289 assert_eq!(chunk.len(), 1024);
290 assert!(chunk.iter().all(|&b| b == i as u8));
291 }
292
293 let chunks: Vec<_> = mmap.chunks(3000)
295 .collect::<Result<Vec<_>>>()
296 .expect("collect chunks");
297
298 assert_eq!(chunks.len(), 4); assert_eq!(chunks[3].len(), 1240);
300
301 fs::remove_file(&path).expect("cleanup");
302 }
303
304 #[test]
305 #[cfg(feature = "iterator")]
306 fn test_page_iterator() {
307 let path = tmp_path("page_iter");
308 let _ = fs::remove_file(&path);
309
310 let ps = page_size();
311 let file_size = ps * 3 + 100; let mmap = create_mmap(&path, file_size as u64).expect("create");
314
315 let pages: Vec<_> = mmap.pages()
316 .collect::<Result<Vec<_>>>()
317 .expect("collect pages");
318
319 assert_eq!(pages.len(), 4); assert_eq!(pages[0].len(), ps);
321 assert_eq!(pages[1].len(), ps);
322 assert_eq!(pages[2].len(), ps);
323 assert_eq!(pages[3].len(), 100);
324
325 fs::remove_file(&path).expect("cleanup");
326 }
327
328 #[test]
329 #[cfg(feature = "iterator")]
330 fn test_mutable_chunk_iterator() {
331 let path = tmp_path("mut_chunk_iter");
332 let _ = fs::remove_file(&path);
333
334 let mmap = create_mmap(&path, 4096).expect("create");
335
336 let result = mmap.chunks_mut(1024).for_each_mut(|offset, chunk| {
338 let value = (offset / 1024) as u8;
339 chunk.fill(value);
340 Ok::<(), std::io::Error>(())
341 });
342
343 assert!(result.is_ok());
344 assert!(result.unwrap().is_ok());
345
346 mmap.flush().expect("flush");
347
348 let mut buf = [0u8; 1024];
350 for i in 0..4 {
351 mmap.read_into(i * 1024, &mut buf).expect("read");
352 assert!(buf.iter().all(|&b| b == i as u8));
353 }
354
355 fs::remove_file(&path).expect("cleanup");
356 }
357
358 #[test]
359 #[cfg(feature = "iterator")]
360 fn test_iterator_size_hint() {
361 let path = tmp_path("size_hint");
362 let _ = fs::remove_file(&path);
363
364 let mmap = create_mmap(&path, 10000).expect("create");
365
366 let iter = mmap.chunks(1000);
367 assert_eq!(iter.size_hint(), (10, Some(10)));
368
369 let iter = mmap.chunks(3000);
370 assert_eq!(iter.size_hint(), (4, Some(4)));
371
372 fs::remove_file(&path).expect("cleanup");
373 }
374
375 #[test]
376 #[cfg(feature = "iterator")]
377 fn test_empty_file_iteration() {
378 let path = tmp_path("empty_iter");
379 let _ = fs::remove_file(&path);
380
381 let mmap = create_mmap(&path, 1).expect("create"); mmap.resize(1).expect("resize"); let chunks: Vec<_> = mmap.chunks(1024)
385 .collect::<Result<Vec<_>>>()
386 .expect("collect");
387
388 assert_eq!(chunks.len(), 1);
389 assert_eq!(chunks[0].len(), 1);
390
391 fs::remove_file(&path).expect("cleanup");
392 }
393}