diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock index 2a6ea499..ea1e7cee 100644 --- a/Cargo-minimal.lock +++ b/Cargo-minimal.lock @@ -41,6 +41,7 @@ dependencies = [ "bitcoin_hashes", "bitcoinconsensus", "core2", + "hex-conservative", "hex_lit", "mutagen", "secp256k1", @@ -74,6 +75,7 @@ version = "0.12.0" dependencies = [ "bitcoin-internals", "core2", + "hex-conservative", "schemars", "serde", "serde_json", @@ -163,6 +165,15 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" +[[package]] +name = "hex-conservative" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" +dependencies = [ + "core2", +] + [[package]] name = "hex_lit" version = "0.1.1" diff --git a/Cargo-recent.lock b/Cargo-recent.lock index 9f8f80e7..31b69aaa 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -40,6 +40,7 @@ dependencies = [ "bitcoin_hashes", "bitcoinconsensus", "core2", + "hex-conservative", "hex_lit", "mutagen", "secp256k1", @@ -73,6 +74,7 @@ version = "0.12.0" dependencies = [ "bitcoin-internals", "core2", + "hex-conservative", "schemars", "serde", "serde_json", @@ -156,6 +158,15 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" +[[package]] +name = "hex-conservative" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" +dependencies = [ + "core2", +] + [[package]] name = "hex_lit" version = "0.1.1" diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml index c4d080da..f7a12a22 100644 --- a/bitcoin/Cargo.toml +++ b/bitcoin/Cargo.toml @@ -26,8 +26,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", "hashes/std", "bech32/std", "internals/std"] -no-std = ["core2", "hashes/alloc", "hashes/core2", "secp256k1/alloc"] +std = ["secp256k1/std", "hashes/std", "bech32/std", "internals/std", "hex/std"] +no-std = ["core2", "hashes/alloc", "hashes/core2", "secp256k1/alloc", "hex/alloc", "hex/core2"] [package.metadata.docs.rs] all-features = true @@ -35,6 +35,7 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] internals = { package = "bitcoin-internals", version = "0.2.0" } +hex = { package = "hex-conservative", version = "0.1.1", default-features = false } bech32 = { version = "0.9.0", default-features = false } hashes = { package = "bitcoin_hashes", version = "0.12.0", default-features = false } secp256k1 = { version = "0.27.0", default-features = false, features = ["bitcoin_hashes"] } @@ -43,6 +44,8 @@ hex_lit = "0.1.1" base64 = { version = "0.13.0", 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 } + +# There is no reason to use this dependency directly, it is activated by the "no-std" feature. core2 = { version = "0.3.2", default-features = false, features = ["alloc"], optional = true } # Do NOT use this as a feature! Use the `serde` feature instead. actual-serde = { package = "serde", version = "1.0.103", default-features = false, features = [ "derive", "alloc" ], optional = true } diff --git a/bitcoin/examples/bip32.rs b/bitcoin/examples/bip32.rs index 7904c5f6..f9c81343 100644 --- a/bitcoin/examples/bip32.rs +++ b/bitcoin/examples/bip32.rs @@ -5,7 +5,7 @@ use std::{env, process}; use bitcoin::address::Address; use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey}; -use bitcoin::hashes::hex::FromHex; +use bitcoin::hex::FromHex; use bitcoin::secp256k1::ffi::types::AlignedType; use bitcoin::secp256k1::Secp256k1; use bitcoin::PublicKey; diff --git a/bitcoin/src/bip152.rs b/bitcoin/src/bip152.rs index 380a8402..b04f352e 100644 --- a/bitcoin/src/bip152.rs +++ b/bitcoin/src/bip152.rs @@ -371,7 +371,7 @@ impl BlockTransactions { #[cfg(test)] mod test { - use hashes::hex::FromHex; + use hex::FromHex; use super::*; use crate::blockdata::locktime::absolute; diff --git a/bitcoin/src/bip32.rs b/bitcoin/src/bip32.rs index dc381dc0..036b56af 100644 --- a/bitcoin/src/bip32.rs +++ b/bitcoin/src/bip32.rs @@ -12,7 +12,7 @@ use core::ops::Index; use core::str::FromStr; use core::{fmt, slice}; -use hashes::{hex, sha512, Hash, HashEngine, Hmac, HmacEngine}; +use hashes::{sha512, Hash, HashEngine, Hmac, HmacEngine}; use internals::{impl_array_newtype, write_err}; use secp256k1::{self, Secp256k1, XOnlyPublicKey}; #[cfg(feature = "serde")] @@ -468,7 +468,7 @@ pub enum Error { /// Base58 encoding error Base58(base58::Error), /// Hexadecimal decoding error - Hex(hex::Error), + Hex(hex::HexToArrayError), /// `PublicKey` hex should be 66 or 130 digits long. InvalidPublicKeyHexLength(usize), } diff --git a/bitcoin/src/blockdata/block.rs b/bitcoin/src/blockdata/block.rs index dcacffec..921565e6 100644 --- a/bitcoin/src/blockdata/block.rs +++ b/bitcoin/src/blockdata/block.rs @@ -423,7 +423,7 @@ impl std::error::Error for ValidationError { #[cfg(test)] mod tests { - use hashes::hex::FromHex; + use hex::FromHex; use super::*; use crate::consensus::encode::{deserialize, serialize}; diff --git a/bitcoin/src/blockdata/script/mod.rs b/bitcoin/src/blockdata/script/mod.rs index ecd68547..640e7226 100644 --- a/bitcoin/src/blockdata/script/mod.rs +++ b/bitcoin/src/blockdata/script/mod.rs @@ -513,7 +513,7 @@ impl<'de> serde::Deserialize<'de> for ScriptBuf { { use core::fmt::Formatter; - use hashes::hex::FromHex; + use hex::FromHex; if deserializer.is_human_readable() { struct Visitor; diff --git a/bitcoin/src/blockdata/script/owned.rs b/bitcoin/src/blockdata/script/owned.rs index eb6190ca..db917225 100644 --- a/bitcoin/src/blockdata/script/owned.rs +++ b/bitcoin/src/blockdata/script/owned.rs @@ -3,7 +3,6 @@ #[cfg(doc)] use core::ops::Deref; -use hashes::hex; use secp256k1::{Secp256k1, Verification}; use crate::blockdata::opcodes::all::*; @@ -156,8 +155,8 @@ impl ScriptBuf { } /// Creates a [`ScriptBuf`] from a hex string. - pub fn from_hex(s: &str) -> Result { - use hashes::hex::FromHex; + pub fn from_hex(s: &str) -> Result { + use hex::FromHex; let v = Vec::from_hex(s)?; Ok(ScriptBuf::from_bytes(v)) diff --git a/bitcoin/src/blockdata/transaction.rs b/bitcoin/src/blockdata/transaction.rs index 88326540..52d06c38 100644 --- a/bitcoin/src/blockdata/transaction.rs +++ b/bitcoin/src/blockdata/transaction.rs @@ -95,7 +95,7 @@ impl fmt::Display for OutPoint { #[non_exhaustive] pub enum ParseOutPointError { /// Error in TXID part. - Txid(hashes::hex::Error), + Txid(hex::HexToArrayError), /// Error in vout part. Vout(crate::error::ParseIntError), /// Error in general format. @@ -1390,7 +1390,7 @@ impl InputWeightPrediction { mod tests { use core::str::FromStr; - use hashes::hex::FromHex; + use hex::FromHex; use super::*; use crate::blockdata::constants::WITNESS_SCALE_FACTOR; diff --git a/bitcoin/src/blockdata/witness.rs b/bitcoin/src/blockdata/witness.rs index d0b7408c..892de5fa 100644 --- a/bitcoin/src/blockdata/witness.rs +++ b/bitcoin/src/blockdata/witness.rs @@ -462,8 +462,8 @@ impl<'de> serde::Deserialize<'de> for Witness { self, mut a: A, ) -> Result { - use hashes::hex::Error::*; - use hashes::hex::FromHex; + use hex::FromHex; + use hex::HexToBytesError::*; use serde::de::{self, Unexpected}; let mut ret = match a.size_hint() { @@ -485,10 +485,6 @@ impl<'de> serde::Deserialize<'de> for Witness { }, OddLengthString(len) => de::Error::invalid_length(len, &"an even length string"), - InvalidLength(expected, got) => { - let exp = format!("expected length: {}", expected); - de::Error::invalid_length(got, &exp.as_str()) - } })?; ret.push(vec); } diff --git a/bitcoin/src/consensus/serde.rs b/bitcoin/src/consensus/serde.rs index c2393635..2b857547 100644 --- a/bitcoin/src/consensus/serde.rs +++ b/bitcoin/src/consensus/serde.rs @@ -38,7 +38,7 @@ pub mod hex { use core::fmt; use core::marker::PhantomData; - use internals::hex::BufEncoder; + use hex::buf_encoder::BufEncoder; /// Marker for upper/lower case type-level flags ("type-level enum"). /// @@ -54,15 +54,15 @@ pub mod hex { mod sealed { pub trait Case { /// Internal detail, don't depend on it!!! - const INTERNAL_CASE: internals::hex::Case; + const INTERNAL_CASE: hex::Case; } impl Case for super::Lower { - const INTERNAL_CASE: internals::hex::Case = internals::hex::Case::Lower; + const INTERNAL_CASE: hex::Case = hex::Case::Lower; } impl Case for super::Upper { - const INTERNAL_CASE: internals::hex::Case = internals::hex::Case::Upper; + const INTERNAL_CASE: hex::Case = hex::Case::Upper; } } @@ -102,18 +102,18 @@ pub mod hex { /// Error returned when a hex string decoder can't be created. #[derive(Debug)] - pub struct DecodeInitError(hashes::hex::Error); + pub struct DecodeInitError(hex::HexToBytesError); /// Error returned when a hex string contains invalid characters. #[derive(Debug)] - pub struct DecodeError(hashes::hex::Error); + pub struct DecodeError(hex::HexToBytesError); /// Hex decoder state. - pub struct Decoder<'a>(hashes::hex::HexIterator<'a>); + pub struct Decoder<'a>(hex::HexToBytesIter<'a>); impl<'a> Decoder<'a> { fn new(s: &'a str) -> Result { - match hashes::hex::HexIterator::new(s) { + match hex::HexToBytesIter::new(s) { Ok(iter) => Ok(Decoder(iter)), Err(error) => Err(DecodeInitError(error)), } @@ -138,10 +138,10 @@ pub mod hex { impl super::IntoDeError for DecodeInitError { fn into_de_error(self) -> E { - use hashes::hex::Error; + use hex::HexToBytesError; match self.0 { - Error::OddLengthString(len) => + HexToBytesError::OddLengthString(len) => E::invalid_length(len, &"an even number of ASCII-encoded hex digits"), error => panic!("unexpected error: {:?}", error), } @@ -150,15 +150,15 @@ pub mod hex { impl super::IntoDeError for DecodeError { fn into_de_error(self) -> E { - use hashes::hex::Error; + use hex::HexToBytesError; use serde::de::Unexpected; const EXPECTED_CHAR: &str = "an ASCII-encoded hex digit"; match self.0 { - Error::InvalidChar(c) if c.is_ascii() => + HexToBytesError::InvalidChar(c) if c.is_ascii() => E::invalid_value(Unexpected::Char(c as _), &EXPECTED_CHAR), - Error::InvalidChar(c) => + HexToBytesError::InvalidChar(c) => E::invalid_value(Unexpected::Unsigned(c.into()), &EXPECTED_CHAR), error => panic!("unexpected error: {:?}", error), } diff --git a/bitcoin/src/crypto/ecdsa.rs b/bitcoin/src/crypto/ecdsa.rs index 65f94446..7f0e6d15 100644 --- a/bitcoin/src/crypto/ecdsa.rs +++ b/bitcoin/src/crypto/ecdsa.rs @@ -7,8 +7,7 @@ use core::str::FromStr; use core::{fmt, iter}; -use hashes::hex::{self, FromHex}; -use internals::hex::display::DisplayHex; +use hex::FromHex; use internals::write_err; use secp256k1; @@ -188,8 +187,8 @@ impl<'a> IntoIterator for &'a SerializedSignature { #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[non_exhaustive] pub enum Error { - /// Hex encoding error - HexEncoding(hex::Error), + /// Hex decoding error + Hex(hex::HexToBytesError), /// Base58 encoding error NonStandardSighashType(u32), /// Empty Signature @@ -201,7 +200,7 @@ pub enum Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Error::HexEncoding(ref e) => write_err!(f, "Signature hex encoding error"; e), + Error::Hex(ref e) => write_err!(f, "Signature hex decoding error"; e), Error::NonStandardSighashType(hash_ty) => write!(f, "Non standard signature hash type {}", hash_ty), Error::EmptySignature => write!(f, "Empty ECDSA signature"), @@ -216,7 +215,7 @@ impl std::error::Error for Error { use self::Error::*; match self { - HexEncoding(e) => Some(e), + Hex(e) => Some(e), Secp256k1(e) => Some(e), NonStandardSighashType(_) | EmptySignature => None, } @@ -231,6 +230,6 @@ impl From for Error { fn from(err: NonStandardSighashType) -> Self { Error::NonStandardSighashType(err.0) } } -impl From for Error { - fn from(err: hex::Error) -> Self { Error::HexEncoding(err) } +impl From for Error { + fn from(err: hex::HexToBytesError) -> Self { Error::Hex(err) } } diff --git a/bitcoin/src/crypto/key.rs b/bitcoin/src/crypto/key.rs index 41c541c0..8471af53 100644 --- a/bitcoin/src/crypto/key.rs +++ b/bitcoin/src/crypto/key.rs @@ -9,8 +9,8 @@ use core::fmt::{self, Write}; use core::ops; use core::str::FromStr; -use hashes::hex::FromHex; -use hashes::{hash160, hex, Hash}; +use hashes::{hash160, Hash}; +use hex::FromHex; use internals::write_err; #[cfg(feature = "rand-std")] pub use secp256k1::rand; @@ -34,7 +34,7 @@ pub enum Error { /// Invalid key prefix error InvalidKeyPrefix(u8), /// Hex decoding error - Hex(hex::Error), + Hex(hex::HexToArrayError), /// `PublicKey` hex should be 66 or 130 digits long. InvalidHexLength(usize), } @@ -74,8 +74,8 @@ impl From for Error { fn from(e: secp256k1::Error) -> Error { Error::Secp256k1(e) } } -impl From for Error { - fn from(e: hex::Error) -> Self { Error::Hex(e) } +impl From for Error { + fn from(e: hex::HexToArrayError) -> Self { Error::Hex(e) } } /// A Bitcoin ECDSA public key @@ -735,7 +735,7 @@ impl From for TweakedPublicKey { mod tests { use std::str::FromStr; - use hashes::hex::FromHex; + use hex::FromHex; use secp256k1::Secp256k1; use super::*; diff --git a/bitcoin/src/crypto/sighash.rs b/bitcoin/src/crypto/sighash.rs index fe4f757d..93c00da9 100644 --- a/bitcoin/src/crypto/sighash.rs +++ b/bitcoin/src/crypto/sighash.rs @@ -1110,8 +1110,8 @@ fn is_invalid_use_of_sighash_single(sighash: u32, input_index: usize, output_len mod tests { use std::str::FromStr; - use hashes::hex::FromHex; use hashes::HashEngine; + use hex::FromHex; use super::*; use crate::address::Address; diff --git a/bitcoin/src/internal_macros.rs b/bitcoin/src/internal_macros.rs index ae0e54f6..52c446f8 100644 --- a/bitcoin/src/internal_macros.rs +++ b/bitcoin/src/internal_macros.rs @@ -51,7 +51,7 @@ pub(crate) use test_macros::*; #[cfg(test)] mod test_macros { - macro_rules! hex (($hex:expr) => ( as hashes::hex::FromHex>::from_hex($hex).unwrap())); + macro_rules! hex (($hex:expr) => ( as $crate::hex::FromHex>::from_hex($hex).unwrap())); pub(crate) use hex; } @@ -61,7 +61,7 @@ mod test_macros { /// - core::fmt::UpperHex /// - core::fmt::Display /// - core::str::FromStr -/// - hashes::hex::FromHex +/// - hex::FromHex macro_rules! impl_bytes_newtype { ($t:ident, $len:literal) => { impl $t { @@ -83,14 +83,14 @@ macro_rules! impl_bytes_newtype { impl core::fmt::LowerHex for $t { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - use internals::hex::{display, Case}; + use $crate::hex::{display, Case}; display::fmt_hex_exact!(f, $len, &self.0, Case::Lower) } } impl core::fmt::UpperHex for $t { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - use internals::hex::{display, Case}; + use $crate::hex::{display, Case}; display::fmt_hex_exact!(f, $len, &self.0, Case::Upper) } } @@ -107,30 +107,22 @@ macro_rules! impl_bytes_newtype { } } - impl $crate::hashes::hex::FromHex for $t { - fn from_byte_iter(iter: I) -> Result + impl $crate::hex::FromHex for $t { + type Err = $crate::hex::HexToArrayError; + + fn from_byte_iter(iter: I) -> Result where - I: core::iter::Iterator> + I: core::iter::Iterator> + core::iter::ExactSizeIterator + core::iter::DoubleEndedIterator, { - if iter.len() == $len { - let mut ret = [0; $len]; - for (n, byte) in iter.enumerate() { - ret[n] = byte?; - } - Ok($t(ret)) - } else { - Err($crate::hashes::hex::Error::InvalidLength(2 * $len, 2 * iter.len())) - } + Ok($t($crate::hex::FromHex::from_byte_iter(iter)?)) } } impl core::str::FromStr for $t { - type Err = $crate::hashes::hex::Error; - fn from_str(s: &str) -> Result { - $crate::hashes::hex::FromHex::from_hex(s) - } + type Err = $crate::hex::HexToArrayError; + fn from_str(s: &str) -> Result { $crate::hex::FromHex::from_hex(s) } } #[cfg(feature = "serde")] @@ -164,7 +156,7 @@ macro_rules! impl_bytes_newtype { use $crate::serde::de::Unexpected; if let Ok(hex) = core::str::from_utf8(v) { - $crate::hashes::hex::FromHex::from_hex(hex).map_err(E::custom) + $crate::hex::FromHex::from_hex(hex).map_err(E::custom) } else { return Err(E::invalid_value(Unexpected::Bytes(v), &self)); } @@ -174,7 +166,7 @@ macro_rules! impl_bytes_newtype { where E: $crate::serde::de::Error, { - $crate::hashes::hex::FromHex::from_hex(v).map_err(E::custom) + $crate::hex::FromHex::from_hex(v).map_err(E::custom) } } diff --git a/bitcoin/src/lib.rs b/bitcoin/src/lib.rs index 5b3ef180..af6e9ed2 100644 --- a/bitcoin/src/lib.rs +++ b/bitcoin/src/lib.rs @@ -66,9 +66,15 @@ pub extern crate bech32; /// Bitcoin's libbitcoinconsensus with Rust binding. pub extern crate bitcoinconsensus; +#[cfg(not(feature = "std"))] +extern crate core2; + /// Rust implementation of cryptographic hash function algorithems. pub extern crate hashes; +/// Re-export the `hex-conservative` crate. +pub extern crate hex; + /// 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; @@ -186,7 +192,7 @@ mod prelude { #[cfg(not(feature = "std"))] pub use crate::io_extras::sink; - pub use internals::hex::display::DisplayHex; + pub use hex::DisplayHex; } #[cfg(bench)] diff --git a/bitcoin/src/merkle_tree/block.rs b/bitcoin/src/merkle_tree/block.rs index 8d031ef8..1a45eb1e 100644 --- a/bitcoin/src/merkle_tree/block.rs +++ b/bitcoin/src/merkle_tree/block.rs @@ -13,7 +13,7 @@ //! //! ```rust //! use bitcoin::hash_types::Txid; -//! use bitcoin::hashes::hex::FromHex; +//! use bitcoin::hex::FromHex; //! use bitcoin::{Block, MerkleBlock}; //! //! // Get the proof from a bitcoind by running in the terminal: @@ -73,7 +73,7 @@ impl MerkleBlock { /// /// ```rust /// use bitcoin::hash_types::Txid; - /// use bitcoin::hashes::hex::FromHex; + /// use bitcoin::hex::FromHex; /// use bitcoin::{Block, MerkleBlock}; /// /// // Block 80000 @@ -223,7 +223,7 @@ impl PartialMerkleTree { /// /// ```rust /// use bitcoin::hash_types::Txid; - /// use bitcoin::hashes::hex::FromHex; + /// use bitcoin::hex::FromHex; /// use bitcoin::merkle_tree::{MerkleBlock, PartialMerkleTree}; /// /// // Block 80000 @@ -751,7 +751,7 @@ mod tests { /// Returns a real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af) /// with 9 txs. fn get_block_13b8a() -> Block { - use hashes::hex::FromHex; + use hex::FromHex; let block_hex = include_str!("../../tests/data/block_13b8a.hex"); deserialize(&Vec::from_hex(block_hex).unwrap()).unwrap() } diff --git a/bitcoin/src/network/address.rs b/bitcoin/src/network/address.rs index 3dec4f14..f20907c3 100644 --- a/bitcoin/src/network/address.rs +++ b/bitcoin/src/network/address.rs @@ -306,7 +306,7 @@ mod test { use core::str::FromStr; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; - use hashes::hex::FromHex; + use hex::FromHex; use super::{AddrV2, AddrV2Message, Address}; use crate::consensus::encode::{deserialize, serialize}; diff --git a/bitcoin/src/network/constants.rs b/bitcoin/src/network/constants.rs index 202c2d69..882592a2 100644 --- a/bitcoin/src/network/constants.rs +++ b/bitcoin/src/network/constants.rs @@ -31,7 +31,7 @@ use core::fmt::Display; use core::str::FromStr; use core::{fmt, ops}; -use hashes::hex::{Error, FromHex}; +use hex::FromHex; use internals::{debug_from_display, write_err}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -313,7 +313,7 @@ impl Magic { #[derive(Debug, PartialEq, Eq, Clone)] pub struct ParseMagicError { /// The error that occurred when parsing the string. - error: Error, + error: hex::HexToArrayError, /// The byte string that failed to parse. magic: String, } @@ -362,7 +362,7 @@ impl TryFrom for Network { impl fmt::Display for Magic { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - internals::fmt_hex_exact!(f, 4, &self.0, internals::hex::Case::Lower)?; + hex::fmt_hex_exact!(f, 4, &self.0, hex::Case::Lower)?; Ok(()) } } @@ -370,14 +370,14 @@ debug_from_display!(Magic); impl fmt::LowerHex for Magic { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - internals::fmt_hex_exact!(f, 4, &self.0, internals::hex::Case::Lower)?; + hex::fmt_hex_exact!(f, 4, &self.0, hex::Case::Lower)?; Ok(()) } } impl fmt::UpperHex for Magic { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - internals::fmt_hex_exact!(f, 4, &self.0, internals::hex::Case::Upper)?; + hex::fmt_hex_exact!(f, 4, &self.0, hex::Case::Upper)?; Ok(()) } } diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs index d190bda8..2e647909 100644 --- a/bitcoin/src/pow.rs +++ b/bitcoin/src/pow.rs @@ -790,13 +790,13 @@ macro_rules! impl_hex { ($hex:ident, $case:expr) => { impl $hex for U256 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - internals::hex::display::fmt_hex_exact!(f, 32, &self.to_be_bytes(), $case) + hex::fmt_hex_exact!(f, 32, &self.to_be_bytes(), $case) } } }; } -impl_hex!(LowerHex, internals::hex::Case::Lower); -impl_hex!(UpperHex, internals::hex::Case::Upper); +impl_hex!(LowerHex, hex::Case::Lower); +impl_hex!(UpperHex, hex::Case::Upper); #[cfg(feature = "serde")] impl crate::serde::Serialize for U256 { @@ -825,7 +825,7 @@ impl<'de> crate::serde::Deserialize<'de> for U256 { fn deserialize>(d: D) -> Result { use core::convert::TryInto; - use hashes::hex::FromHex; + use hex::FromHex; use crate::serde::de; diff --git a/bitcoin/src/psbt/macros.rs b/bitcoin/src/psbt/macros.rs index 02bbd8bf..328bdb4d 100644 --- a/bitcoin/src/psbt/macros.rs +++ b/bitcoin/src/psbt/macros.rs @@ -4,7 +4,7 @@ macro_rules! hex_psbt { ($s:expr) => { <$crate::psbt::Psbt>::deserialize( - &<$crate::prelude::Vec as $crate::hashes::hex::FromHex>::from_hex($s).unwrap(), + &<$crate::prelude::Vec as $crate::hex::FromHex>::from_hex($s).unwrap(), ) }; } diff --git a/bitcoin/src/psbt/serialize.rs b/bitcoin/src/psbt/serialize.rs index 28ac1d85..dfca1b6c 100644 --- a/bitcoin/src/psbt/serialize.rs +++ b/bitcoin/src/psbt/serialize.rs @@ -178,9 +178,7 @@ impl Deserialize for ecdsa::Signature { ecdsa::Error::EmptySignature => Error::InvalidEcdsaSignature(e), ecdsa::Error::NonStandardSighashType(flag) => Error::NonStandardSighashType(flag), ecdsa::Error::Secp256k1(..) => Error::InvalidEcdsaSignature(e), - ecdsa::Error::HexEncoding(..) => { - unreachable!("Decoding from slice, not hex") - } + ecdsa::Error::Hex(..) => unreachable!("Decoding from slice, not hex"), }) } } diff --git a/bitcoin/src/serde_utils.rs b/bitcoin/src/serde_utils.rs index e3cdee7a..c0789e0c 100644 --- a/bitcoin/src/serde_utils.rs +++ b/bitcoin/src/serde_utils.rs @@ -12,7 +12,7 @@ impl<'a> serde::Serialize for SerializeBytesAsHex<'a> { where S: serde::Serializer, { - use internals::hex::display::DisplayHex; + use hex::DisplayHex; serializer.collect_str(&format_args!("{:x}", self.0.as_hex())) } @@ -24,7 +24,7 @@ pub mod btreemap_byte_values { // NOTE: This module can be exactly copied to use with HashMap. - use hashes::hex::FromHex; + use hex::FromHex; use serde; use crate::prelude::*; @@ -248,7 +248,7 @@ pub mod hex_bytes { //! Module for serialization of byte arrays as hex strings. #![allow(missing_docs)] - use hashes::hex::FromHex; + use hex::FromHex; use serde; pub fn serialize(bytes: &T, s: S) -> Result diff --git a/bitcoin/src/taproot.rs b/bitcoin/src/taproot.rs index ae1b3f45..9a90757f 100644 --- a/bitcoin/src/taproot.rs +++ b/bitcoin/src/taproot.rs @@ -1561,9 +1561,9 @@ impl std::error::Error for TaprootError { mod test { use core::str::FromStr; - use hashes::hex::FromHex; use hashes::sha256t::Tag; use hashes::{sha256, Hash, HashEngine}; + use hex::FromHex; use secp256k1::{VerifyOnly, XOnlyPublicKey}; use super::*; diff --git a/bitcoin/tests/psbt.rs b/bitcoin/tests/psbt.rs index 692cc9b6..5271c59f 100644 --- a/bitcoin/tests/psbt.rs +++ b/bitcoin/tests/psbt.rs @@ -9,7 +9,7 @@ use bitcoin::bip32::{ExtendedPrivKey, ExtendedPubKey, Fingerprint, IntoDerivatio use bitcoin::blockdata::opcodes::OP_0; use bitcoin::blockdata::script; use bitcoin::consensus::encode::{deserialize, serialize_hex}; -use bitcoin::hashes::hex::FromHex; +use bitcoin::hex::FromHex; use bitcoin::psbt::{Psbt, PsbtSighashType}; use bitcoin::script::PushBytes; use bitcoin::secp256k1::{self, Secp256k1}; diff --git a/bitcoin/tests/serde.rs b/bitcoin/tests/serde.rs index fc125975..e4a85060 100644 --- a/bitcoin/tests/serde.rs +++ b/bitcoin/tests/serde.rs @@ -31,8 +31,8 @@ use bitcoin::bip32::{ChildNumber, ExtendedPrivKey, ExtendedPubKey, KeySource}; use bitcoin::blockdata::locktime::{absolute, relative}; use bitcoin::blockdata::witness::Witness; use bitcoin::consensus::encode::deserialize; -use bitcoin::hashes::hex::FromHex; use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash}; +use bitcoin::hex::FromHex; use bitcoin::psbt::raw::{self, Key, Pair, ProprietaryKey}; use bitcoin::psbt::{Input, Output, Psbt, PsbtSighashType}; use bitcoin::sighash::{EcdsaSighashType, TapSighashType}; diff --git a/hashes/Cargo.toml b/hashes/Cargo.toml index 4ce146d6..30313e14 100644 --- a/hashes/Cargo.toml +++ b/hashes/Cargo.toml @@ -14,9 +14,11 @@ exclude = ["tests", "contrib"] [features] default = ["std"] -std = ["alloc", "internals/std"] -alloc = ["internals/alloc"] +std = ["alloc", "internals/std", "hex/std"] +alloc = ["internals/alloc", "hex/alloc"] serde-std = ["serde/std"] +# If you want I/O you must enable either "std" or "core2". +core2 = ["actual-core2", "hex/core2"] [package.metadata.docs.rs] all-features = true @@ -24,12 +26,15 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] internals = { package = "bitcoin-internals", version = "0.2.0" } +hex = { package = "hex-conservative", version = "0.1.1", default-features = false } -core2 = { version = "0.3.0", default_features = false, optional = true } schemars = { version = "0.8.3", optional = true } # Only enable this if you explicitly do not want to use "std", otherwise enable "serde-std". serde = { version = "1.0", default-features = false, optional = true } +# Do NOT use this feature! Use the "core2" feature instead. +actual-core2 = { package = "core2", version = "0.3.2", default-features = false, optional = true } + [dev-dependencies] serde_test = "1.0" serde_json = "1.0" diff --git a/hashes/src/hex.rs b/hashes/src/hex.rs deleted file mode 100644 index 31845a75..00000000 --- a/hashes/src/hex.rs +++ /dev/null @@ -1,225 +0,0 @@ -// SPDX-License-Identifier: CC0-1.0 - -//! Hex encoding and decoding. -//! - -use core::{fmt, str}; -#[cfg(feature = "std")] -use std::io; - -#[cfg(all(feature = "core2", not(feature = "std")))] -use core2::io; - -#[cfg(all(feature = "alloc", not(feature = "std")))] -use crate::alloc::vec::Vec; - -/// Hex decoding error. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Error { - /// Non-hexadecimal character. - InvalidChar(u8), - /// Purported hex string had odd length. - OddLengthString(usize), - /// Tried to parse fixed-length hash from a string with the wrong type (expected, got). - InvalidLength(usize, usize), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::InvalidChar(ch) => write!(f, "invalid hex character {}", ch), - Error::OddLengthString(ell) => write!(f, "odd hex string length {}", ell), - Error::InvalidLength(ell, ell2) => - write!(f, "bad hex string length {} (expected {})", ell2, ell), - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use self::Error::*; - - match self { - InvalidChar(_) | OddLengthString(_) | InvalidLength(_, _) => None, - } - } -} - -/// Trait for objects that can be deserialized from hex strings. -pub trait FromHex: Sized { - /// Produces an object from a byte iterator. - fn from_byte_iter(iter: I) -> Result - where - I: Iterator> + ExactSizeIterator + DoubleEndedIterator; - - /// Produces an object from a hex string. - fn from_hex(s: &str) -> Result { Self::from_byte_iter(HexIterator::new(s)?) } -} - -/// Iterator over a hex-encoded string slice which decodes hex and yields bytes. -pub struct HexIterator<'a> { - /// The `Bytes` iterator whose next two bytes will be decoded to yield - /// the next byte. - iter: str::Bytes<'a>, -} - -impl<'a> HexIterator<'a> { - /// Constructs a new `HexIterator` from a string slice. - /// - /// # Errors - /// - /// If the input string is of odd length. - pub fn new(s: &'a str) -> Result, Error> { - if s.len() % 2 != 0 { - Err(Error::OddLengthString(s.len())) - } else { - Ok(HexIterator { iter: s.bytes() }) - } - } -} - -fn chars_to_hex(hi: u8, lo: u8) -> Result { - let hih = (hi as char).to_digit(16).ok_or(Error::InvalidChar(hi))?; - let loh = (lo as char).to_digit(16).ok_or(Error::InvalidChar(lo))?; - - let ret = (hih << 4) + loh; - Ok(ret as u8) -} - -impl<'a> Iterator for HexIterator<'a> { - type Item = Result; - - fn next(&mut self) -> Option> { - let hi = self.iter.next()?; - let lo = self.iter.next().unwrap(); - Some(chars_to_hex(hi, lo)) - } - - fn size_hint(&self) -> (usize, Option) { - let (min, max) = self.iter.size_hint(); - (min / 2, max.map(|x| x / 2)) - } -} - -#[cfg(any(feature = "std", feature = "core2"))] -impl<'a> io::Read for HexIterator<'a> { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let mut bytes_read = 0usize; - for dst in buf { - match self.next() { - Some(Ok(src)) => { - *dst = src; - bytes_read += 1; - } - _ => break, - } - } - Ok(bytes_read) - } -} - -impl<'a> DoubleEndedIterator for HexIterator<'a> { - fn next_back(&mut self) -> Option> { - let lo = self.iter.next_back()?; - let hi = self.iter.next_back().unwrap(); - Some(chars_to_hex(hi, lo)) - } -} - -impl<'a> ExactSizeIterator for HexIterator<'a> {} - -#[cfg(any(test, feature = "std", feature = "alloc"))] -impl FromHex for Vec { - fn from_byte_iter(iter: I) -> Result - where - I: Iterator> + ExactSizeIterator + DoubleEndedIterator, - { - iter.collect() - } -} - -macro_rules! impl_fromhex_array { - ($len:expr) => { - impl FromHex for [u8; $len] { - fn from_byte_iter(iter: I) -> Result - where - I: Iterator> + ExactSizeIterator + DoubleEndedIterator, - { - if iter.len() == $len { - let mut ret = [0; $len]; - for (n, byte) in iter.enumerate() { - ret[n] = byte?; - } - Ok(ret) - } else { - Err(Error::InvalidLength(2 * $len, 2 * iter.len())) - } - } - } - }; -} - -impl_fromhex_array!(2); -impl_fromhex_array!(4); -impl_fromhex_array!(6); -impl_fromhex_array!(8); -impl_fromhex_array!(10); -impl_fromhex_array!(12); -impl_fromhex_array!(14); -impl_fromhex_array!(16); -impl_fromhex_array!(20); -impl_fromhex_array!(24); -impl_fromhex_array!(28); -impl_fromhex_array!(32); -impl_fromhex_array!(33); -impl_fromhex_array!(64); -impl_fromhex_array!(65); -impl_fromhex_array!(128); -impl_fromhex_array!(256); -impl_fromhex_array!(384); -impl_fromhex_array!(512); - -#[cfg(test)] -#[cfg(feature = "alloc")] -mod tests { - use internals::hex::exts::DisplayHex; - - use super::*; - - #[test] - fn hex_roundtrip() { - let expected = "0123456789abcdef"; - let expected_up = "0123456789ABCDEF"; - - let parse: Vec = FromHex::from_hex(expected).expect("parse lowercase string"); - assert_eq!(parse, vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); - let ser = parse.to_lower_hex_string(); - assert_eq!(ser, expected); - - let parse: Vec = FromHex::from_hex(expected_up).expect("parse uppercase string"); - assert_eq!(parse, vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); - let ser = parse.to_lower_hex_string(); - assert_eq!(ser, expected); - - let parse: [u8; 8] = FromHex::from_hex(expected_up).expect("parse uppercase string"); - assert_eq!(parse, [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); - let ser = parse.to_lower_hex_string(); - assert_eq!(ser, expected); - } - - #[test] - fn hex_error() { - let oddlen = "0123456789abcdef0"; - let badchar1 = "Z123456789abcdef"; - let badchar2 = "012Y456789abcdeb"; - let badchar3 = "«23456789abcdef"; - - assert_eq!(Vec::::from_hex(oddlen), Err(Error::OddLengthString(17))); - assert_eq!(<[u8; 4]>::from_hex(oddlen), Err(Error::OddLengthString(17))); - assert_eq!(<[u8; 8]>::from_hex(oddlen), Err(Error::OddLengthString(17))); - assert_eq!(Vec::::from_hex(badchar1), Err(Error::InvalidChar(b'Z'))); - assert_eq!(Vec::::from_hex(badchar2), Err(Error::InvalidChar(b'Y'))); - assert_eq!(Vec::::from_hex(badchar3), Err(Error::InvalidChar(194))); - } -} diff --git a/hashes/src/impls.rs b/hashes/src/impls.rs index 5ff0179d..088a7f1f 100644 --- a/hashes/src/impls.rs +++ b/hashes/src/impls.rs @@ -5,13 +5,7 @@ //! Implementations of traits defined in `std` / `core2` and not in `core`. //! -#[cfg(feature = "std")] -use std::io; - -#[cfg(not(feature = "std"))] -use core2::io; - -use crate::{hmac, ripemd160, sha1, sha256, sha512, siphash24, HashEngine}; +use crate::{hmac, io, ripemd160, sha1, sha256, sha512, siphash24, HashEngine}; impl io::Write for sha1::HashEngine { fn flush(&mut self) -> io::Result<()> { Ok(()) } diff --git a/hashes/src/internal_macros.rs b/hashes/src/internal_macros.rs index 13ef0be1..32a52dbf 100644 --- a/hashes/src/internal_macros.rs +++ b/hashes/src/internal_macros.rs @@ -9,11 +9,11 @@ macro_rules! arr_newtype_fmt_impl { fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result { #[allow(unused)] use crate::Hash as _; - let case = internals::hex::Case::Lower; + let case = $crate::hex::Case::Lower; if <$ty<$($gen),*>>::DISPLAY_BACKWARD { - internals::hex::display::fmt_hex_exact!(f, $bytes, self.0.iter().rev(), case) + $crate::hex::fmt_hex_exact!(f, $bytes, self.0.iter().rev(), case) } else { - internals::hex::display::fmt_hex_exact!(f, $bytes, self.0.iter(), case) + $crate::hex::fmt_hex_exact!(f, $bytes, self.0.iter(), case) } } } @@ -23,11 +23,11 @@ macro_rules! arr_newtype_fmt_impl { fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result { #[allow(unused)] use crate::Hash as _; - let case = internals::hex::Case::Upper; + let case = $crate::hex::Case::Upper; if <$ty<$($gen),*>>::DISPLAY_BACKWARD { - internals::hex::display::fmt_hex_exact!(f, $bytes, self.0.iter().rev(), case) + $crate::hex::fmt_hex_exact!(f, $bytes, self.0.iter().rev(), case) } else { - internals::hex::display::fmt_hex_exact!(f, $bytes, self.0.iter(), case) + $crate::hex::fmt_hex_exact!(f, $bytes, self.0.iter(), case) } } } @@ -76,7 +76,7 @@ macro_rules! hash_trait_impls { /// This is mainly intended as an internal method and you shouldn't need it unless /// you're doing something special. pub fn forward_hex(&self) -> impl '_ + core::fmt::LowerHex + core::fmt::UpperHex { - internals::hex::display::DisplayHex::as_hex(&self.0) + $crate::hex::DisplayHex::as_hex(&self.0) } /// Displays hex backwards, regardless of how this type would display it naturally. @@ -84,7 +84,7 @@ macro_rules! hash_trait_impls { /// This is mainly intended as an internal method and you shouldn't need it unless /// you're doing something special. pub fn backward_hex(&self) -> impl '_ + core::fmt::LowerHex + core::fmt::UpperHex { - internals::hex::display::DisplayArray::<_, [u8; $bits / 8 * 2]>::new(self.0.iter().rev()) + $crate::hex::display::DisplayArray::<_, [u8; $bits / 8 * 2]>::new(self.0.iter().rev()) } /// Zero cost conversion between a fixed length byte array shared reference and @@ -103,15 +103,15 @@ macro_rules! hash_trait_impls { } impl<$($gen: $gent),*> str::FromStr for Hash<$($gen),*> { - type Err = $crate::hex::Error; + type Err = $crate::hex::HexToArrayError; fn from_str(s: &str) -> Result { - use $crate::hex::{FromHex, HexIterator}; + use $crate::hex::{FromHex, HexToBytesIter}; use $crate::Hash; let inner: [u8; $bits / 8] = if $reverse { - FromHex::from_byte_iter(HexIterator::new(s)?.rev())? + FromHex::from_byte_iter(HexToBytesIter::new(s)?.rev())? } else { - FromHex::from_byte_iter(HexIterator::new(s)?)? + FromHex::from_byte_iter(HexToBytesIter::new(s)?)? }; Ok(Self::from_byte_array(inner)) } diff --git a/hashes/src/lib.rs b/hashes/src/lib.rs index b54feb2d..fa92397a 100644 --- a/hashes/src/lib.rs +++ b/hashes/src/lib.rs @@ -79,12 +79,12 @@ // Instead of littering the codebase for non-fuzzing code just globally allow. #![cfg_attr(hashes_fuzz, allow(dead_code, unused_imports))] +#[cfg(all(not(test), not(feature = "std"), feature = "core2"))] +extern crate actual_core2 as core2; #[cfg(all(feature = "alloc", not(feature = "std")))] extern crate alloc; #[cfg(any(test, feature = "std"))] extern crate core; -#[cfg(feature = "core2")] -extern crate core2; #[cfg(feature = "serde")] /// A generic serialization/deserialization framework. @@ -95,6 +95,9 @@ extern crate serde_test; #[cfg(bench)] extern crate test; +/// Re-export the `hex-conservative` crate. +pub extern crate hex; + #[doc(hidden)] pub mod _export { /// A re-export of core::* @@ -113,9 +116,8 @@ mod util; pub mod serde_macros; pub mod cmp; pub mod hash160; -pub mod hex; pub mod hmac; -#[cfg(any(feature = "std", feature = "core2"))] +#[cfg(any(test, feature = "std", feature = "core2"))] mod impls; pub mod ripemd160; pub mod sha1; @@ -127,7 +129,12 @@ pub mod sha512_256; pub mod siphash24; use core::{borrow, fmt, hash, ops}; +// You get I/O if you enable "std" or "core2" (as well as during testing). +#[cfg(any(test, feature = "std"))] +use std::io; +#[cfg(all(not(test), not(feature = "std"), feature = "core2"))] +use core2::io; pub use hmac::{Hmac, HmacEngine}; /// A hashing engine which bytes can be serialized into. diff --git a/hashes/src/sha256.rs b/hashes/src/sha256.rs index e34e3646..5bafe883 100644 --- a/hashes/src/sha256.rs +++ b/hashes/src/sha256.rs @@ -125,7 +125,7 @@ impl> Index for Midstate { } impl str::FromStr for Midstate { - type Err = hex::Error; + type Err = hex::HexToArrayError; fn from_str(s: &str) -> Result { hex::FromHex::from_hex(s) } } @@ -174,9 +174,12 @@ impl Midstate { } impl hex::FromHex for Midstate { - fn from_byte_iter(iter: I) -> Result + type Err = hex::HexToArrayError; + fn from_byte_iter(iter: I) -> Result where - I: Iterator> + ExactSizeIterator + DoubleEndedIterator, + I: Iterator> + + ExactSizeIterator + + DoubleEndedIterator, { // DISPLAY_BACKWARD is true Ok(Midstate::from_byte_array(hex::FromHex::from_byte_iter(iter.rev())?)) diff --git a/hashes/src/util.rs b/hashes/src/util.rs index 39eb33b6..b8c23c74 100644 --- a/hashes/src/util.rs +++ b/hashes/src/util.rs @@ -268,15 +268,15 @@ macro_rules! hash_newtype { } impl $crate::_export::_core::str::FromStr for $newtype { - type Err = $crate::hex::Error; + type Err = $crate::hex::HexToArrayError; fn from_str(s: &str) -> $crate::_export::_core::result::Result<$newtype, Self::Err> { - use $crate::hex::{HexIterator, FromHex}; + use $crate::hex::{FromHex, HexToBytesIter}; use $crate::Hash; let inner: <$hash as Hash>::Bytes = if ::DISPLAY_BACKWARD { - FromHex::from_byte_iter(HexIterator::new(s)?.rev())? + FromHex::from_byte_iter(HexToBytesIter::new(s)?.rev())? } else { - FromHex::from_byte_iter(HexIterator::new(s)?)? + FromHex::from_byte_iter(HexToBytesIter::new(s)?)? }; Ok($newtype(<$hash>::from_byte_array(inner))) } diff --git a/internals/src/hex/buf_encoder.rs b/internals/src/hex/buf_encoder.rs deleted file mode 100644 index 2d6793d9..00000000 --- a/internals/src/hex/buf_encoder.rs +++ /dev/null @@ -1,396 +0,0 @@ -// SPDX-License-Identifier: CC0-1.0 - -//! Implements a buffered encoder. -//! -//! The main type of this module is [`BufEncoder`] which provides buffered hex encoding. Such is -//! faster than the usual `write!(f, "{02x}", b)?` in a for loop because it reduces dynamic -//! dispatch and decreases the number of allocations if a `String` is being created. - -use core::borrow::Borrow; - -pub use out_bytes::OutBytes; - -use super::Case; - -/// Trait for types that can be soundly converted to `OutBytes`. -/// -/// To protect the API from future breakage this sealed trait guards which types can be used with -/// the `Encoder`. Currently it is implemented for byte arrays of various interesting lengths. -/// -/// ## Safety -/// -/// This is not `unsafe` yet but the `as_out_bytes` should always return the same reference if the -/// same reference is supplied. IOW the returned memory address and length should be the same if -/// the input memory address and length are the same. -/// -/// If the trait ever becomes `unsafe` this will be required for soundness. -pub trait AsOutBytes: out_bytes::Sealed { - /// Performs the conversion. - fn as_out_bytes(&self) -> &OutBytes; - - /// Performs the conversion. - fn as_mut_out_bytes(&mut self) -> &mut OutBytes; -} - -/// A buffer with compile-time-known length. -/// -/// This is essentially `Default + AsOutBytes` but supports lengths 1.41 doesn't. -pub trait FixedLenBuf: Sized + AsOutBytes { - /// Creates an uninitialized buffer. - /// - /// The current implementtions initialize the buffer with zeroes but it should be treated a - /// uninitialized anyway. - fn uninit() -> Self; -} - -/// Implements `OutBytes` -/// -/// This prevents the rest of the crate from accessing the field of `OutBytes`. -mod out_bytes { - use super::AsOutBytes; - - /// A byte buffer that can only be written-into. - /// - /// You shouldn't concern yourself with this, just call `BufEncoder::new` with your array. - /// - /// This prepares the API for potential future support of `[MaybeUninit]`. We don't want to use - /// `unsafe` until it's proven to be needed but if it does we have an easy, compatible upgrade - /// option. - /// - /// Warning: `repr(transparent)` is an internal implementation detail and **must not** be - /// relied on! - #[repr(transparent)] - pub struct OutBytes([u8]); - - impl OutBytes { - /// Returns the first `len` bytes as initialized. - /// - /// Not `unsafe` because we don't use `unsafe` (yet). - /// - /// ## Panics - /// - /// The method panics if `len` is out of bounds. - #[track_caller] - pub(crate) fn assume_init(&self, len: usize) -> &[u8] { &self.0[..len] } - - /// Writes given bytes into the buffer. - /// - /// ## Panics - /// - /// The method panics if pos is out of bounds or `bytes` don't fit into the buffer. - #[track_caller] - pub(crate) fn write(&mut self, pos: usize, bytes: &[u8]) { - self.0[pos..(pos + bytes.len())].copy_from_slice(bytes); - } - - /// Returns the length of the buffer. - pub(crate) fn len(&self) -> usize { self.0.len() } - - fn from_bytes(slice: &[u8]) -> &Self { - // SAFETY: copied from std - // conversion of reference to pointer of the same referred type is always sound, - // including in unsized types. - // Thanks to repr(transparent) the types have the same layout making the other - // conversion sound. - // The pointer was just created from a reference that's still alive so dereferencing is - // sound. - unsafe { &*(slice as *const [u8] as *const Self) } - } - - fn from_mut_bytes(slice: &mut [u8]) -> &mut Self { - // SAFETY: copied from std - // conversion of reference to pointer of the same referred type is always sound, - // including in unsized types. - // Thanks to repr(transparent) the types have the same layout making the other - // conversion sound. - // The pointer was just created from a reference that's still alive so dereferencing is - // sound. - unsafe { &mut *(slice as *mut [u8] as *mut Self) } - } - } - - macro_rules! impl_from_array { - ($($len:expr),* $(,)?) => { - $( - impl super::FixedLenBuf for [u8; $len] { - fn uninit() -> Self { - [0u8; $len] - } - } - - impl AsOutBytes for [u8; $len] { - fn as_out_bytes(&self) -> &OutBytes { - OutBytes::from_bytes(self) - } - - fn as_mut_out_bytes(&mut self) -> &mut OutBytes { - OutBytes::from_mut_bytes(self) - } - } - - impl Sealed for [u8; $len] {} - - impl<'a> super::super::display::DisplayHex for &'a [u8; $len / 2] { - type Display = super::super::display::DisplayArray, [u8; $len]>; - fn as_hex(self) -> Self::Display { - super::super::display::DisplayArray::new(self.iter()) - } - - fn hex_reserve_suggestion(self) -> usize { - $len - } - } - )* - } - } - - impl AsOutBytes for &'_ mut T { - fn as_out_bytes(&self) -> &OutBytes { (**self).as_out_bytes() } - - fn as_mut_out_bytes(&mut self) -> &mut OutBytes { (**self).as_mut_out_bytes() } - } - - impl Sealed for &'_ mut T {} - - impl AsOutBytes for OutBytes { - fn as_out_bytes(&self) -> &OutBytes { self } - - fn as_mut_out_bytes(&mut self) -> &mut OutBytes { self } - } - - impl Sealed for OutBytes {} - - // As a sanity check we only provide conversions for even, non-empty arrays. - // Weird lengths 66 and 130 are provided for serialized public keys. - impl_from_array!( - 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 40, 64, 66, 128, 130, 256, 512, - 1024, 2048, 4096, 8192 - ); - - /// Prevents outside crates from implementing the trait - pub trait Sealed {} -} - -/// Hex-encodes bytes into the provided buffer. -/// -/// This is an important building block for fast hex-encoding. Because string writing tools -/// provided by `core::fmt` involve dynamic dispatch and don't allow reserving capacity in strings -/// buffering the hex and then formatting it is significantly faster. -pub struct BufEncoder { - buf: T, - pos: usize, -} - -impl BufEncoder { - /// Creates an empty `BufEncoder`. - /// - /// This is usually used with uninitialized (zeroed) byte array allocated on stack. - /// This can only be constructed with an even-length, non-empty array. - #[inline] - pub fn new(buf: T) -> Self { BufEncoder { buf, pos: 0 } } - - /// Encodes `byte` as hex in given `case` and appends it to the buffer. - /// - /// ## Panics - /// - /// The method panics if the buffer is full. - #[inline] - #[track_caller] - pub fn put_byte(&mut self, byte: u8, case: Case) { - self.buf.as_mut_out_bytes().write(self.pos, &super::byte_to_hex(byte, case.table())); - self.pos += 2; - } - - /// Encodes `bytes` as hex in given `case` and appends them to the buffer. - /// - /// ## Panics - /// - /// The method panics if the bytes wouldn't fit the buffer. - #[inline] - #[track_caller] - pub fn put_bytes(&mut self, bytes: I, case: Case) - where - I: IntoIterator, - I::Item: Borrow, - { - self.put_bytes_inner(bytes.into_iter(), case) - } - - #[inline] - #[track_caller] - fn put_bytes_inner(&mut self, bytes: I, case: Case) - where - I: Iterator, - I::Item: Borrow, - { - // May give the compiler better optimization opportunity - if let Some(max) = bytes.size_hint().1 { - assert!(max <= self.space_remaining()); - } - for byte in bytes { - self.put_byte(*byte.borrow(), case); - } - } - - /// Encodes as many `bytes` as fit into the buffer as hex and return the remainder. - /// - /// This method works just like `put_bytes` but instead of panicking it returns the unwritten - /// bytes. The method returns an empty slice if all bytes were written - #[must_use = "this may write only part of the input buffer"] - #[inline] - #[track_caller] - pub fn put_bytes_min<'a>(&mut self, bytes: &'a [u8], case: Case) -> &'a [u8] { - let to_write = self.space_remaining().min(bytes.len()); - self.put_bytes(&bytes[..to_write], case); - &bytes[to_write..] - } - - /// Returns true if no more bytes can be written into the buffer. - #[inline] - pub fn is_full(&self) -> bool { self.pos == self.buf.as_out_bytes().len() } - - /// Returns the written bytes as a hex `str`. - #[inline] - pub fn as_str(&self) -> &str { - core::str::from_utf8(self.buf.as_out_bytes().assume_init(self.pos)) - .expect("we only write ASCII") - } - - /// Resets the buffer to become empty. - #[inline] - pub fn clear(&mut self) { self.pos = 0; } - - /// How many bytes can be written to this buffer. - /// - /// Note that this returns the number of bytes before encoding, not number of hex digits. - #[inline] - pub fn space_remaining(&self) -> usize { (self.buf.as_out_bytes().len() - self.pos) / 2 } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn empty() { - let mut buf = [0u8; 2]; - let encoder = BufEncoder::new(&mut buf); - assert_eq!(encoder.as_str(), ""); - assert!(!encoder.is_full()); - } - - #[test] - fn single_byte_exact_buf() { - let mut buf = [0u8; 2]; - let mut encoder = BufEncoder::new(&mut buf); - assert_eq!(encoder.space_remaining(), 1); - encoder.put_byte(42, Case::Lower); - assert_eq!(encoder.as_str(), "2a"); - assert_eq!(encoder.space_remaining(), 0); - assert!(encoder.is_full()); - encoder.clear(); - assert_eq!(encoder.space_remaining(), 1); - assert!(!encoder.is_full()); - encoder.put_byte(42, Case::Upper); - assert_eq!(encoder.as_str(), "2A"); - assert_eq!(encoder.space_remaining(), 0); - assert!(encoder.is_full()); - } - - #[test] - fn single_byte_oversized_buf() { - let mut buf = [0u8; 4]; - let mut encoder = BufEncoder::new(&mut buf); - assert_eq!(encoder.space_remaining(), 2); - encoder.put_byte(42, Case::Lower); - assert_eq!(encoder.space_remaining(), 1); - assert_eq!(encoder.as_str(), "2a"); - assert!(!encoder.is_full()); - encoder.clear(); - assert_eq!(encoder.space_remaining(), 2); - encoder.put_byte(42, Case::Upper); - assert_eq!(encoder.as_str(), "2A"); - assert_eq!(encoder.space_remaining(), 1); - assert!(!encoder.is_full()); - } - - #[test] - fn two_bytes() { - let mut buf = [0u8; 4]; - let mut encoder = BufEncoder::new(&mut buf); - encoder.put_byte(42, Case::Lower); - assert_eq!(encoder.space_remaining(), 1); - encoder.put_byte(255, Case::Lower); - assert_eq!(encoder.space_remaining(), 0); - assert_eq!(encoder.as_str(), "2aff"); - assert!(encoder.is_full()); - encoder.clear(); - assert!(!encoder.is_full()); - encoder.put_byte(42, Case::Upper); - encoder.put_byte(255, Case::Upper); - assert_eq!(encoder.as_str(), "2AFF"); - assert!(encoder.is_full()); - } - - #[test] - fn put_bytes_min() { - let mut buf = [0u8; 2]; - let mut encoder = BufEncoder::new(&mut buf); - let remainder = encoder.put_bytes_min(b"", Case::Lower); - assert_eq!(remainder, b""); - assert_eq!(encoder.as_str(), ""); - let remainder = encoder.put_bytes_min(b"*", Case::Lower); - assert_eq!(remainder, b""); - assert_eq!(encoder.as_str(), "2a"); - encoder.clear(); - let remainder = encoder.put_bytes_min(&[42, 255], Case::Lower); - assert_eq!(remainder, &[255]); - assert_eq!(encoder.as_str(), "2a"); - } - - #[test] - fn same_as_fmt() { - use core::fmt::{self, Write}; - - struct Writer { - buf: [u8; 2], - pos: usize, - } - - impl Writer { - fn as_str(&self) -> &str { core::str::from_utf8(&self.buf[..self.pos]).unwrap() } - } - - impl Write for Writer { - fn write_str(&mut self, s: &str) -> fmt::Result { - assert!(self.pos <= 2); - if s.len() > 2 - self.pos { - Err(fmt::Error) - } else { - self.buf[self.pos..(self.pos + s.len())].copy_from_slice(s.as_bytes()); - self.pos += s.len(); - Ok(()) - } - } - } - - let mut writer = Writer { buf: [0u8; 2], pos: 0 }; - let mut buf = [0u8; 2]; - let mut encoder = BufEncoder::new(&mut buf); - - for i in 0..=255 { - write!(writer, "{:02x}", i).unwrap(); - encoder.put_byte(i, Case::Lower); - assert_eq!(encoder.as_str(), writer.as_str()); - writer.pos = 0; - encoder.clear(); - } - for i in 0..=255 { - write!(writer, "{:02X}", i).unwrap(); - encoder.put_byte(i, Case::Upper); - assert_eq!(encoder.as_str(), writer.as_str()); - writer.pos = 0; - encoder.clear(); - } - } -} diff --git a/internals/src/hex/display.rs b/internals/src/hex/display.rs deleted file mode 100644 index 85da1684..00000000 --- a/internals/src/hex/display.rs +++ /dev/null @@ -1,303 +0,0 @@ -// SPDX-License-Identifier: CC0-1.0 - -//! Helpers for displaying bytes as hex strings. -//! -//! This module provides a trait for displaying things as hex as well as an implementation for -//! `&[u8]`. - -use core::borrow::Borrow; -use core::fmt; - -use super::buf_encoder::{BufEncoder, OutBytes}; -use super::Case; -use crate::hex::buf_encoder::FixedLenBuf; -#[cfg(feature = "alloc")] -use crate::prelude::*; - -/// Extension trait for types that can be displayed as hex. -/// -/// Types that have a single, obvious text representation being hex should **not** implement this -/// trait and simply implement `Display` instead. -/// -/// This trait should be generally implemented for references only. We would prefer to use GAT but -/// that is beyond our MSRV. As a lint we require the `IsRef` trait which is implemented for all -/// references. -pub trait DisplayHex: Copy + sealed::IsRef { - /// The type providing [`fmt::Display`] implementation. - /// - /// This is usually a wrapper type holding a reference to `Self`. - type Display: fmt::LowerHex + fmt::UpperHex; - - /// Display `Self` as a continuous sequence of ASCII hex chars. - fn as_hex(self) -> Self::Display; - - /// Create a lower-hex-encoded string. - /// - /// A shorthand for `to_hex_string(Case::Lower)`, so that `Case` doesn't need to be imported. - /// - /// This may be faster than `.display_hex().to_string()` because it uses `reserve_suggestion`. - #[cfg(feature = "alloc")] - fn to_lower_hex_string(self) -> String { self.to_hex_string(Case::Lower) } - - /// Create an upper-hex-encoded string. - /// - /// A shorthand for `to_hex_string(Case::Upper)`, so that `Case` doesn't need to be imported. - /// - /// This may be faster than `.display_hex().to_string()` because it uses `reserve_suggestion`. - #[cfg(feature = "alloc")] - fn to_upper_hex_string(self) -> String { self.to_hex_string(Case::Upper) } - - /// Create a hex-encoded string. - /// - /// This may be faster than `.display_hex().to_string()` because it uses `reserve_suggestion`. - #[cfg(feature = "alloc")] - fn to_hex_string(self, case: Case) -> String { - let mut string = String::new(); - self.append_hex_to_string(case, &mut string); - string - } - - /// Appends hex-encoded content to an existing `String`. - /// - /// This may be faster than `write!(string, "{:x}", self.display_hex())` because it uses - /// `reserve_sugggestion`. - #[cfg(feature = "alloc")] - fn append_hex_to_string(self, case: Case, string: &mut String) { - use fmt::Write; - - string.reserve(self.hex_reserve_suggestion()); - match case { - Case::Lower => write!(string, "{:x}", self.as_hex()), - Case::Upper => write!(string, "{:X}", self.as_hex()), - } - .unwrap_or_else(|_| { - let name = core::any::type_name::(); - // We don't expect `std` to ever be buggy, so the bug is most likely in the `Display` - // impl of `Self::Display`. - panic!("The implementation of Display for {} returned an error when it shouldn't", name) - }) - } - - /// Hints how much bytes to reserve when creating a `String`. - /// - /// Implementors that know the number of produced bytes upfront should override this. - /// Defaults to 0. - /// - // We prefix the name with `hex_` to avoid potential collision with other methods. - fn hex_reserve_suggestion(self) -> usize { 0 } -} - -mod sealed { - /// Trait marking a shared reference. - pub trait IsRef: Copy {} - - impl IsRef for &'_ T {} -} - -impl<'a> DisplayHex for &'a [u8] { - type Display = DisplayByteSlice<'a>; - - #[inline] - fn as_hex(self) -> Self::Display { DisplayByteSlice { bytes: self } } - - #[inline] - fn hex_reserve_suggestion(self) -> usize { - // Since the string wouldn't fit into address space if this overflows (actually even for - // smaller amounts) it's better to panic right away. It should also give the optimizer - // better opportunities. - self.len().checked_mul(2).expect("the string wouldn't fit into address space") - } -} - -#[cfg(feature = "alloc")] -impl<'a> DisplayHex for &'a alloc::vec::Vec { - type Display = DisplayByteSlice<'a>; - - #[inline] - fn as_hex(self) -> Self::Display { DisplayByteSlice { bytes: self } } - - #[inline] - fn hex_reserve_suggestion(self) -> usize { - // Since the string wouldn't fit into address space if this overflows (actually even for - // smaller amounts) it's better to panic right away. It should also give the optimizer - // better opportunities. - self.len().checked_mul(2).expect("the string wouldn't fit into address space") - } -} - -/// Displays byte slice as hex. -/// -/// Created by [`<&[u8] as DisplayHex>::as_hex`](DisplayHex::as_hex). -pub struct DisplayByteSlice<'a> { - // pub because we want to keep lengths in sync - pub(crate) bytes: &'a [u8], -} - -impl<'a> DisplayByteSlice<'a> { - fn display(&self, f: &mut fmt::Formatter, case: Case) -> fmt::Result { - let mut buf = [0u8; 1024]; - let mut encoder = super::BufEncoder::new(&mut buf); - - let mut chunks = self.bytes.chunks_exact(512); - for chunk in &mut chunks { - encoder.put_bytes(chunk, case); - f.write_str(encoder.as_str())?; - encoder.clear(); - } - encoder.put_bytes(chunks.remainder(), case); - f.write_str(encoder.as_str()) - } -} - -impl<'a> fmt::LowerHex for DisplayByteSlice<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Lower) } -} - -impl<'a> fmt::UpperHex for DisplayByteSlice<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Upper) } -} - -/// Displays byte array as hex. -/// -/// Created by [`<&[u8; LEN] as DisplayHex>::as_hex`](DisplayHex::as_hex). -pub struct DisplayArray -where - A::Item: Borrow, -{ - array: A, - _buffer_marker: core::marker::PhantomData, -} - -impl DisplayArray -where - A::Item: Borrow, -{ - /// Creates the wrapper. - pub fn new(array: A) -> Self { DisplayArray { array, _buffer_marker: Default::default() } } - - fn display(&self, f: &mut fmt::Formatter, case: Case) -> fmt::Result { - let mut buf = B::uninit(); - let mut encoder = super::BufEncoder::new(&mut buf); - encoder.put_bytes(self.array.clone(), case); - f.pad_integral(true, "0x", encoder.as_str()) - } -} - -impl fmt::LowerHex for DisplayArray -where - A::Item: Borrow, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Lower) } -} - -impl fmt::UpperHex for DisplayArray -where - A::Item: Borrow, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Upper) } -} - -/// Format known-length array as hex. -/// -/// This supports all formatting options of formatter and may be faster than calling -/// `display_as_hex()` on an arbitrary `&[u8]`. Note that the implementation intentionally keeps -/// leading zeros even when not requested. This is designed to display values such as hashes and -/// keys and removing leading zeros would be confusing. -/// -/// ## Parameters -/// -/// * `$formatter` - a [`fmt::Formatter`]. -/// * `$len` known length of `$bytes`, must be a const expression. -/// * `$bytes` - bytes to be encoded, most likely a reference to an array. -/// * `$case` - value of type [`Case`] determining whether to format as lower or upper case. -/// -/// ## Panics -/// -/// This macro panics if `$len` is not equal to `$bytes.len()`. It also fails to compile if `$len` -/// is more than half of `usize::MAX`. -#[macro_export] -macro_rules! fmt_hex_exact { - ($formatter:expr, $len:expr, $bytes:expr, $case:expr) => {{ - // statically check $len - #[allow(deprecated)] - const _: () = [()][($len > usize::MAX / 2) as usize]; - assert_eq!($bytes.len(), $len); - let mut buf = [0u8; $len * 2]; - let buf = $crate::hex::buf_encoder::AsOutBytes::as_mut_out_bytes(&mut buf); - $crate::hex::display::fmt_hex_exact_fn($formatter, buf, $bytes, $case) - }}; -} -pub use fmt_hex_exact; - -// Implementation detail of `write_hex_exact` macro to de-duplicate the code -#[doc(hidden)] -#[inline] -pub fn fmt_hex_exact_fn( - f: &mut fmt::Formatter, - buf: &mut OutBytes, - bytes: I, - case: Case, -) -> fmt::Result -where - I: IntoIterator, - I::Item: Borrow, -{ - let mut encoder = BufEncoder::new(buf); - encoder.put_bytes(bytes, case); - f.pad_integral(true, "0x", encoder.as_str()) -} - -#[cfg(test)] -mod tests { - #[cfg(feature = "alloc")] - use super::*; - - #[cfg(feature = "alloc")] - mod alloc { - use super::*; - - fn check_encoding(bytes: &[u8]) { - use core::fmt::Write; - - let s1 = bytes.to_lower_hex_string(); - let mut s2 = String::with_capacity(bytes.len() * 2); - for b in bytes { - write!(s2, "{:02x}", b).unwrap(); - } - assert_eq!(s1, s2); - } - - #[test] - fn empty() { check_encoding(b""); } - - #[test] - fn single() { check_encoding(b"*"); } - - #[test] - fn two() { check_encoding(b"*x"); } - - #[test] - fn just_below_boundary() { check_encoding(&[42; 512]); } - - #[test] - fn just_above_boundary() { check_encoding(&[42; 513]); } - - #[test] - fn just_above_double_boundary() { check_encoding(&[42; 1025]); } - - #[test] - fn fmt_exact_macro() { - use crate::alloc::string::ToString; - - struct Dummy([u8; 32]); - - impl fmt::Display for Dummy { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt_hex_exact!(f, 32, &self.0, Case::Lower) - } - } - - assert_eq!(Dummy([42; 32]).to_string(), "2a".repeat(32)); - } - } -} diff --git a/internals/src/hex/mod.rs b/internals/src/hex/mod.rs deleted file mode 100644 index 54fd0465..00000000 --- a/internals/src/hex/mod.rs +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: CC0-1.0 - -//! Helpers for encoding bytes as hex strings. - -pub mod buf_encoder; -pub mod display; - -pub use buf_encoder::BufEncoder; - -/// Reexports of extension traits. -pub mod exts { - pub use super::display::DisplayHex; -} - -/// Possible case of hex. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum Case { - /// Produce lower-case chars (`[0-9a-f]`). - /// - /// This is the default. - Lower, - - /// Produce upper-case chars (`[0-9A-F]`). - Upper, -} - -impl Default for Case { - fn default() -> Self { Case::Lower } -} - -impl Case { - /// Returns the encoding table. - /// - /// The returned table may only contain displayable ASCII chars. - #[inline] - #[rustfmt::skip] - pub(crate) fn table(self) -> &'static [u8; 16] { - static LOWER: [u8; 16] = [b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b', b'c', b'd', b'e', b'f']; - static UPPER: [u8; 16] = [b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', b'E', b'F']; - - match self { - Case::Lower => &LOWER, - Case::Upper => &UPPER, - } - } -} - -/// Encodes single byte as two ASCII chars using the given table. -/// -/// The function guarantees only returning values from the provided table. -#[inline] -pub(crate) fn byte_to_hex(byte: u8, table: &[u8; 16]) -> [u8; 2] { - [table[usize::from(byte.wrapping_shr(4))], table[usize::from(byte & 0x0F)]] -} diff --git a/internals/src/lib.rs b/internals/src/lib.rs index f85e0b8f..a024e273 100644 --- a/internals/src/lib.rs +++ b/internals/src/lib.rs @@ -19,13 +19,6 @@ extern crate alloc; extern crate std; pub mod error; -pub mod hex; pub mod macros; mod parse; pub mod serde; - -/// Mainly reexports based on features. -pub(crate) mod prelude { - #[cfg(feature = "alloc")] - pub(crate) use alloc::string::String; -}