simd_r_drive/storage_engine/entry_stream.rs
1use simd_r_drive_entry_handle::EntryHandle;
2use std::io::{self, Read};
3
4/// `EntryStream` provides a **streaming interface** over an `EntryHandle`.
5///
6/// This struct allows **reading large entries in chunks** instead of loading
7/// the entire entry into memory. It is useful when working with entries larger
8/// than available RAM.
9///
10/// # ⚠️ **Non Zero-Copy Warning**
11/// Unlike `EntryHandle`, this implementation **performs memory copies**.
12/// Each call to `read()` copies a portion of the entry into a user-provided buffer.
13///
14/// **For zero-copy access**, use `EntryHandle::as_slice()` instead.
15///
16/// # Example Usage
17/// ```rust
18/// use simd_r_drive::storage_engine::{DataStore, EntryHandle, EntryStream, traits::{DataStoreReader, DataStoreWriter}};
19/// use std::io::Read;
20/// use std::path::PathBuf;
21/// use tempfile::tempdir;
22///
23/// let temp_dir = tempdir().expect("Failed to create temp dir");
24/// let temp_path = temp_dir.path().join("test_storage.bin");
25///
26/// let data_store = DataStore::from(PathBuf::from(temp_path));
27///
28/// // Write some test data
29/// data_store.write(b"test_key", b"test_data");
30/// let entry_handle = data_store.read(b"test_key").unwrap().unwrap();
31///
32/// // Assume `entry_handle` is obtained from storage
33/// let mut stream = EntryStream::from(entry_handle);
34///
35/// let mut buffer = vec![0; 4096]; // Read in 4KB chunks
36/// while let Ok(bytes_read) = stream.read(&mut buffer) {
37/// if bytes_read == 0 {
38/// break; // EOF
39/// }
40/// // Replace this with actual processing logic
41/// println!("Read {} bytes", bytes_read);
42/// }
43/// ```
44pub struct EntryStream {
45 entry_handle: EntryHandle,
46 position: usize, // Tracks how much has been read
47}
48
49impl From<EntryHandle> for EntryStream {
50 /// Converts an `EntryHandle` into an `EntryStream`.
51 ///
52 /// This allows the entry's data to be read **incrementally** instead of accessing
53 /// the full slice in memory at once.
54 ///
55 /// # ⚠️ **Non Zero-Copy Warning**
56 /// - **Streaming reads require memory copies.**
57 /// - If you need direct access to the full entry **without copying**, use `EntryHandle::as_slice()`.
58 fn from(entry_handle: EntryHandle) -> Self {
59 Self {
60 position: entry_handle.range.start,
61 entry_handle,
62 }
63 }
64}
65
66impl Read for EntryStream {
67 // Reads a chunk of the entry into the provided buffer.
68 ///
69 /// - Returns `Ok(0)` on EOF (no more data).
70 /// - Reads up to `buf.len()` bytes from the entry.
71 /// - Moves the read position forward after each call.
72 ///
73 /// # ⚠️ **Non Zero-Copy Warning**
74 /// - This method **copies** data from the memory-mapped file into the buffer.
75 /// - **Use `EntryHandle::as_slice()` for zero-copy access.**
76 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
77 let slice = self.entry_handle.as_slice(); // Get zero-copy reference
78
79 let remaining = self.entry_handle.range.end - self.position;
80 if remaining == 0 {
81 return Ok(0); // EOF
82 }
83
84 let bytes_to_read = remaining.min(buf.len());
85 let start_idx = self.position - self.entry_handle.range.start;
86
87 buf[..bytes_to_read].copy_from_slice(&slice[start_idx..start_idx + bytes_to_read]);
88
89 self.position += bytes_to_read;
90 Ok(bytes_to_read)
91 }
92}