Merge rust-bitcoin/rust-bitcoin#2234: Inline io module in the io crate root

f764a607ac Use conventional import path for io crate (Tobin C. Harding)
5c0759a390 Inline io module in io crate root (Tobin C. Harding)
80fe9b99b2 Move public macros to a separate module (Tobin C. Harding)

Pull request description:

  Its not immediately obvious why we nest the whole `io` code in an `io` submodule within `lib.rs`. As far as I can tell we can inline it and re-export from `rust-bitcoin` same as we do for our other dependencies.

  This change would effect other users of the crate but since the `io` crate is unreleased this effects no-one except us.

  After doing this it might be because `crate::io::Foo` looks good when near `std::io::Foo`?

ACKs for top commit:
  apoelstra:
    ACK f764a607ac
  Kixunil:
    ACK f764a607ac

Tree-SHA512: 38888b0c23d5f2cd874f77dd332fe4fa4b9acb90e3a2dac19e62ed3d98151acd7480c719aa85434e1a3de987af2c4f565528a914a14d5fd3f0f0e410cbdf5d40
This commit is contained in:
Andrew Poelstra 2023-11-30 17:46:14 +00:00
commit a3f6f53d37
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
23 changed files with 386 additions and 394 deletions

View File

@ -27,8 +27,8 @@ bitcoinconsensus-std = ["bitcoinconsensus/std", "std"]
# The no-std feature doesn't disable std - you need to turn off the std feature for that by disabling default.
# Instead no-std enables additional features required for this crate to be usable without std.
# As a result, both can be enabled without conflict.
std = ["secp256k1/std", "bitcoin-io/std", "hashes/std", "bech32/std", "internals/std", "hex/std"]
no-std = ["hashes/alloc", "hashes/io", "bitcoin-io/alloc", "bech32/alloc", "secp256k1/alloc", "hex/alloc"]
std = ["secp256k1/std", "io/std", "hashes/std", "bech32/std", "internals/std", "hex/std"]
no-std = ["hashes/alloc", "hashes/io", "io/alloc", "bech32/alloc", "secp256k1/alloc", "hex/alloc"]
[package.metadata.docs.rs]
all-features = true
@ -40,10 +40,9 @@ hex = { package = "hex-conservative", version = "0.1.1", default-features = fals
bech32 = { version = "0.10.0-beta", default-features = false }
hashes = { package = "bitcoin_hashes", version = "0.13.0", default-features = false }
secp256k1 = { version = "0.28.0", default-features = false, features = ["hashes"] }
io = { package = "bitcoin-io", version = "0.1", default-features = false }
hex_lit = "0.1.1"
bitcoin-io = { version = "0.1", default-features = false }
base64 = { version = "0.21.3", optional = true }
# Only use this feature for no-std builds, otherwise use bitcoinconsensus-std.
bitcoinconsensus = { version = "0.20.2-0.5.0", default-features = false, optional = true }

View File

@ -12,7 +12,6 @@ use core::str::FromStr;
use core::{default, ops};
use crate::consensus::encode::{self, Decodable, Encodable};
use crate::io;
use crate::prelude::*;
/// A set of denominations in which amounts can be expressed.

View File

@ -50,7 +50,6 @@ use crate::blockdata::transaction::OutPoint;
use crate::consensus::encode::VarInt;
use crate::consensus::{Decodable, Encodable};
use crate::hash_types::{BlockHash, FilterHash, FilterHeader};
use crate::io;
use crate::prelude::*;
/// Golomb encoding parameter as in BIP-158, see also https://gist.github.com/sipa/576d5f09c3b86c3b1b75598d799fc845

View File

@ -13,6 +13,7 @@ use core::{fmt, slice};
use hashes::{hash160, hash_newtype, sha512, Hash, HashEngine, Hmac, HmacEngine};
use internals::{impl_array_newtype, write_err};
use io::Write;
use secp256k1::{self, Secp256k1, XOnlyPublicKey};
#[cfg(feature = "serde")]
use serde;
@ -20,7 +21,6 @@ use serde;
use crate::base58;
use crate::crypto::key::{self, Keypair, PrivateKey, PublicKey};
use crate::internal_macros::impl_bytes_newtype;
use crate::io::Write;
use crate::network::Network;
use crate::prelude::*;

View File

@ -20,7 +20,7 @@ use crate::hash_types::{TxMerkleNode, WitnessCommitment, WitnessMerkleNode, Wtxi
use crate::internal_macros::impl_consensus_encoding;
use crate::pow::{CompactTarget, Target, Work};
use crate::prelude::*;
use crate::{io, merkle_tree, Network, VarInt};
use crate::{merkle_tree, Network, VarInt};
#[rustfmt::skip] // Keep public re-exports separate.
#[doc(inline)]
@ -645,9 +645,10 @@ mod tests {
mod benches {
use test::{black_box, Bencher};
use io::sink;
use super::Block;
use crate::consensus::{deserialize, Decodable, Encodable};
use crate::io::sink;
#[bench]
pub fn bench_stream_reader(bh: &mut Bencher) {

View File

@ -10,6 +10,7 @@ use core::cmp::{Ordering, PartialOrd};
use core::{fmt, mem};
use internals::write_err;
use io::{Read, Write};
#[cfg(all(test, mutate))]
use mutagen::mutate;
@ -17,7 +18,6 @@ use mutagen::mutate;
use crate::absolute;
use crate::consensus::encode::{self, Decodable, Encodable};
use crate::error::ParseIntError;
use crate::io::{self, Read, Write};
use crate::parse::{impl_parse_str_from_int_fallible, impl_parse_str_from_int_infallible};
use crate::prelude::*;
use crate::string::FromHexStr;

View File

@ -31,7 +31,7 @@ use crate::script::Push;
#[cfg(doc)]
use crate::sighash::{EcdsaSighashType, TapSighashType};
use crate::string::FromHexStr;
use crate::{io, Amount, VarInt};
use crate::{Amount, VarInt};
#[rustfmt::skip] // Keep public re-exports separate.
#[cfg(feature = "bitcoinconsensus")]
@ -2207,11 +2207,11 @@ mod tests {
#[cfg(bench)]
mod benches {
use hex_lit::hex;
use io::sink;
use test::{black_box, Bencher};
use super::Transaction;
use crate::consensus::{deserialize, Encodable};
use crate::io::sink;
const SOME_TX: &str = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000";

View File

@ -8,10 +8,11 @@
use core::fmt;
use core::ops::Index;
use io::{Read, Write};
use crate::consensus::encode::{Error, MAX_VEC_SIZE};
use crate::consensus::{Decodable, Encodable, WriteExt};
use crate::crypto::ecdsa;
use crate::io::{self, Read, Write};
use crate::prelude::*;
use crate::taproot::TAPROOT_ANNEX_PREFIX;
use crate::{Script, VarInt};

View File

@ -20,12 +20,12 @@ use core::{fmt, mem, u32};
use hashes::{sha256, sha256d, Hash};
use internals::write_err;
use io::{Cursor, Read};
use crate::bip152::{PrefilledTransaction, ShortId};
use crate::blockdata::block;
use crate::blockdata::transaction::{Transaction, TxIn, TxOut};
use crate::hash_types::{BlockHash, FilterHash, FilterHeader, TxMerkleNode};
use crate::io::{self, Cursor, Read};
#[cfg(feature = "std")]
use crate::p2p::{
address::{AddrV2Message, Address},

View File

@ -18,7 +18,6 @@ use serde::{Deserializer, Serializer};
use super::encode::Error as ConsensusError;
use super::{Decodable, Encodable};
use crate::io;
/// Hex-encoding strategy
pub struct Hex<Case = hex::Lower>(PhantomData<Case>)

View File

@ -74,6 +74,9 @@ pub extern crate hashes;
/// Re-export the `hex-conservative` crate.
pub extern crate hex;
/// Re-export the `bitcoin-io` crate.
pub extern crate io;
/// Rust wrapper library for Pieter Wuille's libsecp256k1. Implements ECDSA and BIP 340 signatures
/// for the SECG elliptic curve group secp256k1 and related utilities.
pub extern crate secp256k1;
@ -113,7 +116,6 @@ pub mod sign_message;
pub mod string;
pub mod taproot;
use bitcoin_io::io;
#[rustfmt::skip] // Keep public re-exports separate.
#[doc(inline)]

View File

@ -48,7 +48,6 @@ use crate::blockdata::transaction::Transaction;
use crate::blockdata::weight::Weight;
use crate::consensus::encode::{self, Decodable, Encodable};
use crate::hash_types::{TxMerkleNode, Txid};
use crate::io;
use crate::prelude::*;
/// Data structure that represents a block header paired to a partial merkle tree.

View File

@ -21,7 +21,6 @@ use core::iter;
use hashes::Hash;
use crate::consensus::encode::Encodable;
use crate::io;
use crate::prelude::*;
#[rustfmt::skip]

View File

@ -10,7 +10,6 @@ use core::{fmt, iter};
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
use crate::consensus::encode::{self, Decodable, Encodable, ReadExt, VarInt, WriteExt};
use crate::io;
use crate::p2p::ServiceFlags;
use crate::prelude::*;

View File

@ -12,7 +12,6 @@ use hashes::{sha256d, Hash};
use crate::blockdata::{block, transaction};
use crate::consensus::encode::{self, CheckedData, Decodable, Encodable, VarInt};
use crate::io;
use crate::merkle_tree::MerkleBlock;
use crate::p2p::address::{AddrV2Message, Address};
use crate::p2p::{

View File

@ -7,7 +7,6 @@
use crate::consensus::{encode, Decodable, Encodable, ReadExt};
use crate::internal_macros::impl_consensus_encoding;
use crate::io;
/// `filterload` message sets the current bloom filter
#[derive(Clone, PartialEq, Eq, Debug)]

View File

@ -9,6 +9,7 @@
use core::fmt::{self, LowerHex, UpperHex};
use core::ops::{Add, Div, Mul, Not, Rem, Shl, Shr, Sub};
use io::{Read, Write};
#[cfg(all(test, mutate))]
use mutagen::mutate;
@ -16,7 +17,6 @@ use crate::consensus::encode::{self, Decodable, Encodable};
#[cfg(doc)]
use crate::consensus::Params;
use crate::hash_types::BlockHash;
use crate::io::{self, Read, Write};
use crate::prelude::String;
use crate::string::FromHexStr;
use crate::Network;

View File

@ -1,10 +1,11 @@
// SPDX-License-Identifier: CC0-1.0
use io::{Cursor, Read};
use crate::bip32::{ChildNumber, DerivationPath, Fingerprint, Xpub};
use crate::blockdata::transaction::Transaction;
use crate::consensus::encode::MAX_VEC_SIZE;
use crate::consensus::{encode, Decodable};
use crate::io::{self, Cursor, Read};
use crate::prelude::*;
use crate::psbt::map::Map;
use crate::psbt::{raw, Error, Psbt};

View File

@ -12,7 +12,6 @@ use super::serialize::{Deserialize, Serialize};
use crate::consensus::encode::{
self, deserialize, serialize, Decodable, Encodable, ReadExt, VarInt, WriteExt, MAX_VEC_SIZE,
};
use crate::io;
use crate::prelude::*;
use crate::psbt::Error;

View File

@ -10,7 +10,7 @@ extern crate bitcoin_hashes;
#[cfg(feature = "alloc")] use alloc::string::ToString;
use bitcoin_hashes::{sha256, Hash, HashEngine};
use bitcoin_io::io::Write;
use bitcoin_io::Write;
use core::str::FromStr;
use cortex_m_rt::entry;
use cortex_m_semihosting::{debug, hprintln};

View File

@ -66,7 +66,7 @@ impl_write!(
#[cfg(test)]
mod tests {
use bitcoin_io::io::Write;
use bitcoin_io::Write;
use crate::{hash160, hmac, ripemd160, sha1, sha256, sha256d, sha512, siphash24, Hash};

View File

@ -20,386 +20,334 @@
#[cfg(any(feature = "alloc", feature = "std"))]
extern crate alloc;
mod macros;
/// Standard I/O stream definitions which are API-equivalent to `std`'s `io` module. See
/// [`std::io`] for more info.
pub mod io {
#[cfg(any(feature = "alloc", feature = "std"))]
#[cfg(any(feature = "alloc", feature = "std"))]
use alloc::boxed::Box;
use core::convert::TryInto;
use core::fmt::{Debug, Display, Formatter};
#[cfg(all(feature = "alloc", not(feature = "std")))]
mod sealed {
use alloc::boxed::Box;
use core::convert::TryInto;
use core::fmt::{Debug, Display, Formatter};
#[cfg(all(feature = "alloc", not(feature = "std")))]
mod sealed {
use alloc::boxed::Box;
use alloc::string::String;
use core::fmt::Debug;
pub trait IntoBoxDynDebug {
fn into(self) -> Box<dyn Debug + Send + Sync + 'static>;
}
impl IntoBoxDynDebug for &str {
fn into(self) -> Box<dyn Debug + Send + Sync + 'static> { Box::new(String::from(self)) }
}
impl IntoBoxDynDebug for String {
fn into(self) -> Box<dyn Debug + Send + Sync + 'static> { Box::new(self) }
}
use alloc::string::String;
use core::fmt::Debug;
pub trait IntoBoxDynDebug {
fn into(self) -> Box<dyn Debug + Send + Sync + 'static>;
}
#[derive(Debug)]
pub struct Error {
kind: ErrorKind,
#[cfg(feature = "std")]
error: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
#[cfg(all(feature = "alloc", not(feature = "std")))]
error: Option<Box<dyn Debug + Send + Sync + 'static>>,
impl IntoBoxDynDebug for &str {
fn into(self) -> Box<dyn Debug + Send + Sync + 'static> { Box::new(String::from(self)) }
}
impl Error {
#[cfg(feature = "std")]
pub fn new<E>(kind: ErrorKind, error: E) -> Error
where
E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
{
Self { kind, error: Some(error.into()) }
}
#[cfg(all(feature = "alloc", not(feature = "std")))]
pub fn new<E: sealed::IntoBoxDynDebug>(kind: ErrorKind, error: E) -> Error {
Self { kind, error: Some(error.into()) }
}
pub fn kind(&self) -> ErrorKind { self.kind }
impl IntoBoxDynDebug for String {
fn into(self) -> Box<dyn Debug + Send + Sync + 'static> { Box::new(self) }
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Error {
Self {
kind,
#[cfg(any(feature = "std", feature = "alloc"))]
error: None,
}
}
}
impl Display for Error {
fn fmt(&self, fmt: &mut Formatter) -> core::result::Result<(), core::fmt::Error> {
fmt.write_fmt(format_args!("I/O Error: {}", self.kind.description()))?;
#[cfg(any(feature = "alloc", feature = "std"))]
if let Some(e) = &self.error {
fmt.write_fmt(format_args!(". {:?}", e))?;
}
Ok(())
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.error.as_ref().and_then(|e| e.as_ref().source())
}
#[allow(deprecated)]
fn description(&self) -> &str {
match self.error.as_ref() {
Some(e) => e.description(),
None => self.kind.description(),
}
}
#[allow(deprecated)]
fn cause(&self) -> Option<&dyn std::error::Error> {
self.error.as_ref().and_then(|e| e.as_ref().cause())
}
}
impl Error {
#[cfg(feature = "std")]
pub fn get_ref(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> {
self.error.as_deref()
}
#[cfg(all(feature = "alloc", not(feature = "std")))]
pub fn get_ref(&self) -> Option<&(dyn Debug + Send + Sync + 'static)> {
self.error.as_deref()
}
}
#[cfg(feature = "std")]
impl From<std::io::Error> for Error {
fn from(o: std::io::Error) -> Error {
Self { kind: ErrorKind::from_std(o.kind()), error: o.into_inner() }
}
}
#[cfg(feature = "std")]
impl From<Error> for std::io::Error {
fn from(o: Error) -> std::io::Error {
if let Some(err) = o.error {
std::io::Error::new(o.kind.to_std(), err)
} else {
o.kind.to_std().into()
}
}
}
macro_rules! define_errorkind {
($($kind: ident),*) => {
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
/// A minimal subset of [`std::io::ErrorKind`] which is used for [`Error`]. Note that, as with
/// [`std::io`], only [`Self::Interrupted`] has defined semantics in this crate, all other
/// variants are provided here only to provide higher-fidelity conversions to and from
/// [`std::io::Error`].
pub enum ErrorKind {
$($kind),*
}
impl ErrorKind {
fn description(&self) -> &'static str {
match self {
$(Self::$kind => stringify!($kind)),*
}
}
#[cfg(feature = "std")]
fn to_std(self) -> std::io::ErrorKind {
match self {
$(Self::$kind => std::io::ErrorKind::$kind),*
}
}
#[cfg(feature = "std")]
fn from_std(o: std::io::ErrorKind) -> ErrorKind {
match o {
$(std::io::ErrorKind::$kind => ErrorKind::$kind),*,
_ => ErrorKind::Other
}
}
}
}
}
define_errorkind!(
NotFound,
PermissionDenied,
ConnectionRefused,
ConnectionReset,
ConnectionAborted,
NotConnected,
AddrInUse,
AddrNotAvailable,
BrokenPipe,
AlreadyExists,
WouldBlock,
InvalidInput,
InvalidData,
TimedOut,
WriteZero,
Interrupted,
UnexpectedEof,
// Note: Any time we bump the MSRV any new error kinds should be added here!
Other
);
pub type Result<T> = core::result::Result<T, Error>;
/// 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(ErrorKind::UnexpectedEof.into()),
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> {
Ok(<R as std::io::Read>::read(self, buf)?)
}
}
#[cfg(not(feature = "std"))]
impl Read for &[u8] {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let cnt = core::cmp::min(self.len(), buf.len());
buf[..cnt].copy_from_slice(&self[..cnt]);
*self = &self[cnt..];
Ok(cnt)
}
}
pub struct Cursor<T> {
inner: T,
pos: u64,
}
impl<T: AsRef<[u8]>> Cursor<T> {
#[inline]
pub fn new(inner: T) -> Self { Cursor { inner, pos: 0 } }
#[inline]
pub fn position(&self) -> u64 { self.pos }
#[inline]
pub fn into_inner(self) -> T { self.inner }
}
impl<T: AsRef<[u8]>> Read for Cursor<T> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let inner: &[u8] = self.inner.as_ref();
let start_pos = self.pos.try_into().unwrap_or(inner.len());
let read = core::cmp::min(inner.len().saturating_sub(start_pos), buf.len());
buf[..read].copy_from_slice(&inner[start_pos..start_pos + read]);
self.pos = self
.pos
.saturating_add(read.try_into().unwrap_or(u64::max_value() /* unreachable */));
Ok(read)
}
}
/// A generic trait describing an output stream. See [`std::io::Write`] for more info.
pub trait Write {
fn write(&mut self, buf: &[u8]) -> Result<usize>;
fn flush(&mut self) -> Result<()>;
#[inline]
fn write_all(&mut self, mut buf: &[u8]) -> Result<()> {
while !buf.is_empty() {
match self.write(buf) {
Ok(0) => return Err(ErrorKind::UnexpectedEof.into()),
Ok(len) => buf = &buf[len..],
Err(e) if e.kind() == ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(())
}
}
#[cfg(feature = "std")]
impl<W: std::io::Write> Write for W {
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize> {
Ok(<W as std::io::Write>::write(self, buf)?)
}
#[inline]
fn flush(&mut self) -> Result<()> { Ok(<W as std::io::Write>::flush(self)?) }
}
#[cfg(all(feature = "alloc", not(feature = "std")))]
impl Write for alloc::vec::Vec<u8> {
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize> {
self.extend_from_slice(buf);
Ok(buf.len())
}
#[inline]
fn flush(&mut self) -> Result<()> { Ok(()) }
}
#[cfg(not(feature = "std"))]
impl<'a> Write for &'a mut [u8] {
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize> {
let cnt = core::cmp::min(self.len(), buf.len());
self[..cnt].copy_from_slice(&buf[..cnt]);
*self = &mut core::mem::take(self)[cnt..];
Ok(cnt)
}
#[inline]
fn flush(&mut self) -> Result<()> { Ok(()) }
}
/// A sink to which all writes succeed. See [`std::io::Sink`] for more info.
pub struct Sink;
#[cfg(not(feature = "std"))]
impl Write for Sink {
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize> { Ok(buf.len()) }
#[inline]
fn write_all(&mut self, _: &[u8]) -> Result<()> { Ok(()) }
#[inline]
fn flush(&mut self) -> Result<()> { Ok(()) }
}
#[cfg(feature = "std")]
impl std::io::Write for Sink {
#[inline]
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { Ok(buf.len()) }
#[inline]
fn write_all(&mut self, _: &[u8]) -> std::io::Result<()> { Ok(()) }
#[inline]
fn flush(&mut self) -> std::io::Result<()> { Ok(()) }
}
/// Returns a sink to which all writes succeed. See [`std::io::sink`] for more info.
pub fn sink() -> Sink { Sink }
}
#[doc(hidden)]
#[cfg(feature = "std")]
/// Re-export std for the below macro
pub use std as _std;
#[derive(Debug)]
pub struct Error {
kind: ErrorKind,
#[cfg(feature = "std")]
error: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
#[cfg(all(feature = "alloc", not(feature = "std")))]
error: Option<Box<dyn Debug + Send + Sync + 'static>>,
}
impl Error {
#[cfg(feature = "std")]
pub fn new<E>(kind: ErrorKind, error: E) -> Error
where
E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
{
Self { kind, error: Some(error.into()) }
}
#[cfg(all(feature = "alloc", not(feature = "std")))]
pub fn new<E: sealed::IntoBoxDynDebug>(kind: ErrorKind, error: E) -> Error {
Self { kind, error: Some(error.into()) }
}
pub fn kind(&self) -> ErrorKind { self.kind }
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Error {
Self {
kind,
#[cfg(any(feature = "std", feature = "alloc"))]
error: None,
}
}
}
impl Display for Error {
fn fmt(&self, fmt: &mut Formatter) -> core::result::Result<(), core::fmt::Error> {
fmt.write_fmt(format_args!("I/O Error: {}", self.kind.description()))?;
#[cfg(any(feature = "alloc", feature = "std"))]
if let Some(e) = &self.error {
fmt.write_fmt(format_args!(". {:?}", e))?;
}
Ok(())
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.error.as_ref().and_then(|e| e.as_ref().source())
}
#[allow(deprecated)]
fn description(&self) -> &str {
match self.error.as_ref() {
Some(e) => e.description(),
None => self.kind.description(),
}
}
#[allow(deprecated)]
fn cause(&self) -> Option<&dyn std::error::Error> {
self.error.as_ref().and_then(|e| e.as_ref().cause())
}
}
impl Error {
#[cfg(feature = "std")]
pub fn get_ref(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> {
self.error.as_deref()
}
#[cfg(all(feature = "alloc", not(feature = "std")))]
pub fn get_ref(&self) -> Option<&(dyn Debug + Send + Sync + 'static)> {
self.error.as_deref()
}
}
#[cfg(feature = "std")]
impl From<std::io::Error> for Error {
fn from(o: std::io::Error) -> Error {
Self { kind: ErrorKind::from_std(o.kind()), error: o.into_inner() }
}
}
#[cfg(feature = "std")]
impl From<Error> for std::io::Error {
fn from(o: Error) -> std::io::Error {
if let Some(err) = o.error {
std::io::Error::new(o.kind.to_std(), err)
} else {
o.kind.to_std().into()
}
}
}
macro_rules! define_errorkind {
($($kind: ident),*) => {
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
/// A minimal subset of [`std::io::ErrorKind`] which is used for [`Error`]. Note that, as with
/// [`std::io`], only [`Self::Interrupted`] has defined semantics in this crate, all other
/// variants are provided here only to provide higher-fidelity conversions to and from
/// [`std::io::Error`].
pub enum ErrorKind {
$($kind),*
}
impl ErrorKind {
fn description(&self) -> &'static str {
match self {
$(Self::$kind => stringify!($kind)),*
}
}
#[cfg(feature = "std")]
fn to_std(self) -> std::io::ErrorKind {
match self {
$(Self::$kind => std::io::ErrorKind::$kind),*
}
}
#[cfg(feature = "std")]
fn from_std(o: std::io::ErrorKind) -> ErrorKind {
match o {
$(std::io::ErrorKind::$kind => ErrorKind::$kind),*,
_ => ErrorKind::Other
}
}
}
}
}
define_errorkind!(
NotFound,
PermissionDenied,
ConnectionRefused,
ConnectionReset,
ConnectionAborted,
NotConnected,
AddrInUse,
AddrNotAvailable,
BrokenPipe,
AlreadyExists,
WouldBlock,
InvalidInput,
InvalidData,
TimedOut,
WriteZero,
Interrupted,
UnexpectedEof,
// Note: Any time we bump the MSRV any new error kinds should be added here!
Other
);
pub type Result<T> = core::result::Result<T, Error>;
/// 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(ErrorKind::UnexpectedEof.into()),
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> {
Ok(<R as std::io::Read>::read(self, buf)?)
}
}
#[macro_export]
/// Because we cannot provide a blanket implementation of [`std::io::Write`] for all implementers
/// of this crate's `io::Write` trait, we provide this macro instead.
///
/// This macro will implement `Write` given a `write` and `flush` fn, either by implementing the
/// crate's native `io::Write` trait directly, or a more generic trait from `std` for users using
/// that feature. In any case, this crate's `io::Write` feature will be implemented for the given
/// type, even if indirectly.
#[cfg(not(feature = "std"))]
macro_rules! impl_write {
($ty: ty, $write_fn: expr, $flush_fn: expr $(, $bounded_ty: ident : $bounds: path),*) => {
impl<$($bounded_ty: $bounds),*> $crate::io::Write for $ty {
#[inline]
fn write(&mut self, buf: &[u8]) -> $crate::io::Result<usize> {
$write_fn(self, buf)
}
#[inline]
fn flush(&mut self) -> $crate::io::Result<()> {
$flush_fn(self)
}
}
impl Read for &[u8] {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let cnt = core::cmp::min(self.len(), buf.len());
buf[..cnt].copy_from_slice(&self[..cnt]);
*self = &self[cnt..];
Ok(cnt)
}
}
#[macro_export]
/// Because we cannot provide a blanket implementation of [`std::io::Write`] for all implementers
/// of this crate's `io::Write` trait, we provide this macro instead.
///
/// This macro will implement `Write` given a `write` and `flush` fn, either by implementing the
/// crate's native `io::Write` trait directly, or a more generic trait from `std` for users using
/// that feature. In any case, this crate's `io::Write` feature will be implemented for the given
/// type, even if indirectly.
#[cfg(feature = "std")]
macro_rules! impl_write {
($ty: ty, $write_fn: expr, $flush_fn: expr $(, $bounded_ty: ident : $bounds: path),*) => {
impl<$($bounded_ty: $bounds),*> $crate::_std::io::Write for $ty {
#[inline]
fn write(&mut self, buf: &[u8]) -> $crate::_std::io::Result<usize> {
$write_fn(self, buf)
}
#[inline]
fn flush(&mut self) -> $crate::_std::io::Result<()> {
$flush_fn(self)
}
}
pub struct Cursor<T> {
inner: T,
pos: u64,
}
impl<T: AsRef<[u8]>> Cursor<T> {
#[inline]
pub fn new(inner: T) -> Self { Cursor { inner, pos: 0 } }
#[inline]
pub fn position(&self) -> u64 { self.pos }
#[inline]
pub fn into_inner(self) -> T { self.inner }
}
impl<T: AsRef<[u8]>> Read for Cursor<T> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let inner: &[u8] = self.inner.as_ref();
let start_pos = self.pos.try_into().unwrap_or(inner.len());
let read = core::cmp::min(inner.len().saturating_sub(start_pos), buf.len());
buf[..read].copy_from_slice(&inner[start_pos..start_pos + read]);
self.pos = self
.pos
.saturating_add(read.try_into().unwrap_or(u64::max_value() /* unreachable */));
Ok(read)
}
}
/// A generic trait describing an output stream. See [`std::io::Write`] for more info.
pub trait Write {
fn write(&mut self, buf: &[u8]) -> Result<usize>;
fn flush(&mut self) -> Result<()>;
#[inline]
fn write_all(&mut self, mut buf: &[u8]) -> Result<()> {
while !buf.is_empty() {
match self.write(buf) {
Ok(0) => return Err(ErrorKind::UnexpectedEof.into()),
Ok(len) => buf = &buf[len..],
Err(e) if e.kind() == ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(())
}
}
#[cfg(feature = "std")]
impl<W: std::io::Write> Write for W {
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize> {
Ok(<W as std::io::Write>::write(self, buf)?)
}
#[inline]
fn flush(&mut self) -> Result<()> { Ok(<W as std::io::Write>::flush(self)?) }
}
#[cfg(all(feature = "alloc", not(feature = "std")))]
impl Write for alloc::vec::Vec<u8> {
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize> {
self.extend_from_slice(buf);
Ok(buf.len())
}
#[inline]
fn flush(&mut self) -> Result<()> { Ok(()) }
}
#[cfg(not(feature = "std"))]
impl<'a> Write for &'a mut [u8] {
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize> {
let cnt = core::cmp::min(self.len(), buf.len());
self[..cnt].copy_from_slice(&buf[..cnt]);
*self = &mut core::mem::take(self)[cnt..];
Ok(cnt)
}
#[inline]
fn flush(&mut self) -> Result<()> { Ok(()) }
}
/// A sink to which all writes succeed. See [`std::io::Sink`] for more info.
pub struct Sink;
#[cfg(not(feature = "std"))]
impl Write for Sink {
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize> { Ok(buf.len()) }
#[inline]
fn write_all(&mut self, _: &[u8]) -> Result<()> { Ok(()) }
#[inline]
fn flush(&mut self) -> Result<()> { Ok(()) }
}
#[cfg(feature = "std")]
impl std::io::Write for Sink {
#[inline]
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { Ok(buf.len()) }
#[inline]
fn write_all(&mut self, _: &[u8]) -> std::io::Result<()> { Ok(()) }
#[inline]
fn flush(&mut self) -> std::io::Result<()> { Ok(()) }
}
/// Returns a sink to which all writes succeed. See [`std::io::sink`] for more info.
pub fn sink() -> Sink { Sink }

49
io/src/macros.rs Normal file
View File

@ -0,0 +1,49 @@
//! Public macros for porvide.d for users to be able implement our `io::Write` trait.
#[macro_export]
/// Because we cannot provide a blanket implementation of [`std::io::Write`] for all implementers
/// of this crate's `io::Write` trait, we provide this macro instead.
///
/// This macro will implement `Write` given a `write` and `flush` fn, either by implementing the
/// crate's native `io::Write` trait directly, or a more generic trait from `std` for users using
/// that feature. In any case, this crate's `io::Write` feature will be implemented for the given
/// type, even if indirectly.
#[cfg(not(feature = "std"))]
macro_rules! impl_write {
($ty: ty, $write_fn: expr, $flush_fn: expr $(, $bounded_ty: ident : $bounds: path),*) => {
impl<$($bounded_ty: $bounds),*> $crate::Write for $ty {
#[inline]
fn write(&mut self, buf: &[u8]) -> $crate::Result<usize> {
$write_fn(self, buf)
}
#[inline]
fn flush(&mut self) -> $crate::Result<()> {
$flush_fn(self)
}
}
}
}
#[macro_export]
/// Because we cannot provide a blanket implementation of [`std::io::Write`] for all implementers
/// of this crate's `io::Write` trait, we provide this macro instead.
///
/// This macro will implement `Write` given a `write` and `flush` fn, either by implementing the
/// crate's native `io::Write` trait directly, or a more generic trait from `std` for users using
/// that feature. In any case, this crate's `io::Write` feature will be implemented for the given
/// type, even if indirectly.
#[cfg(feature = "std")]
macro_rules! impl_write {
($ty: ty, $write_fn: expr, $flush_fn: expr $(, $bounded_ty: ident : $bounds: path),*) => {
impl<$($bounded_ty: $bounds),*> std::io::Write for $ty {
#[inline]
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
$write_fn(self, buf)
}
#[inline]
fn flush(&mut self) -> std::io::Result<()> {
$flush_fn(self)
}
}
}
}