Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 79a5204

Browse files
committedMar 9, 2021
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.)
1 parent c543fe6 commit 79a5204

File tree

7 files changed

+196
-83
lines changed

7 files changed

+196
-83
lines changed
 

‎binread/src/io/error.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ enum Repr {
1616
}
1717

1818
#[non_exhaustive]
19-
#[derive(Debug)]
19+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2020
pub enum ErrorKind {
2121
NotFound,
2222
PermissionDenied,
@@ -44,4 +44,10 @@ impl Error {
4444
repr: Repr::Simple(kind)
4545
}
4646
}
47+
48+
pub fn kind(&self) -> ErrorKind {
49+
match self.repr {
50+
Repr::Simple(kind) => kind,
51+
}
52+
}
4753
}

‎binread/src/io/mod.rs

+4-81
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,14 @@
11
//! A swappable version of [std::io](std::io) that works in `no_std + alloc` environments.
22
//! If the feature flag `std` is enabled (as it is by default), this will just re-export types from `std::io`.
3+
34
pub mod prelude;
45
pub mod cursor;
56
pub mod error;
67

7-
#[cfg(feature = "std")]
8-
pub use std::io::{Error, ErrorKind};
9-
10-
#[cfg(not(feature = "std"))]
11-
pub use error::{Error, ErrorKind};
12-
13-
#[cfg(feature = "std")]
14-
pub use std::io::Result;
15-
168
#[cfg(not(feature = "std"))]
17-
pub type Result<T> = core::result::Result<T, Error>;
18-
19-
/// A simplified version of [std::io::Read](std::io::Read) for use in no_std environments
20-
pub trait Read {
21-
fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
22-
23-
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
24-
if let Ok(n) = self.read(buf) {
25-
if n == buf.len() {
26-
return Ok(())
27-
}
28-
}
29-
30-
Err(Error::new(ErrorKind::UnexpectedEof, "Out of bytes in reader"))
31-
}
32-
33-
fn iter_bytes(&mut self) -> Bytes<'_, Self>
34-
where Self: Sized,
35-
{
36-
Bytes {
37-
inner: self
38-
}
39-
}
40-
}
41-
42-
pub struct Bytes<'a, R: Read> {
43-
inner: &'a mut R
44-
}
45-
46-
impl<'a, R: Read> Iterator for Bytes<'a, R> {
47-
type Item = Result<u8>;
48-
49-
fn next(&mut self) -> Option<Self::Item> {
50-
let mut byte = [0u8];
51-
Some(
52-
self.inner.read_exact(&mut byte)
53-
.map(|_| byte[0])
54-
)
55-
}
56-
}
57-
58-
#[cfg(feature = "std")]
59-
pub use std::io::SeekFrom;
60-
9+
mod no_std;
6110
#[cfg(not(feature = "std"))]
62-
#[derive(Debug, Clone, Copy)]
63-
pub enum SeekFrom {
64-
Start(u64),
65-
End(i64),
66-
Current(i64),
67-
}
68-
69-
pub trait Seek {
70-
fn seek(&mut self, pos: SeekFrom) -> Result<u64>;
71-
}
11+
pub use no_std::*;
7212

7313
#[cfg(feature = "std")]
74-
impl<R: std::io::Read> Read for R {
75-
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
76-
self.read(buf)
77-
}
78-
}
79-
80-
#[cfg(feature = "std")]
81-
impl<S: std::io::Seek> Seek for S {
82-
fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
83-
self.seek(pos)
84-
}
85-
}
86-
87-
#[cfg(feature = "std")]
88-
pub use std::io::Cursor;
89-
90-
#[cfg(not(feature = "std"))]
91-
pub use cursor::Cursor;
14+
pub use std::io::{Bytes, Cursor, Error, ErrorKind, Read, Result, Seek, SeekFrom};

‎binread/src/io/no_std.rs

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
pub use super::{cursor::Cursor, error::{Error, ErrorKind}};
2+
3+
pub type Result<T> = core::result::Result<T, Error>;
4+
5+
/// A simplified version of [std::io::Read](std::io::Read) for use in no_std environments
6+
pub trait Read {
7+
fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
8+
9+
fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<()> {
10+
while !buf.is_empty() {
11+
match self.read(buf) {
12+
Ok(0) => break,
13+
Ok(n) => {
14+
let tmp = buf;
15+
buf = &mut tmp[n..];
16+
}
17+
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
18+
Err(e) => return Err(e),
19+
}
20+
}
21+
if !buf.is_empty() {
22+
Err(Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
23+
} else {
24+
Ok(())
25+
}
26+
}
27+
28+
fn bytes(self) -> Bytes<Self>
29+
where
30+
Self: Sized,
31+
{
32+
Bytes { inner: self }
33+
}
34+
35+
fn by_ref(&mut self) -> &mut Self
36+
where
37+
Self: Sized,
38+
{
39+
self
40+
}
41+
}
42+
43+
impl<R: Read + ?Sized> Read for &mut R {
44+
#[inline]
45+
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
46+
(**self).read(buf)
47+
}
48+
49+
#[inline]
50+
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
51+
(**self).read_exact(buf)
52+
}
53+
}
54+
55+
#[derive(Debug)]
56+
pub struct Bytes<R: Read> {
57+
inner: R
58+
}
59+
60+
impl<R: Read> Iterator for Bytes<R> {
61+
type Item = Result<u8>;
62+
63+
fn next(&mut self) -> Option<Result<u8>> {
64+
let mut byte = 0;
65+
loop {
66+
return match self.inner.read(core::slice::from_mut(&mut byte)) {
67+
Ok(0) => None,
68+
Ok(..) => Some(Ok(byte)),
69+
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
70+
Err(e) => Some(Err(e)),
71+
};
72+
}
73+
}
74+
}
75+
76+
#[derive(Debug, Clone, Copy)]
77+
pub enum SeekFrom {
78+
Start(u64),
79+
End(i64),
80+
Current(i64),
81+
}
82+
83+
pub trait Seek {
84+
fn seek(&mut self, pos: SeekFrom) -> Result<u64>;
85+
}
86+
87+
impl<S: Seek + ?Sized> Seek for &mut S {
88+
#[inline]
89+
fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
90+
(**self).seek(pos)
91+
}
92+
}

‎binread/src/strings.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ impl BinRead for Vec<NonZeroU8> {
2727
fn read_options<R: Read + Seek>(reader: &mut R, _: &ReadOptions, _: Self::Args) -> BinResult<Self>
2828
{
2929
reader
30-
.iter_bytes()
30+
.bytes()
3131
.take_while(|x| !matches!(x, Ok(0)))
3232
.map(|x| Ok(x.map(|byte| unsafe { NonZeroU8::new_unchecked(byte) })?))
3333
.collect()

‎binread/tests/io/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#[cfg(not(feature = "std"))]
2+
mod no_std;

‎binread/tests/io/no_std.rs

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use binread::io::{Cursor, Error, ErrorKind, Read, Result};
2+
3+
#[derive(Debug)]
4+
struct MalfunctioningEddie<'data> {
5+
error: Option<Error>,
6+
data: Cursor<&'data [u8]>,
7+
}
8+
9+
impl <'data> MalfunctioningEddie<'data> {
10+
fn new(data: &'data[u8]) -> Self {
11+
Self {
12+
error: None,
13+
data: Cursor::new(data),
14+
}
15+
}
16+
17+
fn nice_to_meet_you(&mut self) {
18+
self.error = Some(Error::new(ErrorKind::BrokenPipe, ""));
19+
}
20+
21+
fn what_a_surprise(&mut self) {
22+
self.error = Some(Error::new(ErrorKind::Interrupted, ""));
23+
}
24+
}
25+
26+
impl Read for MalfunctioningEddie<'_> {
27+
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
28+
if let Some(error) = self.error.take() {
29+
Err(error)
30+
} else {
31+
self.data.read(buf)
32+
}
33+
}
34+
}
35+
36+
#[test]
37+
fn bytes() {
38+
let mut cursor = MalfunctioningEddie::new(b"\0\x01\x02\x03\x04\x05");
39+
{
40+
let mut bytes = cursor.by_ref().bytes();
41+
assert!(matches!(bytes.next(), Some(Ok(0))));
42+
assert!(matches!(bytes.next(), Some(Ok(1))));
43+
}
44+
45+
// Interrupted error should cause a retry
46+
cursor.what_a_surprise();
47+
{
48+
let mut bytes = cursor.by_ref().bytes();
49+
assert!(matches!(bytes.next(), Some(Ok(2))));
50+
}
51+
52+
// Reads through Bytes should have advanced the underlying stream
53+
let mut raw_read_data = [0u8; 2];
54+
assert_eq!(cursor.read(&mut raw_read_data).unwrap(), 2);
55+
assert_eq!(raw_read_data, [3, 4]);
56+
57+
// Errors other than Interrupted should be returned
58+
cursor.nice_to_meet_you();
59+
{
60+
let mut bytes = cursor.bytes();
61+
assert_eq!(bytes.next().unwrap().unwrap_err().kind(), ErrorKind::BrokenPipe);
62+
}
63+
}
64+
65+
#[test]
66+
fn read_exact() {
67+
let mut cursor = MalfunctioningEddie::new(b"\0\x01\x02\x03\x04\x05");
68+
69+
let mut raw_read_data = [0u8; 2];
70+
cursor.read_exact(&mut raw_read_data).unwrap();
71+
assert_eq!(raw_read_data, [0, 1]);
72+
73+
// Interrupted error should cause a retry
74+
cursor.what_a_surprise();
75+
cursor.read_exact(&mut raw_read_data).unwrap();
76+
assert_eq!(raw_read_data, [2, 3]);
77+
78+
// Errors other than Interrupted should be returned
79+
cursor.nice_to_meet_you();
80+
assert_eq!(cursor.read_exact(&mut raw_read_data).unwrap_err().kind(), ErrorKind::BrokenPipe);
81+
82+
// Read through a mutable reference should work as if it were directly on
83+
// the cursor
84+
cursor.by_ref().read_exact(&mut raw_read_data).unwrap();
85+
assert_eq!(raw_read_data, [4, 5]);
86+
87+
// EOF reads should not succeed
88+
assert_eq!(cursor.read_exact(&mut raw_read_data).unwrap_err().kind(), ErrorKind::UnexpectedEof);
89+
}

‎binread/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
mod io;

0 commit comments

Comments
 (0)
Failed to load comments.