diff --git a/src/dir.rs b/src/dir.rs index b23fe21..7c5cda4 100644 --- a/src/dir.rs +++ b/src/dir.rs @@ -15,7 +15,8 @@ use crate::dir_entry::{SFN_PADDING, SFN_SIZE}; use crate::error::{Error, IoError}; use crate::file::File; use crate::fs::{DiskSlice, FileSystem, FsIoAdapter, OemCpConverter, ReadWriteSeek}; -use crate::io::{self, IoBase, Read, Seek, SeekFrom, Write}; +use crate::io::private::Sealed; +use crate::io::{self, IoBase, Read, ReadFile, Seek, SeekFrom, Write, WriteFile}; use crate::time::TimeProvider; const LFN_PADDING: u16 = 0xFFFF; @@ -51,11 +52,13 @@ impl Clone for DirRawStream<'_, IO, TP, OCC> { } } +impl Sealed for DirRawStream<'_, IO, TP, OCC> {} + impl IoBase for DirRawStream<'_, IO, TP, OCC> { type Error = Error; } -impl Read for DirRawStream<'_, IO, TP, OCC> { +impl ReadFile for DirRawStream<'_, IO, TP, OCC> { fn read(&mut self, buf: &mut [u8]) -> Result { match self { DirRawStream::File(file) => file.read(buf), @@ -64,7 +67,14 @@ impl Read for DirRawStream<'_, IO, TP, } } -impl Write for DirRawStream<'_, IO, TP, OCC> { +impl Read for DirRawStream<'_, IO, TP, OCC> { + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { + ::read_exact(self, buf) + } +} + +impl WriteFile for DirRawStream<'_, IO, TP, OCC> { fn write(&mut self, buf: &[u8]) -> Result { match self { DirRawStream::File(file) => file.write(buf), @@ -73,12 +83,24 @@ impl Write for DirRawStream<'_, IO, TP } fn flush(&mut self) -> Result<(), Self::Error> { match self { - DirRawStream::File(file) => file.flush(), - DirRawStream::Root(raw) => raw.flush(), + DirRawStream::File(file) => as WriteFile>::flush(file), + DirRawStream::Root(raw) => as WriteFile>::flush(raw), } } } +impl Write for DirRawStream<'_, IO, TP, OCC> { + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + ::write_all(self, buf) + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + ::flush(self) + } +} + impl Seek for DirRawStream<'_, IO, TP, OCC> { fn seek(&mut self, pos: SeekFrom) -> Result { match self { diff --git a/src/file.rs b/src/file.rs index 5df3a29..7f03606 100644 --- a/src/file.rs +++ b/src/file.rs @@ -3,7 +3,8 @@ use core::convert::TryFrom; use crate::dir_entry::DirEntryEditor; use crate::error::Error; use crate::fs::{FileSystem, ReadWriteSeek}; -use crate::io::{IoBase, Read, Seek, SeekFrom, Write}; +use crate::io::private::Sealed; +use crate::io::{IoBase, Read, ReadFile, Seek, SeekFrom, Write, WriteFile}; use crate::time::{Date, DateTime, TimeProvider}; const MAX_FILE_SIZE: u32 = core::u32::MAX; @@ -250,11 +251,13 @@ impl Clone for File<'_, IO, TP, OCC> { } } +impl Sealed for File<'_, IO, TP, OCC> {} + impl IoBase for File<'_, IO, TP, OCC> { type Error = Error; } -impl Read for File<'_, IO, TP, OCC> { +impl ReadFile for File<'_, IO, TP, OCC> { fn read(&mut self, buf: &mut [u8]) -> Result { trace!("File::read"); let cluster_size = self.fs.cluster_size(); @@ -286,15 +289,10 @@ impl Read for File<'_, IO, TP, OCC> { } trace!("read {} bytes in cluster {}", read_size, current_cluster); let offset_in_fs = self.fs.offset_from_cluster(current_cluster) + u64::from(offset_in_cluster); - let read_bytes = { - let mut disk = self.fs.disk.borrow_mut(); - disk.seek(SeekFrom::Start(offset_in_fs))?; - disk.read(&mut buf[..read_size])? - }; - if read_bytes == 0 { - return Ok(0); - } - self.offset += read_bytes as u32; + let mut disk = self.fs.disk.borrow_mut(); + disk.seek(SeekFrom::Start(offset_in_fs))?; + disk.read_exact(&mut buf[..read_size])?; + self.offset += read_size as u32; self.current_cluster = Some(current_cluster); if let Some(ref mut e) = self.entry { @@ -303,7 +301,14 @@ impl Read for File<'_, IO, TP, OCC> { e.set_accessed(now); } } - Ok(read_bytes) + Ok(read_size) + } +} + +impl Read for File<'_, IO, TP, OCC> { + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { + ::read_exact(self, buf) } } @@ -313,11 +318,11 @@ where std::io::Error: From>, { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - Ok(Read::read(self, buf)?) + Ok(ReadFile::read(self, buf)?) } } -impl Write for File<'_, IO, TP, OCC> { +impl WriteFile for File<'_, IO, TP, OCC> { fn write(&mut self, buf: &[u8]) -> Result { trace!("File::write"); let cluster_size = self.fs.cluster_size(); @@ -365,19 +370,14 @@ impl Write for File<'_, IO, TP, OCC> { }; trace!("write {} bytes in cluster {}", write_size, current_cluster); let offset_in_fs = self.fs.offset_from_cluster(current_cluster) + u64::from(offset_in_cluster); - let written_bytes = { - let mut disk = self.fs.disk.borrow_mut(); - disk.seek(SeekFrom::Start(offset_in_fs))?; - disk.write(&buf[..write_size])? - }; - if written_bytes == 0 { - return Ok(0); - } + let mut disk = self.fs.disk.borrow_mut(); + disk.seek(SeekFrom::Start(offset_in_fs))?; + disk.write_all(&buf[..write_size])?; // some bytes were writter - update position and optionally size - self.offset += written_bytes as u32; + self.offset += write_size as u32; self.current_cluster = Some(current_cluster); self.update_dir_entry_after_write(); - Ok(written_bytes) + Ok(write_size) } fn flush(&mut self) -> Result<(), Self::Error> { @@ -385,13 +385,26 @@ impl Write for File<'_, IO, TP, OCC> { } } +impl Write for File<'_, IO, TP, OCC> { + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + ::write_all(self, buf) + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + ::flush(self) + } +} + #[cfg(feature = "std")] impl std::io::Write for File<'_, IO, TP, OCC> where std::io::Error: From>, { fn write(&mut self, buf: &[u8]) -> std::io::Result { - Ok(Write::write(self, buf)?) + Write::write_all(self, buf)?; + Ok(buf.len()) } fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { diff --git a/src/fs.rs b/src/fs.rs index e0c906e..7f39c22 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -13,7 +13,8 @@ use crate::dir::{Dir, DirRawStream}; use crate::dir_entry::{DirFileEntryData, FileAttributes, SFN_PADDING, SFN_SIZE}; use crate::error::Error; use crate::file::File; -use crate::io::{self, IoBase, Read, ReadLeExt, Seek, SeekFrom, Write, WriteLeExt}; +use crate::io::private::Sealed; +use crate::io::{self, IoBase, Read, ReadFile, ReadLeExt, Seek, SeekFrom, Write, WriteFile, WriteLeExt}; use crate::table::{ alloc_cluster, count_free_clusters, format_fat, read_fat_flags, ClusterIterator, RESERVED_FAT_ENTRIES, }; @@ -694,23 +695,26 @@ pub(crate) struct FsIoAdapter<'a, IO: ReadWriteSeek, TP, OCC> { fs: &'a FileSystem, } +impl Sealed for FsIoAdapter<'_, IO, TP, OCC> {} + impl IoBase for FsIoAdapter<'_, IO, TP, OCC> { type Error = IO::Error; } impl Read for FsIoAdapter<'_, IO, TP, OCC> { - fn read(&mut self, buf: &mut [u8]) -> Result { - self.fs.disk.borrow_mut().read(buf) + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { + self.fs.disk.borrow_mut().read_exact(buf) } } impl Write for FsIoAdapter<'_, IO, TP, OCC> { - fn write(&mut self, buf: &[u8]) -> Result { - let size = self.fs.disk.borrow_mut().write(buf)?; - if size > 0 { - self.fs.set_dirty_flag(true)?; + fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + if buf.is_empty() { + Ok(()) + } else { + self.fs.disk.borrow_mut().write_all(buf)?; + self.fs.set_dirty_flag(true) } - Ok(size) } fn flush(&mut self) -> Result<(), Self::Error> { @@ -797,22 +801,31 @@ impl Clone for DiskSlice { } } +impl Sealed for DiskSlice {} + impl IoBase for DiskSlice { type Error = Error; } -impl, S: Read + Seek> Read for DiskSlice { +impl, S: Read + Seek> ReadFile for DiskSlice { fn read(&mut self, buf: &mut [u8]) -> Result { let offset = self.begin + self.offset; let read_size = (buf.len() as u64).min(self.size - self.offset) as usize; self.inner.borrow_mut().seek(SeekFrom::Start(offset))?; - let size = self.inner.borrow_mut().read(&mut buf[..read_size])?; - self.offset += size as u64; - Ok(size) + self.inner.borrow_mut().read_exact(&mut buf[..read_size])?; + self.offset += read_size as u64; + Ok(read_size) } } -impl, S: Write + Seek> Write for DiskSlice { +impl, S: Read + Seek> Read for DiskSlice { + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { + ::read_exact(self, buf) + } +} + +impl, S: Write + Seek> WriteFile for DiskSlice { fn write(&mut self, buf: &[u8]) -> Result { let offset = self.begin + self.offset; let write_size = (buf.len() as u64).min(self.size - self.offset) as usize; @@ -834,6 +847,18 @@ impl, S: Write + Seek> Write for DiskSlice { } } +impl, S: Write + Seek> Write for DiskSlice { + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + ::write_all(self, buf) + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + ::flush(self) + } +} + impl Seek for DiskSlice { fn seek(&mut self, pos: SeekFrom) -> Result { let new_offset_opt: Option = match pos { diff --git a/src/io.rs b/src/io.rs index 07b4108..9e25656 100644 --- a/src/io.rs +++ b/src/io.rs @@ -1,4 +1,5 @@ use crate::error::IoError; +use crate::io::private::Sealed; /// Provides IO error as an associated type. /// @@ -9,10 +10,36 @@ pub trait IoBase { type Error: IoError; } +pub(crate) mod private { + pub trait Sealed {} +} + /// The `Read` trait allows for reading bytes from a source. /// /// It is based on the `std::io::Read` trait. pub trait Read: IoBase { + /// Read the exact number of bytes required to fill `buf`. + /// + /// This function reads as many bytes as necessary to completely fill the specified buffer `buf`. + /// + /// # Errors + /// + /// If this function encounters an error for which `IoError::is_interrupted` returns true then the error is ignored + /// and the operation will continue. + /// + /// If this function encounters an end of file before completely filling the buffer, it returns an error + /// instantiated by a call to `IoError::new_unexpected_eof_error`. The contents of `buf` are unspecified in this + /// case. + /// + /// If this function returns an error, it is unspecified how many bytes it has read, but it will never read more + /// than would be necessary to completely fill the buffer. + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error>; +} + +/// The `ReadFile` trait allows for reading bytes from a source. +/// +/// It is based on the `std::io::Read` trait. +pub trait ReadFile: Read + Sealed { /// Pull some bytes from this source into the specified buffer, returning how many bytes were read. /// /// This function does not provide any guarantees about whether it blocks waiting for data, but if an object needs @@ -78,6 +105,32 @@ pub trait Read: IoBase { /// /// It is based on the `std::io::Write` trait. pub trait Write: IoBase { + /// Attempts to write an entire buffer into this writer. + /// + /// This method will continuously call `write` until there is no more data to be written or an error is returned. + /// Errors for which `IoError::is_interrupted` method returns true are being skipped. This method will not return + /// until the entire buffer has been successfully written or such an error occurs. + /// If `write` returns 0 before the entire buffer has been written this method will return an error instantiated by + /// a call to `IoError::new_write_zero_error`. + /// + /// # Errors + /// + /// This function will return the first error for which `IoError::is_interrupted` method returns false that `write` + /// returns. + fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error>; + + /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. + /// + /// # Errors + /// + /// It is considered an error if not all bytes could be written due to I/O errors or EOF being reached. + fn flush(&mut self) -> Result<(), Self::Error>; +} + +/// The `WriteFile` trait allows for writing bytes into the sink. +/// +/// It is based on the `std::io::Write` trait. +pub trait WriteFile: Write + Sealed { /// Write a buffer into this writer, returning how many bytes were written. /// /// # Errors @@ -195,13 +248,16 @@ impl StdIoWrapper { } } +#[cfg(feature = "std")] +impl Sealed for StdIoWrapper {} + #[cfg(feature = "std")] impl IoBase for StdIoWrapper { type Error = std::io::Error; } #[cfg(feature = "std")] -impl Read for StdIoWrapper { +impl ReadFile for StdIoWrapper { fn read(&mut self, buf: &mut [u8]) -> Result { self.inner.read(buf) } @@ -211,11 +267,14 @@ impl Read for StdIoWrapper { } #[cfg(feature = "std")] -impl Write for StdIoWrapper { - fn write(&mut self, buf: &[u8]) -> Result { - self.inner.write(buf) +impl Read for StdIoWrapper { + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { + ::read_exact(self, buf) } - +} +#[cfg(feature = "std")] +impl Write for StdIoWrapper { fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { self.inner.write_all(buf) }