diff --git a/crates/io-core/src/reader.rs b/crates/io-core/src/reader.rs index dfc55f6b8..49433af73 100644 --- a/crates/io-core/src/reader.rs +++ b/crates/io-core/src/reader.rs @@ -182,11 +182,9 @@ impl BytesBufferedReader { /// let reader = BytesBufferedReader::from_file_read(&file, 0, 1024).await?; /// ``` #[cfg(unix)] - pub async fn from_file_mmap(file: &tokio::fs::File, offset: u64, size: usize) -> Result { + pub async fn from_file_read(file: &tokio::fs::File, offset: u64, size: usize) -> Result { use tokio::io::{AsyncReadExt, AsyncSeekExt, SeekFrom}; - // For mmap, we need the file path - fall back to regular read if not available - // This is a simplified implementation let mut cloned = file.try_clone().await?; cloned.seek(SeekFrom::Start(offset)).await?; @@ -203,7 +201,7 @@ impl BytesBufferedReader { /// /// On platforms that don't support mmap, this falls back to regular file I/O. #[cfg(not(unix))] - pub async fn from_file_mmap(file: &tokio::fs::File, offset: u64, size: usize) -> Result { + pub async fn from_file_read(file: &tokio::fs::File, offset: u64, size: usize) -> Result { use tokio::io::{AsyncReadExt, AsyncSeekExt, SeekFrom}; let mut cloned = file.try_clone().await?; @@ -218,12 +216,13 @@ impl BytesBufferedReader { }) } - /// Create a Bytes-backed reader from a file using normal buffered reads. - /// - /// This is the preferred name for new code. It is currently equivalent to - /// the historical `from_file_mmap` compatibility method. - pub async fn from_file_read(file: &tokio::fs::File, offset: u64, size: usize) -> Result { - Self::from_file_mmap(file, offset, size).await + /// Historical name for `from_file_read`. + #[deprecated( + since = "1.0.0-beta.8", + note = "use from_file_read; this method performs normal reads into owned Bytes" + )] + pub async fn from_file_mmap(file: &tokio::fs::File, offset: u64, size: usize) -> Result { + Self::from_file_read(file, offset, size).await } /// Get the remaining data as Bytes (zero-copy). @@ -286,8 +285,17 @@ impl std::fmt::Debug for BytesBufferedReader { #[cfg(test)] mod tests { use super::*; + use std::path::PathBuf; use tokio::io::AsyncReadExt; + fn temp_file_path(test_name: &str) -> PathBuf { + let nonce = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .expect("system time should be after unix epoch") + .as_nanos(); + std::env::temp_dir().join(format!("rustfs-io-core-{test_name}-{}-{nonce}", std::process::id())) + } + #[tokio::test] async fn test_from_bytes() { let data = Bytes::from("hello world"); @@ -312,6 +320,49 @@ mod tests { assert_eq!(&buf[..n], b"hello"); } + #[tokio::test] + async fn test_from_file_read_reads_requested_range() { + let path = temp_file_path("from-file-read"); + tokio::fs::write(&path, b"hello world") + .await + .expect("write temp file for reader test"); + + let file = tokio::fs::File::open(&path).await.expect("open temp file for reader test"); + let mut reader = BytesBufferedReader::from_file_read(&file, 6, 5) + .await + .expect("read requested range into Bytes"); + + let mut output = Vec::new(); + reader.read_to_end(&mut output).await.expect("drain reader output"); + + assert_eq!(output, b"world"); + + let _ = tokio::fs::remove_file(path).await; + } + + #[tokio::test] + #[allow(deprecated)] + async fn test_from_file_mmap_legacy_alias_reads_requested_range() { + let path = temp_file_path("from-file-mmap"); + tokio::fs::write(&path, b"hello world") + .await + .expect("write temp file for legacy reader test"); + + let file = tokio::fs::File::open(&path) + .await + .expect("open temp file for legacy reader test"); + let mut reader = BytesBufferedReader::from_file_mmap(&file, 0, 5) + .await + .expect("read requested range through legacy alias"); + + let mut output = Vec::new(); + reader.read_to_end(&mut output).await.expect("drain legacy reader output"); + + assert_eq!(output, b"hello"); + + let _ = tokio::fs::remove_file(path).await; + } + #[tokio::test] async fn test_remaining_bytes() { let data = Bytes::from("hello world"); diff --git a/crates/io-core/src/writer.rs b/crates/io-core/src/writer.rs index 293f6a8bf..2d1f1a754 100644 --- a/crates/io-core/src/writer.rs +++ b/crates/io-core/src/writer.rs @@ -101,7 +101,7 @@ impl BytesMutWriter { /// /// # Arguments /// - /// * `data` - Data to write (as Bytes for zero-copy potential) + /// * `data` - Data to append to the internal buffer /// /// # Returns /// @@ -112,9 +112,9 @@ impl BytesMutWriter { /// /// ```ignore /// let data = Bytes::from("hello world"); - /// let written = writer.write_zero_copy(data).await?; + /// let written = writer.write_buffered(data).await?; /// ``` - pub async fn write_zero_copy(&mut self, data: Bytes) -> Result { + pub async fn write_buffered(&mut self, data: Bytes) -> Result { if self.finalized { return Err(ZeroCopyWriteError::Finalized("Cannot write to finalized writer".to_string())); } @@ -126,12 +126,13 @@ impl BytesMutWriter { Ok(len) } - /// Write data into the internal buffer. - /// - /// This is the preferred name for new code. It is currently equivalent to - /// the historical `write_zero_copy` compatibility method. - pub async fn write_buffered(&mut self, data: Bytes) -> Result { - self.write_zero_copy(data).await + /// Historical name for `write_buffered`. + #[deprecated( + since = "1.0.0-beta.8", + note = "use write_buffered; this method appends bytes into an internal buffer" + )] + pub async fn write_zero_copy(&mut self, data: Bytes) -> Result { + self.write_buffered(data).await } /// Write a slice of data. @@ -313,11 +314,11 @@ mod tests { } #[tokio::test] - async fn test_write_zero_copy() { + async fn test_write_buffered() { let mut writer = BytesMutWriter::new(); let data = Bytes::from("hello world"); - let written = writer.write_zero_copy(data).await.unwrap(); + let written = writer.write_buffered(data).await.unwrap(); assert_eq!(written, 11); assert_eq!(writer.bytes_written(), 11); assert_eq!(writer.as_slice(), b"hello world"); @@ -351,7 +352,7 @@ mod tests { let mut writer = BytesMutWriter::new(); let data = Bytes::from("hello world"); - writer.write_zero_copy(data).await.unwrap(); + writer.write_buffered(data).await.unwrap(); let result = writer.into_bytes(); assert_eq!(result.as_ref(), b"hello world"); @@ -362,17 +363,17 @@ mod tests { let mut writer = BytesMutWriter::new(); let data = Bytes::from("hello"); - writer.write_zero_copy(data).await.unwrap(); + writer.write_buffered(data).await.unwrap(); let _result = writer.into_bytes(); // Create new writer and try to write after finalize let mut writer2 = BytesMutWriter::new(); - writer2.write_zero_copy(Bytes::from("test")).await.unwrap(); + writer2.write_buffered(Bytes::from("test")).await.unwrap(); let _ = writer2.into_bytes(); // Writing to a consumed writer should work via new writer let mut writer3 = BytesMutWriter::new(); - let result = writer3.write_zero_copy(Bytes::from("final")).await; + let result = writer3.write_buffered(Bytes::from("final")).await; assert!(result.is_ok()); } @@ -403,7 +404,7 @@ mod tests { async fn test_multiple_writes() { let mut writer = BytesMutWriter::new(); - writer.write_zero_copy(Bytes::from("hello ")).await.unwrap(); + writer.write_buffered(Bytes::from("hello ")).await.unwrap(); writer.write_slice(b"world").await.unwrap(); assert_eq!(writer.as_slice(), b"hello world");