[IO] Move to custom `Read` trait mirroring `std::io::Read`

In order to move towards our own I/O traits in the `rust-bitcoin`
ecosystem, we have to slowly replace our use of the `std` and
`core2` traits.

Here we take the second big step, replacing
`{std,core2}::io::Read` with our own `bitcoin_io::io::Read`. We
provide a blanket impl for our trait for all `std::io::Read`, if
the `std` feature is enabled, allowing users who use their own
streams or `std` streams to call `rust-bitcoin` methods directly.
This commit is contained in:
Matt Corallo 2023-09-11 17:57:07 +00:00
parent 7395093f94
commit 141343edb4
3 changed files with 55 additions and 4 deletions

View File

@ -34,7 +34,6 @@ macro_rules! impl_consensus_encoding {
fn consensus_decode<R: $crate::io::Read + ?Sized>(
r: &mut R,
) -> Result<$thing, $crate::consensus::encode::Error> {
use crate::io::Read as _;
let mut r = r.take($crate::consensus::encode::MAX_VEC_SIZE as u64);
Ok($thing {
$($field: $crate::consensus::Decodable::consensus_decode(&mut r)?),+

View File

@ -10,7 +10,6 @@ use core::convert::TryFrom;
use core::{fmt, iter};
use hashes::{sha256d, Hash};
use io::Read as _;
use crate::blockdata::{block, transaction};
use crate::consensus::encode::{self, CheckedData, Decodable, Encodable, VarInt};

View File

@ -32,14 +32,67 @@ extern crate alloc;
/// Standard I/O stream definitions which are API-equivalent to `std`'s `io` module. See
/// [`std::io`] for more info.
pub mod io {
use core::convert::TryInto;
#[cfg(all(not(feature = "std"), not(feature = "core2")))]
compile_error!("At least one of std or core2 must be enabled");
#[cfg(feature = "std")]
pub use std::io::{Read, Cursor, Take, Error, ErrorKind, Result};
pub use std::io::{Cursor, Error, ErrorKind, Result};
#[cfg(not(feature = "std"))]
pub use core2::io::{Read, Cursor, Take, Error, ErrorKind, Result};
pub use core2::io::{Cursor, Error, ErrorKind, Result};
/// A generic trait describing an input stream. See [`std::io::Read`] for more info.
pub trait Read {
fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
#[inline]
fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<()> {
while !buf.is_empty() {
match self.read(buf) {
Ok(0) => return Err(Error::new(ErrorKind::UnexpectedEof, "")),
Ok(len) => buf = &mut buf[len..],
Err(e) if e.kind() == ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(())
}
#[inline]
fn take(&mut self, limit: u64) -> Take<Self> {
Take { reader: self, remaining: limit }
}
}
pub struct Take<'a, R: Read + ?Sized> {
reader: &'a mut R,
remaining: u64,
}
impl<'a, R: Read + ?Sized> Read for Take<'a, R> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let len = core::cmp::min(buf.len(), self.remaining.try_into().unwrap_or(buf.len()));
let read = self.reader.read(&mut buf[..len])?;
self.remaining -= read.try_into().unwrap_or(self.remaining);
Ok(read)
}
}
#[cfg(feature = "std")]
impl<R: std::io::Read> Read for R {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
<R as std::io::Read>::read(self, buf)
}
}
#[cfg(all(feature = "core2", not(feature = "std")))]
impl<R: core2::io::Read> Read for R {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
<R as core2::io::Read>::read(self, buf)
}
}
/// A generic trait describing an output stream. See [`std::io::Write`] for more info.
pub trait Write {