forked from jam1garner/binread
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Export
std::io
instead of always using thin reimplementation
This allows most implementers of `BinRead` to use std features on the reader instead of restricting them to the subset of features exposed by the no_std reimplementation. This also appears to have been the original intent of the `binread::io` module, per the module-level comment. This is a breaking change because `iter_bytes` is called `bytes` in std and consumes the reader, so this has been changed in the binread implementation to conform to that API. This commit also fixes the earlier binread implementations not handling errors as std does; this is now fixed (by copying from std). (Hopefully rust-lang/rust#48331 will be addressed someday and then this code can disappear entirely.)
- Loading branch information
Showing
7 changed files
with
194 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,91 +1,14 @@ | ||
//! A swappable version of [std::io](std::io) that works in `no_std + alloc` environments. | ||
//! If the feature flag `std` is enabled (as it is by default), this will just re-export types from `std::io`. | ||
|
||
pub mod prelude; | ||
pub mod cursor; | ||
pub mod error; | ||
|
||
#[cfg(feature = "std")] | ||
pub use std::io::{Error, ErrorKind}; | ||
|
||
#[cfg(not(feature = "std"))] | ||
pub use error::{Error, ErrorKind}; | ||
|
||
#[cfg(feature = "std")] | ||
pub use std::io::Result; | ||
|
||
#[cfg(not(feature = "std"))] | ||
pub type Result<T> = core::result::Result<T, Error>; | ||
|
||
/// A simplified version of [std::io::Read](std::io::Read) for use in no_std environments | ||
pub trait Read { | ||
fn read(&mut self, buf: &mut [u8]) -> Result<usize>; | ||
|
||
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { | ||
if let Ok(n) = self.read(buf) { | ||
if n == buf.len() { | ||
return Ok(()) | ||
} | ||
} | ||
|
||
Err(Error::new(ErrorKind::UnexpectedEof, "Out of bytes in reader")) | ||
} | ||
|
||
fn iter_bytes(&mut self) -> Bytes<'_, Self> | ||
where Self: Sized, | ||
{ | ||
Bytes { | ||
inner: self | ||
} | ||
} | ||
} | ||
|
||
pub struct Bytes<'a, R: Read> { | ||
inner: &'a mut R | ||
} | ||
|
||
impl<'a, R: Read> Iterator for Bytes<'a, R> { | ||
type Item = Result<u8>; | ||
|
||
fn next(&mut self) -> Option<Self::Item> { | ||
let mut byte = [0u8]; | ||
Some( | ||
self.inner.read_exact(&mut byte) | ||
.map(|_| byte[0]) | ||
) | ||
} | ||
} | ||
|
||
#[cfg(feature = "std")] | ||
pub use std::io::SeekFrom; | ||
|
||
mod no_std; | ||
#[cfg(not(feature = "std"))] | ||
#[derive(Debug, Clone, Copy)] | ||
pub enum SeekFrom { | ||
Start(u64), | ||
End(i64), | ||
Current(i64), | ||
} | ||
|
||
pub trait Seek { | ||
fn seek(&mut self, pos: SeekFrom) -> Result<u64>; | ||
} | ||
pub use no_std::*; | ||
|
||
#[cfg(feature = "std")] | ||
impl<R: std::io::Read> Read for R { | ||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> { | ||
self.read(buf) | ||
} | ||
} | ||
|
||
#[cfg(feature = "std")] | ||
impl<S: std::io::Seek> Seek for S { | ||
fn seek(&mut self, pos: SeekFrom) -> Result<u64> { | ||
self.seek(pos) | ||
} | ||
} | ||
|
||
#[cfg(feature = "std")] | ||
pub use std::io::Cursor; | ||
|
||
#[cfg(not(feature = "std"))] | ||
pub use cursor::Cursor; | ||
pub use std::io::{Bytes, Cursor, Error, ErrorKind, Read, Result, Seek, SeekFrom}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
pub use super::{cursor::Cursor, error::{Error, ErrorKind}}; | ||
|
||
pub type Result<T> = core::result::Result<T, Error>; | ||
|
||
/// A simplified version of [std::io::Read](std::io::Read) for use in no_std environments | ||
pub trait Read { | ||
fn read(&mut self, buf: &mut [u8]) -> Result<usize>; | ||
|
||
fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<()> { | ||
while !buf.is_empty() { | ||
match self.read(buf) { | ||
Ok(0) => break, | ||
Ok(n) => { | ||
let tmp = buf; | ||
buf = &mut tmp[n..]; | ||
} | ||
Err(ref e) if e.kind() == ErrorKind::Interrupted => {} | ||
Err(e) => return Err(e), | ||
} | ||
} | ||
if !buf.is_empty() { | ||
Err(Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer")) | ||
} else { | ||
Ok(()) | ||
} | ||
} | ||
|
||
fn bytes(self) -> Bytes<Self> | ||
where | ||
Self: Sized, | ||
{ | ||
Bytes { inner: self } | ||
} | ||
|
||
fn by_ref(&mut self) -> &mut Self | ||
where | ||
Self: Sized, | ||
{ | ||
self | ||
} | ||
} | ||
|
||
impl<R: Read + ?Sized> Read for &mut R { | ||
#[inline] | ||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> { | ||
(**self).read(buf) | ||
} | ||
|
||
#[inline] | ||
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { | ||
(**self).read_exact(buf) | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct Bytes<R: Read> { | ||
inner: R | ||
} | ||
|
||
impl<R: Read> Iterator for Bytes<R> { | ||
type Item = Result<u8>; | ||
|
||
fn next(&mut self) -> Option<Result<u8>> { | ||
let mut byte = 0; | ||
loop { | ||
return match self.inner.read(core::slice::from_mut(&mut byte)) { | ||
Ok(0) => None, | ||
Ok(..) => Some(Ok(byte)), | ||
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, | ||
Err(e) => Some(Err(e)), | ||
}; | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone, Copy)] | ||
pub enum SeekFrom { | ||
Start(u64), | ||
End(i64), | ||
Current(i64), | ||
} | ||
|
||
pub trait Seek { | ||
fn seek(&mut self, pos: SeekFrom) -> Result<u64>; | ||
} | ||
|
||
impl<S: Seek + ?Sized> Seek for &mut S { | ||
#[inline] | ||
fn seek(&mut self, pos: SeekFrom) -> Result<u64> { | ||
(**self).seek(pos) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
#[cfg(not(feature = "std"))] | ||
mod no_std; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
use binread::io::{Cursor, Error, ErrorKind, Read, Result}; | ||
|
||
#[derive(Debug)] | ||
struct MalfunctioningEddie<'data> { | ||
error: Option<Error>, | ||
data: Cursor<&'data [u8]>, | ||
} | ||
|
||
impl <'data> MalfunctioningEddie<'data> { | ||
fn new(data: &'data[u8]) -> Self { | ||
Self { | ||
error: None, | ||
data: Cursor::new(data), | ||
} | ||
} | ||
|
||
fn nice_to_meet_you(&mut self) { | ||
self.error = Some(Error::new(ErrorKind::BrokenPipe, "")); | ||
} | ||
|
||
fn what_a_surprise(&mut self) { | ||
self.error = Some(Error::new(ErrorKind::Interrupted, "")); | ||
} | ||
} | ||
|
||
impl Read for MalfunctioningEddie<'_> { | ||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> { | ||
if let Some(error) = self.error.take() { | ||
Err(error) | ||
} else { | ||
self.data.read(buf) | ||
} | ||
} | ||
} | ||
|
||
#[test] | ||
fn bytes() { | ||
let mut cursor = MalfunctioningEddie::new(b"\0\x01\x02\x03\x04\x05"); | ||
{ | ||
let mut bytes = cursor.by_ref().bytes(); | ||
assert!(matches!(bytes.next(), Some(Ok(0)))); | ||
assert!(matches!(bytes.next(), Some(Ok(1)))); | ||
} | ||
|
||
// Interrupted error should cause a retry | ||
cursor.what_a_surprise(); | ||
{ | ||
let mut bytes = cursor.by_ref().bytes(); | ||
assert!(matches!(bytes.next(), Some(Ok(2)))); | ||
} | ||
|
||
// Reads through Bytes should have advanced the underlying stream | ||
let mut raw_read_data = [0u8; 2]; | ||
assert_eq!(cursor.read(&mut raw_read_data).unwrap(), 2); | ||
assert_eq!(raw_read_data, [3, 4]); | ||
|
||
// Errors other than Interrupted should be returned | ||
cursor.nice_to_meet_you(); | ||
let mut bytes = cursor.bytes(); | ||
assert_eq!(bytes.next().unwrap().unwrap_err().kind(), ErrorKind::BrokenPipe); | ||
} | ||
|
||
#[test] | ||
fn read_exact() { | ||
let mut cursor = MalfunctioningEddie::new(b"\0\x01\x02\x03\x04\x05"); | ||
|
||
let mut raw_read_data = [0u8; 2]; | ||
cursor.read_exact(&mut raw_read_data).unwrap(); | ||
assert_eq!(raw_read_data, [0, 1]); | ||
|
||
// Interrupted error should cause a retry | ||
cursor.what_a_surprise(); | ||
cursor.read_exact(&mut raw_read_data).unwrap(); | ||
assert_eq!(raw_read_data, [2, 3]); | ||
|
||
// Errors other than Interrupted should be returned | ||
cursor.nice_to_meet_you(); | ||
assert_eq!(cursor.read_exact(&mut raw_read_data).unwrap_err().kind(), ErrorKind::BrokenPipe); | ||
|
||
// Read through a mutable reference should work as if it were directly on | ||
// the cursor | ||
cursor.by_ref().read_exact(&mut raw_read_data).unwrap(); | ||
assert_eq!(raw_read_data, [4, 5]); | ||
|
||
// EOF reads should not succeed | ||
assert_eq!(cursor.read_exact(&mut raw_read_data).unwrap_err().kind(), ErrorKind::UnexpectedEof); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
mod io; |