diff --git a/rust/src/bufferpool/mod.rs b/rust/src/bufferpool/mod.rs index 7973b08..467de92 100644 --- a/rust/src/bufferpool/mod.rs +++ b/rust/src/bufferpool/mod.rs @@ -104,6 +104,55 @@ where frame_pool: &'a mut dyn framepool::FramePool, } +// Iterator for BufferPool that yields the data T from each frame +pub struct BufferPoolIterator<'a, T> +where + T: Clone, +{ + buffer_pool: &'a mut BufferPool<'a, T>, + current_index: FramePoolId, + total_size: u64, +} + +impl<'a, T> Iterator for BufferPoolIterator<'a, T> +where + T: Clone, +{ + type Item = T; + + fn next(&mut self) -> Option { + if self.current_index >= self.total_size { + return None; + } + + // Use BufferPool's get_page method to transparently handle caching + let result = self + .buffer_pool + .get_page(self.current_index) + .map(|page| page.data()); + + self.current_index += 1; + result + } +} + +impl<'a, T> IntoIterator for &'a mut BufferPool<'a, T> +where + T: Clone, +{ + type Item = T; + type IntoIter = BufferPoolIterator<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + let total_size = self.frame_pool.size(); + BufferPoolIterator { + buffer_pool: self, + current_index: 0, + total_size, + } + } +} + impl<'a, T> BufferPool<'a, T> where T: Clone, @@ -933,4 +982,106 @@ mod tests { let result = bp.get_page(100); assert!(result.is_none()); } + + #[test] + fn test_bufferpool_iterator_basic() { + let mut mem_pool = MemPool::::new(); + mem_pool.resize(3).unwrap(); + + // Initialize with test data + for i in 0..3 { + let data_arc = Arc::new(format!("data_{}", i)); + mem_pool.put_frame(i, data_arc).unwrap(); + } + + let mut bp = BufferPool::::new(2, &mut mem_pool, bottom_evictor); + + // Collect all data using the iterator + let collected: Vec = (&mut bp).into_iter().collect(); + + assert_eq!(collected.len(), 3); + assert_eq!(collected[0], "data_0"); + assert_eq!(collected[1], "data_1"); + assert_eq!(collected[2], "data_2"); + } + + #[test] + fn test_bufferpool_iterator_with_caching() { + let mut mem_pool = MemPool::::new(); + mem_pool.resize(5).unwrap(); + + // Initialize with test data + for i in 0..5 { + let data_arc = Arc::new((i * 10) as i32); + mem_pool.put_frame(i, data_arc).unwrap(); + } + + // Small buffer pool to force evictions + let mut bp = BufferPool::::new(2, &mut mem_pool, bottom_evictor); + + // Iterate and verify caching is transparent + let collected: Vec = (&mut bp).into_iter().collect(); + let sum: i32 = collected.iter().sum(); + + assert_eq!(sum, 0 + 10 + 20 + 30 + 40); // 100 + assert_eq!(collected.len(), 5); + + // Note: Can't check internal state after consuming the iterator + // because it requires accessing bp after it's been mutably borrowed + } + + #[test] + fn test_bufferpool_iterator_empty() { + let mut mem_pool = MemPool::::new(); + let mut bp = BufferPool::::new(5, &mut mem_pool, bottom_evictor); + + let collected: Vec = (&mut bp).into_iter().collect(); + assert_eq!(collected.len(), 0); + } + + #[test] + fn test_bufferpool_iterator_partial_data() { + let mut mem_pool = MemPool::>::new(); + mem_pool.resize(3).unwrap(); + + // Only populate some frames + let data1 = Arc::new(Some("first".to_string())); + let data2 = Arc::new(None); + let data3 = Arc::new(Some("third".to_string())); + + mem_pool.put_frame(0, data1).unwrap(); + mem_pool.put_frame(1, data2).unwrap(); + mem_pool.put_frame(2, data3).unwrap(); + + let mut bp = BufferPool::>::new(2, &mut mem_pool, bottom_evictor); + + let collected: Vec> = (&mut bp).into_iter().collect(); + + assert_eq!(collected.len(), 3); + assert_eq!(collected[0], Some("first".to_string())); + assert_eq!(collected[1], None); + assert_eq!(collected[2], Some("third".to_string())); + } + + #[test] + fn test_bufferpool_iterator_stress() { + let mut mem_pool = MemPool::::new(); + mem_pool.resize(100).unwrap(); + + // Initialize with index values + for i in 0..100 { + let data_arc = Arc::new(i as usize); + mem_pool.put_frame(i, data_arc).unwrap(); + } + + // Very small buffer to force lots of evictions + let mut bp = BufferPool::::new(3, &mut mem_pool, bottom_evictor); + + let collected: Vec = (&mut bp).into_iter().collect(); + + assert_eq!(collected.len(), 100); + for (i, &value) in collected.iter().enumerate() { + assert_eq!(value, i, "Value at index {} should be {}", i, i); + } + } } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 878ab18..4e202aa 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -52,6 +52,45 @@ //! } //! ``` //! +//! ## Iterator Support +//! +//! BufferPool implements iterator support for seamless data traversal with transparent caching: +//! +//! ```rust +//! use std::sync::Arc; +//! use bufferpool::bufferpool::BufferPool; +//! use bufferpool::framepool::{MemPool, FramePool}; +//! +//! // Create and populate a frame pool +//! let mut frame_pool = MemPool::new(); +//! frame_pool.resize(10).unwrap(); +//! +//! for i in 0..10 { +//! let data = Arc::new(format!("Data item {}", i)); +//! frame_pool.put_frame(i, data).unwrap(); +//! } +//! +//! // Create a small buffer pool to demonstrate caching +//! let mut buffer_pool = BufferPool::new( +//! 3, // Only 3 slots in cache +//! &mut frame_pool, +//! bufferpool::bufferpool::bottom_evictor +//! ); +//! +//! // Iterate over all data - caching and eviction happens transparently +//! for data in &mut buffer_pool { +//! println!("Item: {}", data); +//! } +//! +//! // Or collect into a Vec +//! let mut buffer_pool2 = BufferPool::new(3, &mut frame_pool, bufferpool::bufferpool::bottom_evictor); +//! let all_data: Vec = (&mut buffer_pool2).into_iter().collect(); +//! assert_eq!(all_data.len(), 10); +//! ``` +//! +//! The iterator yields the actual data `T` from each frame, not the frames themselves. +//! The BufferPool handles all caching, loading, and eviction transparently during iteration. +//! //! ## Advanced Usage with Disk Storage //! //! ```rust