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: ACKf764a607ac
Kixunil: ACKf764a607ac
Tree-SHA512: 38888b0c23d5f2cd874f77dd332fe4fa4b9acb90e3a2dac19e62ed3d98151acd7480c719aa85434e1a3de987af2c4f565528a914a14d5fd3f0f0e410cbdf5d40
This commit is contained in:
commit
a3f6f53d37
|
@ -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 }
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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::*;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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>)
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -21,7 +21,6 @@ use core::iter;
|
|||
use hashes::Hash;
|
||||
|
||||
use crate::consensus::encode::Encodable;
|
||||
use crate::io;
|
||||
use crate::prelude::*;
|
||||
|
||||
#[rustfmt::skip]
|
||||
|
|
|
@ -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::*;
|
||||
|
||||
|
|
|
@ -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::{
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
684
io/src/lib.rs
684
io/src/lib.rs
|
@ -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 }
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue