Merge rust-bitcoin/rust-bitcoin#1833: Use new `hex-conservative` crate

bb8bd16302 internals: Remove hex module (Tobin C. Harding)
2268b44911 Depend on hex-conservative (Tobin C. Harding)
db50509cd3 Add usage docs to the "core2" feature (Tobin C. Harding)

Pull request description:

  Use the newly released `hex-conservative` crate, by doing the following:

  - Depend on `hex-conservative` in `bitcoin` and `hashes`
  - Re-export `hex-conservative` as `hex` from both crate roots.
  - Remove all the old hex code from `hashes`
  - Remove all the old hex code from `internals`
  - Remove the now unused `internals::prelude`
  - Fix all the import statements (makes up the bulk of the lines changes in this patch)

ACKs for top commit:
  apoelstra:
    ACK bb8bd16302
  sanket1729:
    utACK bb8bd16302

Tree-SHA512: ec83b3941cae6f32272471779f28461bb04959a3f6a126a68bbf2c748d83ff9518ff8932d9e937a6f389c10028bf3eb58c6b6d71ea066924dd7a34faaec7a087
This commit is contained in:
Andrew Poelstra 2023-07-27 16:21:28 +00:00
commit 04976eddcf
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
38 changed files with 151 additions and 1112 deletions

View File

@ -41,6 +41,7 @@ dependencies = [
"bitcoin_hashes", "bitcoin_hashes",
"bitcoinconsensus", "bitcoinconsensus",
"core2", "core2",
"hex-conservative",
"hex_lit", "hex_lit",
"mutagen", "mutagen",
"secp256k1", "secp256k1",
@ -74,6 +75,7 @@ version = "0.12.0"
dependencies = [ dependencies = [
"bitcoin-internals", "bitcoin-internals",
"core2", "core2",
"hex-conservative",
"schemars", "schemars",
"serde", "serde",
"serde_json", "serde_json",
@ -163,6 +165,15 @@ version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" 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]] [[package]]
name = "hex_lit" name = "hex_lit"
version = "0.1.1" version = "0.1.1"

View File

@ -40,6 +40,7 @@ dependencies = [
"bitcoin_hashes", "bitcoin_hashes",
"bitcoinconsensus", "bitcoinconsensus",
"core2", "core2",
"hex-conservative",
"hex_lit", "hex_lit",
"mutagen", "mutagen",
"secp256k1", "secp256k1",
@ -73,6 +74,7 @@ version = "0.12.0"
dependencies = [ dependencies = [
"bitcoin-internals", "bitcoin-internals",
"core2", "core2",
"hex-conservative",
"schemars", "schemars",
"serde", "serde",
"serde_json", "serde_json",
@ -156,6 +158,15 @@ version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" 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]] [[package]]
name = "hex_lit" name = "hex_lit"
version = "0.1.1" version = "0.1.1"

View File

@ -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. # 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. # Instead no-std enables additional features required for this crate to be usable without std.
# As a result, both can be enabled without conflict. # As a result, both can be enabled without conflict.
std = ["secp256k1/std", "hashes/std", "bech32/std", "internals/std"] std = ["secp256k1/std", "hashes/std", "bech32/std", "internals/std", "hex/std"]
no-std = ["core2", "hashes/alloc", "hashes/core2", "secp256k1/alloc"] no-std = ["core2", "hashes/alloc", "hashes/core2", "secp256k1/alloc", "hex/alloc", "hex/core2"]
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true
@ -35,6 +35,7 @@ rustdoc-args = ["--cfg", "docsrs"]
[dependencies] [dependencies]
internals = { package = "bitcoin-internals", version = "0.2.0" } 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 } bech32 = { version = "0.9.0", default-features = false }
hashes = { package = "bitcoin_hashes", version = "0.12.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"] } 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 } base64 = { version = "0.13.0", optional = true }
# Only use this feature for no-std builds, otherwise use bitcoinconsensus-std. # 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 } 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 } core2 = { version = "0.3.2", default-features = false, features = ["alloc"], optional = true }
# Do NOT use this as a feature! Use the `serde` feature instead. # 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 } actual-serde = { package = "serde", version = "1.0.103", default-features = false, features = [ "derive", "alloc" ], optional = true }

View File

@ -5,7 +5,7 @@ use std::{env, process};
use bitcoin::address::Address; use bitcoin::address::Address;
use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey}; 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::ffi::types::AlignedType;
use bitcoin::secp256k1::Secp256k1; use bitcoin::secp256k1::Secp256k1;
use bitcoin::PublicKey; use bitcoin::PublicKey;

View File

@ -371,7 +371,7 @@ impl BlockTransactions {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use hashes::hex::FromHex; use hex::FromHex;
use super::*; use super::*;
use crate::blockdata::locktime::absolute; use crate::blockdata::locktime::absolute;

View File

@ -12,7 +12,7 @@ use core::ops::Index;
use core::str::FromStr; use core::str::FromStr;
use core::{fmt, slice}; 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 internals::{impl_array_newtype, write_err};
use secp256k1::{self, Secp256k1, XOnlyPublicKey}; use secp256k1::{self, Secp256k1, XOnlyPublicKey};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
@ -468,7 +468,7 @@ pub enum Error {
/// Base58 encoding error /// Base58 encoding error
Base58(base58::Error), Base58(base58::Error),
/// Hexadecimal decoding error /// Hexadecimal decoding error
Hex(hex::Error), Hex(hex::HexToArrayError),
/// `PublicKey` hex should be 66 or 130 digits long. /// `PublicKey` hex should be 66 or 130 digits long.
InvalidPublicKeyHexLength(usize), InvalidPublicKeyHexLength(usize),
} }

View File

@ -423,7 +423,7 @@ impl std::error::Error for ValidationError {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use hashes::hex::FromHex; use hex::FromHex;
use super::*; use super::*;
use crate::consensus::encode::{deserialize, serialize}; use crate::consensus::encode::{deserialize, serialize};

View File

@ -513,7 +513,7 @@ impl<'de> serde::Deserialize<'de> for ScriptBuf {
{ {
use core::fmt::Formatter; use core::fmt::Formatter;
use hashes::hex::FromHex; use hex::FromHex;
if deserializer.is_human_readable() { if deserializer.is_human_readable() {
struct Visitor; struct Visitor;

View File

@ -3,7 +3,6 @@
#[cfg(doc)] #[cfg(doc)]
use core::ops::Deref; use core::ops::Deref;
use hashes::hex;
use secp256k1::{Secp256k1, Verification}; use secp256k1::{Secp256k1, Verification};
use crate::blockdata::opcodes::all::*; use crate::blockdata::opcodes::all::*;
@ -156,8 +155,8 @@ impl ScriptBuf {
} }
/// Creates a [`ScriptBuf`] from a hex string. /// Creates a [`ScriptBuf`] from a hex string.
pub fn from_hex(s: &str) -> Result<Self, hex::Error> { pub fn from_hex(s: &str) -> Result<Self, hex::HexToBytesError> {
use hashes::hex::FromHex; use hex::FromHex;
let v = Vec::from_hex(s)?; let v = Vec::from_hex(s)?;
Ok(ScriptBuf::from_bytes(v)) Ok(ScriptBuf::from_bytes(v))

View File

@ -95,7 +95,7 @@ impl fmt::Display for OutPoint {
#[non_exhaustive] #[non_exhaustive]
pub enum ParseOutPointError { pub enum ParseOutPointError {
/// Error in TXID part. /// Error in TXID part.
Txid(hashes::hex::Error), Txid(hex::HexToArrayError),
/// Error in vout part. /// Error in vout part.
Vout(crate::error::ParseIntError), Vout(crate::error::ParseIntError),
/// Error in general format. /// Error in general format.
@ -1390,7 +1390,7 @@ impl InputWeightPrediction {
mod tests { mod tests {
use core::str::FromStr; use core::str::FromStr;
use hashes::hex::FromHex; use hex::FromHex;
use super::*; use super::*;
use crate::blockdata::constants::WITNESS_SCALE_FACTOR; use crate::blockdata::constants::WITNESS_SCALE_FACTOR;

View File

@ -462,8 +462,8 @@ impl<'de> serde::Deserialize<'de> for Witness {
self, self,
mut a: A, mut a: A,
) -> Result<Self::Value, A::Error> { ) -> Result<Self::Value, A::Error> {
use hashes::hex::Error::*; use hex::FromHex;
use hashes::hex::FromHex; use hex::HexToBytesError::*;
use serde::de::{self, Unexpected}; use serde::de::{self, Unexpected};
let mut ret = match a.size_hint() { let mut ret = match a.size_hint() {
@ -485,10 +485,6 @@ impl<'de> serde::Deserialize<'de> for Witness {
}, },
OddLengthString(len) => OddLengthString(len) =>
de::Error::invalid_length(len, &"an even length string"), 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); ret.push(vec);
} }

View File

@ -38,7 +38,7 @@ pub mod hex {
use core::fmt; use core::fmt;
use core::marker::PhantomData; use core::marker::PhantomData;
use internals::hex::BufEncoder; use hex::buf_encoder::BufEncoder;
/// Marker for upper/lower case type-level flags ("type-level enum"). /// Marker for upper/lower case type-level flags ("type-level enum").
/// ///
@ -54,15 +54,15 @@ pub mod hex {
mod sealed { mod sealed {
pub trait Case { pub trait Case {
/// Internal detail, don't depend on it!!! /// Internal detail, don't depend on it!!!
const INTERNAL_CASE: internals::hex::Case; const INTERNAL_CASE: hex::Case;
} }
impl Case for super::Lower { 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 { 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. /// Error returned when a hex string decoder can't be created.
#[derive(Debug)] #[derive(Debug)]
pub struct DecodeInitError(hashes::hex::Error); pub struct DecodeInitError(hex::HexToBytesError);
/// Error returned when a hex string contains invalid characters. /// Error returned when a hex string contains invalid characters.
#[derive(Debug)] #[derive(Debug)]
pub struct DecodeError(hashes::hex::Error); pub struct DecodeError(hex::HexToBytesError);
/// Hex decoder state. /// Hex decoder state.
pub struct Decoder<'a>(hashes::hex::HexIterator<'a>); pub struct Decoder<'a>(hex::HexToBytesIter<'a>);
impl<'a> Decoder<'a> { impl<'a> Decoder<'a> {
fn new(s: &'a str) -> Result<Self, DecodeInitError> { fn new(s: &'a str) -> Result<Self, DecodeInitError> {
match hashes::hex::HexIterator::new(s) { match hex::HexToBytesIter::new(s) {
Ok(iter) => Ok(Decoder(iter)), Ok(iter) => Ok(Decoder(iter)),
Err(error) => Err(DecodeInitError(error)), Err(error) => Err(DecodeInitError(error)),
} }
@ -138,10 +138,10 @@ pub mod hex {
impl super::IntoDeError for DecodeInitError { impl super::IntoDeError for DecodeInitError {
fn into_de_error<E: serde::de::Error>(self) -> E { fn into_de_error<E: serde::de::Error>(self) -> E {
use hashes::hex::Error; use hex::HexToBytesError;
match self.0 { match self.0 {
Error::OddLengthString(len) => HexToBytesError::OddLengthString(len) =>
E::invalid_length(len, &"an even number of ASCII-encoded hex digits"), E::invalid_length(len, &"an even number of ASCII-encoded hex digits"),
error => panic!("unexpected error: {:?}", error), error => panic!("unexpected error: {:?}", error),
} }
@ -150,15 +150,15 @@ pub mod hex {
impl super::IntoDeError for DecodeError { impl super::IntoDeError for DecodeError {
fn into_de_error<E: serde::de::Error>(self) -> E { fn into_de_error<E: serde::de::Error>(self) -> E {
use hashes::hex::Error; use hex::HexToBytesError;
use serde::de::Unexpected; use serde::de::Unexpected;
const EXPECTED_CHAR: &str = "an ASCII-encoded hex digit"; const EXPECTED_CHAR: &str = "an ASCII-encoded hex digit";
match self.0 { 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), E::invalid_value(Unexpected::Char(c as _), &EXPECTED_CHAR),
Error::InvalidChar(c) => HexToBytesError::InvalidChar(c) =>
E::invalid_value(Unexpected::Unsigned(c.into()), &EXPECTED_CHAR), E::invalid_value(Unexpected::Unsigned(c.into()), &EXPECTED_CHAR),
error => panic!("unexpected error: {:?}", error), error => panic!("unexpected error: {:?}", error),
} }

View File

@ -7,8 +7,7 @@
use core::str::FromStr; use core::str::FromStr;
use core::{fmt, iter}; use core::{fmt, iter};
use hashes::hex::{self, FromHex}; use hex::FromHex;
use internals::hex::display::DisplayHex;
use internals::write_err; use internals::write_err;
use secp256k1; use secp256k1;
@ -188,8 +187,8 @@ impl<'a> IntoIterator for &'a SerializedSignature {
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[non_exhaustive] #[non_exhaustive]
pub enum Error { pub enum Error {
/// Hex encoding error /// Hex decoding error
HexEncoding(hex::Error), Hex(hex::HexToBytesError),
/// Base58 encoding error /// Base58 encoding error
NonStandardSighashType(u32), NonStandardSighashType(u32),
/// Empty Signature /// Empty Signature
@ -201,7 +200,7 @@ pub enum Error {
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { 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) => Error::NonStandardSighashType(hash_ty) =>
write!(f, "Non standard signature hash type {}", hash_ty), write!(f, "Non standard signature hash type {}", hash_ty),
Error::EmptySignature => write!(f, "Empty ECDSA signature"), Error::EmptySignature => write!(f, "Empty ECDSA signature"),
@ -216,7 +215,7 @@ impl std::error::Error for Error {
use self::Error::*; use self::Error::*;
match self { match self {
HexEncoding(e) => Some(e), Hex(e) => Some(e),
Secp256k1(e) => Some(e), Secp256k1(e) => Some(e),
NonStandardSighashType(_) | EmptySignature => None, NonStandardSighashType(_) | EmptySignature => None,
} }
@ -231,6 +230,6 @@ impl From<NonStandardSighashType> for Error {
fn from(err: NonStandardSighashType) -> Self { Error::NonStandardSighashType(err.0) } fn from(err: NonStandardSighashType) -> Self { Error::NonStandardSighashType(err.0) }
} }
impl From<hex::Error> for Error { impl From<hex::HexToBytesError> for Error {
fn from(err: hex::Error) -> Self { Error::HexEncoding(err) } fn from(err: hex::HexToBytesError) -> Self { Error::Hex(err) }
} }

View File

@ -9,8 +9,8 @@ use core::fmt::{self, Write};
use core::ops; use core::ops;
use core::str::FromStr; use core::str::FromStr;
use hashes::hex::FromHex; use hashes::{hash160, Hash};
use hashes::{hash160, hex, Hash}; use hex::FromHex;
use internals::write_err; use internals::write_err;
#[cfg(feature = "rand-std")] #[cfg(feature = "rand-std")]
pub use secp256k1::rand; pub use secp256k1::rand;
@ -34,7 +34,7 @@ pub enum Error {
/// Invalid key prefix error /// Invalid key prefix error
InvalidKeyPrefix(u8), InvalidKeyPrefix(u8),
/// Hex decoding error /// Hex decoding error
Hex(hex::Error), Hex(hex::HexToArrayError),
/// `PublicKey` hex should be 66 or 130 digits long. /// `PublicKey` hex should be 66 or 130 digits long.
InvalidHexLength(usize), InvalidHexLength(usize),
} }
@ -74,8 +74,8 @@ impl From<secp256k1::Error> for Error {
fn from(e: secp256k1::Error) -> Error { Error::Secp256k1(e) } fn from(e: secp256k1::Error) -> Error { Error::Secp256k1(e) }
} }
impl From<hex::Error> for Error { impl From<hex::HexToArrayError> for Error {
fn from(e: hex::Error) -> Self { Error::Hex(e) } fn from(e: hex::HexToArrayError) -> Self { Error::Hex(e) }
} }
/// A Bitcoin ECDSA public key /// A Bitcoin ECDSA public key
@ -735,7 +735,7 @@ impl From<TweakedKeyPair> for TweakedPublicKey {
mod tests { mod tests {
use std::str::FromStr; use std::str::FromStr;
use hashes::hex::FromHex; use hex::FromHex;
use secp256k1::Secp256k1; use secp256k1::Secp256k1;
use super::*; use super::*;

View File

@ -1110,8 +1110,8 @@ fn is_invalid_use_of_sighash_single(sighash: u32, input_index: usize, output_len
mod tests { mod tests {
use std::str::FromStr; use std::str::FromStr;
use hashes::hex::FromHex;
use hashes::HashEngine; use hashes::HashEngine;
use hex::FromHex;
use super::*; use super::*;
use crate::address::Address; use crate::address::Address;

View File

@ -51,7 +51,7 @@ pub(crate) use test_macros::*;
#[cfg(test)] #[cfg(test)]
mod test_macros { mod test_macros {
macro_rules! hex (($hex:expr) => (<Vec<u8> as hashes::hex::FromHex>::from_hex($hex).unwrap())); macro_rules! hex (($hex:expr) => (<Vec<u8> as $crate::hex::FromHex>::from_hex($hex).unwrap()));
pub(crate) use hex; pub(crate) use hex;
} }
@ -61,7 +61,7 @@ mod test_macros {
/// - core::fmt::UpperHex /// - core::fmt::UpperHex
/// - core::fmt::Display /// - core::fmt::Display
/// - core::str::FromStr /// - core::str::FromStr
/// - hashes::hex::FromHex /// - hex::FromHex
macro_rules! impl_bytes_newtype { macro_rules! impl_bytes_newtype {
($t:ident, $len:literal) => { ($t:ident, $len:literal) => {
impl $t { impl $t {
@ -83,14 +83,14 @@ macro_rules! impl_bytes_newtype {
impl core::fmt::LowerHex for $t { impl core::fmt::LowerHex for $t {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 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) display::fmt_hex_exact!(f, $len, &self.0, Case::Lower)
} }
} }
impl core::fmt::UpperHex for $t { impl core::fmt::UpperHex for $t {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 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) 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 { impl $crate::hex::FromHex for $t {
fn from_byte_iter<I>(iter: I) -> Result<Self, $crate::hashes::hex::Error> type Err = $crate::hex::HexToArrayError;
fn from_byte_iter<I>(iter: I) -> Result<Self, $crate::hex::HexToArrayError>
where where
I: core::iter::Iterator<Item = Result<u8, $crate::hashes::hex::Error>> I: core::iter::Iterator<Item = Result<u8, $crate::hex::HexToBytesError>>
+ core::iter::ExactSizeIterator + core::iter::ExactSizeIterator
+ core::iter::DoubleEndedIterator, + core::iter::DoubleEndedIterator,
{ {
if iter.len() == $len { Ok($t($crate::hex::FromHex::from_byte_iter(iter)?))
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()))
}
} }
} }
impl core::str::FromStr for $t { impl core::str::FromStr for $t {
type Err = $crate::hashes::hex::Error; type Err = $crate::hex::HexToArrayError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> { $crate::hex::FromHex::from_hex(s) }
$crate::hashes::hex::FromHex::from_hex(s)
}
} }
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
@ -164,7 +156,7 @@ macro_rules! impl_bytes_newtype {
use $crate::serde::de::Unexpected; use $crate::serde::de::Unexpected;
if let Ok(hex) = core::str::from_utf8(v) { 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 { } else {
return Err(E::invalid_value(Unexpected::Bytes(v), &self)); return Err(E::invalid_value(Unexpected::Bytes(v), &self));
} }
@ -174,7 +166,7 @@ macro_rules! impl_bytes_newtype {
where where
E: $crate::serde::de::Error, 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)
} }
} }

View File

@ -66,9 +66,15 @@ pub extern crate bech32;
/// Bitcoin's libbitcoinconsensus with Rust binding. /// Bitcoin's libbitcoinconsensus with Rust binding.
pub extern crate bitcoinconsensus; pub extern crate bitcoinconsensus;
#[cfg(not(feature = "std"))]
extern crate core2;
/// Rust implementation of cryptographic hash function algorithems. /// Rust implementation of cryptographic hash function algorithems.
pub extern crate hashes; 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 /// Rust wrapper library for Pieter Wuille's libsecp256k1. Implements ECDSA and BIP 340 signatures
/// for the SECG elliptic curve group secp256k1 and related utilities. /// for the SECG elliptic curve group secp256k1 and related utilities.
pub extern crate secp256k1; pub extern crate secp256k1;
@ -186,7 +192,7 @@ mod prelude {
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
pub use crate::io_extras::sink; pub use crate::io_extras::sink;
pub use internals::hex::display::DisplayHex; pub use hex::DisplayHex;
} }
#[cfg(bench)] #[cfg(bench)]

View File

@ -13,7 +13,7 @@
//! //!
//! ```rust //! ```rust
//! use bitcoin::hash_types::Txid; //! use bitcoin::hash_types::Txid;
//! use bitcoin::hashes::hex::FromHex; //! use bitcoin::hex::FromHex;
//! use bitcoin::{Block, MerkleBlock}; //! use bitcoin::{Block, MerkleBlock};
//! //!
//! // Get the proof from a bitcoind by running in the terminal: //! // Get the proof from a bitcoind by running in the terminal:
@ -73,7 +73,7 @@ impl MerkleBlock {
/// ///
/// ```rust /// ```rust
/// use bitcoin::hash_types::Txid; /// use bitcoin::hash_types::Txid;
/// use bitcoin::hashes::hex::FromHex; /// use bitcoin::hex::FromHex;
/// use bitcoin::{Block, MerkleBlock}; /// use bitcoin::{Block, MerkleBlock};
/// ///
/// // Block 80000 /// // Block 80000
@ -223,7 +223,7 @@ impl PartialMerkleTree {
/// ///
/// ```rust /// ```rust
/// use bitcoin::hash_types::Txid; /// use bitcoin::hash_types::Txid;
/// use bitcoin::hashes::hex::FromHex; /// use bitcoin::hex::FromHex;
/// use bitcoin::merkle_tree::{MerkleBlock, PartialMerkleTree}; /// use bitcoin::merkle_tree::{MerkleBlock, PartialMerkleTree};
/// ///
/// // Block 80000 /// // Block 80000
@ -751,7 +751,7 @@ mod tests {
/// Returns a real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af) /// Returns a real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af)
/// with 9 txs. /// with 9 txs.
fn get_block_13b8a() -> Block { fn get_block_13b8a() -> Block {
use hashes::hex::FromHex; use hex::FromHex;
let block_hex = include_str!("../../tests/data/block_13b8a.hex"); let block_hex = include_str!("../../tests/data/block_13b8a.hex");
deserialize(&Vec::from_hex(block_hex).unwrap()).unwrap() deserialize(&Vec::from_hex(block_hex).unwrap()).unwrap()
} }

View File

@ -306,7 +306,7 @@ mod test {
use core::str::FromStr; use core::str::FromStr;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use hashes::hex::FromHex; use hex::FromHex;
use super::{AddrV2, AddrV2Message, Address}; use super::{AddrV2, AddrV2Message, Address};
use crate::consensus::encode::{deserialize, serialize}; use crate::consensus::encode::{deserialize, serialize};

View File

@ -31,7 +31,7 @@ use core::fmt::Display;
use core::str::FromStr; use core::str::FromStr;
use core::{fmt, ops}; use core::{fmt, ops};
use hashes::hex::{Error, FromHex}; use hex::FromHex;
use internals::{debug_from_display, write_err}; use internals::{debug_from_display, write_err};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -313,7 +313,7 @@ impl Magic {
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
pub struct ParseMagicError { pub struct ParseMagicError {
/// The error that occurred when parsing the string. /// The error that occurred when parsing the string.
error: Error, error: hex::HexToArrayError,
/// The byte string that failed to parse. /// The byte string that failed to parse.
magic: String, magic: String,
} }
@ -362,7 +362,7 @@ impl TryFrom<Magic> for Network {
impl fmt::Display for Magic { impl fmt::Display for Magic {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 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(()) Ok(())
} }
} }
@ -370,14 +370,14 @@ debug_from_display!(Magic);
impl fmt::LowerHex for Magic { impl fmt::LowerHex for Magic {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 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(()) Ok(())
} }
} }
impl fmt::UpperHex for Magic { impl fmt::UpperHex for Magic {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 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(()) Ok(())
} }
} }

View File

@ -790,13 +790,13 @@ macro_rules! impl_hex {
($hex:ident, $case:expr) => { ($hex:ident, $case:expr) => {
impl $hex for U256 { impl $hex for U256 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 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!(LowerHex, hex::Case::Lower);
impl_hex!(UpperHex, internals::hex::Case::Upper); impl_hex!(UpperHex, hex::Case::Upper);
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
impl crate::serde::Serialize for U256 { impl crate::serde::Serialize for U256 {
@ -825,7 +825,7 @@ impl<'de> crate::serde::Deserialize<'de> for U256 {
fn deserialize<D: crate::serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> { fn deserialize<D: crate::serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
use core::convert::TryInto; use core::convert::TryInto;
use hashes::hex::FromHex; use hex::FromHex;
use crate::serde::de; use crate::serde::de;

View File

@ -4,7 +4,7 @@
macro_rules! hex_psbt { macro_rules! hex_psbt {
($s:expr) => { ($s:expr) => {
<$crate::psbt::Psbt>::deserialize( <$crate::psbt::Psbt>::deserialize(
&<$crate::prelude::Vec<u8> as $crate::hashes::hex::FromHex>::from_hex($s).unwrap(), &<$crate::prelude::Vec<u8> as $crate::hex::FromHex>::from_hex($s).unwrap(),
) )
}; };
} }

View File

@ -178,9 +178,7 @@ impl Deserialize for ecdsa::Signature {
ecdsa::Error::EmptySignature => Error::InvalidEcdsaSignature(e), ecdsa::Error::EmptySignature => Error::InvalidEcdsaSignature(e),
ecdsa::Error::NonStandardSighashType(flag) => Error::NonStandardSighashType(flag), ecdsa::Error::NonStandardSighashType(flag) => Error::NonStandardSighashType(flag),
ecdsa::Error::Secp256k1(..) => Error::InvalidEcdsaSignature(e), ecdsa::Error::Secp256k1(..) => Error::InvalidEcdsaSignature(e),
ecdsa::Error::HexEncoding(..) => { ecdsa::Error::Hex(..) => unreachable!("Decoding from slice, not hex"),
unreachable!("Decoding from slice, not hex")
}
}) })
} }
} }

View File

@ -12,7 +12,7 @@ impl<'a> serde::Serialize for SerializeBytesAsHex<'a> {
where where
S: serde::Serializer, S: serde::Serializer,
{ {
use internals::hex::display::DisplayHex; use hex::DisplayHex;
serializer.collect_str(&format_args!("{:x}", self.0.as_hex())) 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. // NOTE: This module can be exactly copied to use with HashMap.
use hashes::hex::FromHex; use hex::FromHex;
use serde; use serde;
use crate::prelude::*; use crate::prelude::*;
@ -248,7 +248,7 @@ pub mod hex_bytes {
//! Module for serialization of byte arrays as hex strings. //! Module for serialization of byte arrays as hex strings.
#![allow(missing_docs)] #![allow(missing_docs)]
use hashes::hex::FromHex; use hex::FromHex;
use serde; use serde;
pub fn serialize<T, S>(bytes: &T, s: S) -> Result<S::Ok, S::Error> pub fn serialize<T, S>(bytes: &T, s: S) -> Result<S::Ok, S::Error>

View File

@ -1561,9 +1561,9 @@ impl std::error::Error for TaprootError {
mod test { mod test {
use core::str::FromStr; use core::str::FromStr;
use hashes::hex::FromHex;
use hashes::sha256t::Tag; use hashes::sha256t::Tag;
use hashes::{sha256, Hash, HashEngine}; use hashes::{sha256, Hash, HashEngine};
use hex::FromHex;
use secp256k1::{VerifyOnly, XOnlyPublicKey}; use secp256k1::{VerifyOnly, XOnlyPublicKey};
use super::*; use super::*;

View File

@ -9,7 +9,7 @@ use bitcoin::bip32::{ExtendedPrivKey, ExtendedPubKey, Fingerprint, IntoDerivatio
use bitcoin::blockdata::opcodes::OP_0; use bitcoin::blockdata::opcodes::OP_0;
use bitcoin::blockdata::script; use bitcoin::blockdata::script;
use bitcoin::consensus::encode::{deserialize, serialize_hex}; use bitcoin::consensus::encode::{deserialize, serialize_hex};
use bitcoin::hashes::hex::FromHex; use bitcoin::hex::FromHex;
use bitcoin::psbt::{Psbt, PsbtSighashType}; use bitcoin::psbt::{Psbt, PsbtSighashType};
use bitcoin::script::PushBytes; use bitcoin::script::PushBytes;
use bitcoin::secp256k1::{self, Secp256k1}; use bitcoin::secp256k1::{self, Secp256k1};

View File

@ -31,8 +31,8 @@ use bitcoin::bip32::{ChildNumber, ExtendedPrivKey, ExtendedPubKey, KeySource};
use bitcoin::blockdata::locktime::{absolute, relative}; use bitcoin::blockdata::locktime::{absolute, relative};
use bitcoin::blockdata::witness::Witness; use bitcoin::blockdata::witness::Witness;
use bitcoin::consensus::encode::deserialize; use bitcoin::consensus::encode::deserialize;
use bitcoin::hashes::hex::FromHex;
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash}; use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
use bitcoin::hex::FromHex;
use bitcoin::psbt::raw::{self, Key, Pair, ProprietaryKey}; use bitcoin::psbt::raw::{self, Key, Pair, ProprietaryKey};
use bitcoin::psbt::{Input, Output, Psbt, PsbtSighashType}; use bitcoin::psbt::{Input, Output, Psbt, PsbtSighashType};
use bitcoin::sighash::{EcdsaSighashType, TapSighashType}; use bitcoin::sighash::{EcdsaSighashType, TapSighashType};

View File

@ -14,9 +14,11 @@ exclude = ["tests", "contrib"]
[features] [features]
default = ["std"] default = ["std"]
std = ["alloc", "internals/std"] std = ["alloc", "internals/std", "hex/std"]
alloc = ["internals/alloc"] alloc = ["internals/alloc", "hex/alloc"]
serde-std = ["serde/std"] 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] [package.metadata.docs.rs]
all-features = true all-features = true
@ -24,12 +26,15 @@ rustdoc-args = ["--cfg", "docsrs"]
[dependencies] [dependencies]
internals = { package = "bitcoin-internals", version = "0.2.0" } 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 } schemars = { version = "0.8.3", optional = true }
# Only enable this if you explicitly do not want to use "std", otherwise enable "serde-std". # 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 } 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] [dev-dependencies]
serde_test = "1.0" serde_test = "1.0"
serde_json = "1.0" serde_json = "1.0"

View File

@ -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<I>(iter: I) -> Result<Self, Error>
where
I: Iterator<Item = Result<u8, Error>> + ExactSizeIterator + DoubleEndedIterator;
/// Produces an object from a hex string.
fn from_hex(s: &str) -> Result<Self, Error> { 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<HexIterator<'a>, 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<u8, Error> {
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<u8, Error>;
fn next(&mut self) -> Option<Result<u8, Error>> {
let hi = self.iter.next()?;
let lo = self.iter.next().unwrap();
Some(chars_to_hex(hi, lo))
}
fn size_hint(&self) -> (usize, Option<usize>) {
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<usize> {
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<Result<u8, Error>> {
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<u8> {
fn from_byte_iter<I>(iter: I) -> Result<Self, Error>
where
I: Iterator<Item = Result<u8, Error>> + ExactSizeIterator + DoubleEndedIterator,
{
iter.collect()
}
}
macro_rules! impl_fromhex_array {
($len:expr) => {
impl FromHex for [u8; $len] {
fn from_byte_iter<I>(iter: I) -> Result<Self, Error>
where
I: Iterator<Item = Result<u8, Error>> + 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<u8> = 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<u8> = 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::<u8>::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::<u8>::from_hex(badchar1), Err(Error::InvalidChar(b'Z')));
assert_eq!(Vec::<u8>::from_hex(badchar2), Err(Error::InvalidChar(b'Y')));
assert_eq!(Vec::<u8>::from_hex(badchar3), Err(Error::InvalidChar(194)));
}
}

View File

@ -5,13 +5,7 @@
//! Implementations of traits defined in `std` / `core2` and not in `core`. //! Implementations of traits defined in `std` / `core2` and not in `core`.
//! //!
#[cfg(feature = "std")] use crate::{hmac, io, ripemd160, sha1, sha256, sha512, siphash24, HashEngine};
use std::io;
#[cfg(not(feature = "std"))]
use core2::io;
use crate::{hmac, ripemd160, sha1, sha256, sha512, siphash24, HashEngine};
impl io::Write for sha1::HashEngine { impl io::Write for sha1::HashEngine {
fn flush(&mut self) -> io::Result<()> { Ok(()) } fn flush(&mut self) -> io::Result<()> { Ok(()) }

View File

@ -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 { fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result {
#[allow(unused)] #[allow(unused)]
use crate::Hash as _; use crate::Hash as _;
let case = internals::hex::Case::Lower; let case = $crate::hex::Case::Lower;
if <$ty<$($gen),*>>::DISPLAY_BACKWARD { 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 { } 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 { fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result {
#[allow(unused)] #[allow(unused)]
use crate::Hash as _; use crate::Hash as _;
let case = internals::hex::Case::Upper; let case = $crate::hex::Case::Upper;
if <$ty<$($gen),*>>::DISPLAY_BACKWARD { 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 { } 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 /// This is mainly intended as an internal method and you shouldn't need it unless
/// you're doing something special. /// you're doing something special.
pub fn forward_hex(&self) -> impl '_ + core::fmt::LowerHex + core::fmt::UpperHex { 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. /// 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 /// This is mainly intended as an internal method and you shouldn't need it unless
/// you're doing something special. /// you're doing something special.
pub fn backward_hex(&self) -> impl '_ + core::fmt::LowerHex + core::fmt::UpperHex { 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 /// 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),*> { impl<$($gen: $gent),*> str::FromStr for Hash<$($gen),*> {
type Err = $crate::hex::Error; type Err = $crate::hex::HexToArrayError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
use $crate::hex::{FromHex, HexIterator}; use $crate::hex::{FromHex, HexToBytesIter};
use $crate::Hash; use $crate::Hash;
let inner: [u8; $bits / 8] = if $reverse { let inner: [u8; $bits / 8] = if $reverse {
FromHex::from_byte_iter(HexIterator::new(s)?.rev())? FromHex::from_byte_iter(HexToBytesIter::new(s)?.rev())?
} else { } else {
FromHex::from_byte_iter(HexIterator::new(s)?)? FromHex::from_byte_iter(HexToBytesIter::new(s)?)?
}; };
Ok(Self::from_byte_array(inner)) Ok(Self::from_byte_array(inner))
} }

View File

@ -79,12 +79,12 @@
// Instead of littering the codebase for non-fuzzing code just globally allow. // Instead of littering the codebase for non-fuzzing code just globally allow.
#![cfg_attr(hashes_fuzz, allow(dead_code, unused_imports))] #![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")))] #[cfg(all(feature = "alloc", not(feature = "std")))]
extern crate alloc; extern crate alloc;
#[cfg(any(test, feature = "std"))] #[cfg(any(test, feature = "std"))]
extern crate core; extern crate core;
#[cfg(feature = "core2")]
extern crate core2;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
/// A generic serialization/deserialization framework. /// A generic serialization/deserialization framework.
@ -95,6 +95,9 @@ extern crate serde_test;
#[cfg(bench)] #[cfg(bench)]
extern crate test; extern crate test;
/// Re-export the `hex-conservative` crate.
pub extern crate hex;
#[doc(hidden)] #[doc(hidden)]
pub mod _export { pub mod _export {
/// A re-export of core::* /// A re-export of core::*
@ -113,9 +116,8 @@ mod util;
pub mod serde_macros; pub mod serde_macros;
pub mod cmp; pub mod cmp;
pub mod hash160; pub mod hash160;
pub mod hex;
pub mod hmac; pub mod hmac;
#[cfg(any(feature = "std", feature = "core2"))] #[cfg(any(test, feature = "std", feature = "core2"))]
mod impls; mod impls;
pub mod ripemd160; pub mod ripemd160;
pub mod sha1; pub mod sha1;
@ -127,7 +129,12 @@ pub mod sha512_256;
pub mod siphash24; pub mod siphash24;
use core::{borrow, fmt, hash, ops}; 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}; pub use hmac::{Hmac, HmacEngine};
/// A hashing engine which bytes can be serialized into. /// A hashing engine which bytes can be serialized into.

View File

@ -125,7 +125,7 @@ impl<I: SliceIndex<[u8]>> Index<I> for Midstate {
} }
impl str::FromStr for Midstate { impl str::FromStr for Midstate {
type Err = hex::Error; type Err = hex::HexToArrayError;
fn from_str(s: &str) -> Result<Self, Self::Err> { hex::FromHex::from_hex(s) } fn from_str(s: &str) -> Result<Self, Self::Err> { hex::FromHex::from_hex(s) }
} }
@ -174,9 +174,12 @@ impl Midstate {
} }
impl hex::FromHex for Midstate { impl hex::FromHex for Midstate {
fn from_byte_iter<I>(iter: I) -> Result<Self, hex::Error> type Err = hex::HexToArrayError;
fn from_byte_iter<I>(iter: I) -> Result<Self, Self::Err>
where where
I: Iterator<Item = Result<u8, hex::Error>> + ExactSizeIterator + DoubleEndedIterator, I: Iterator<Item = Result<u8, hex::HexToBytesError>>
+ ExactSizeIterator
+ DoubleEndedIterator,
{ {
// DISPLAY_BACKWARD is true // DISPLAY_BACKWARD is true
Ok(Midstate::from_byte_array(hex::FromHex::from_byte_iter(iter.rev())?)) Ok(Midstate::from_byte_array(hex::FromHex::from_byte_iter(iter.rev())?))

View File

@ -268,15 +268,15 @@ macro_rules! hash_newtype {
} }
impl $crate::_export::_core::str::FromStr for $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> { 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; use $crate::Hash;
let inner: <$hash as Hash>::Bytes = if <Self as $crate::Hash>::DISPLAY_BACKWARD { let inner: <$hash as Hash>::Bytes = if <Self as $crate::Hash>::DISPLAY_BACKWARD {
FromHex::from_byte_iter(HexIterator::new(s)?.rev())? FromHex::from_byte_iter(HexToBytesIter::new(s)?.rev())?
} else { } else {
FromHex::from_byte_iter(HexIterator::new(s)?)? FromHex::from_byte_iter(HexToBytesIter::new(s)?)?
}; };
Ok($newtype(<$hash>::from_byte_array(inner))) Ok($newtype(<$hash>::from_byte_array(inner)))
} }

View File

@ -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<u8>]`. 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<core::slice::Iter<'a, u8>, [u8; $len]>;
fn as_hex(self) -> Self::Display {
super::super::display::DisplayArray::new(self.iter())
}
fn hex_reserve_suggestion(self) -> usize {
$len
}
}
)*
}
}
impl<T: AsOutBytes + ?Sized> 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<T: AsOutBytes + ?Sized> 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<T: AsOutBytes> {
buf: T,
pos: usize,
}
impl<T: AsOutBytes> BufEncoder<T> {
/// 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<I>(&mut self, bytes: I, case: Case)
where
I: IntoIterator,
I::Item: Borrow<u8>,
{
self.put_bytes_inner(bytes.into_iter(), case)
}
#[inline]
#[track_caller]
fn put_bytes_inner<I>(&mut self, bytes: I, case: Case)
where
I: Iterator,
I::Item: Borrow<u8>,
{
// 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();
}
}
}

View File

@ -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::<Self::Display>();
// 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<T: ?Sized> 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<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")
}
}
/// 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<A: Clone + IntoIterator, B: FixedLenBuf>
where
A::Item: Borrow<u8>,
{
array: A,
_buffer_marker: core::marker::PhantomData<B>,
}
impl<A: Clone + IntoIterator, B: FixedLenBuf> DisplayArray<A, B>
where
A::Item: Borrow<u8>,
{
/// 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<A: Clone + IntoIterator, B: FixedLenBuf> fmt::LowerHex for DisplayArray<A, B>
where
A::Item: Borrow<u8>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Lower) }
}
impl<A: Clone + IntoIterator, B: FixedLenBuf> fmt::UpperHex for DisplayArray<A, B>
where
A::Item: Borrow<u8>,
{
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<I>(
f: &mut fmt::Formatter,
buf: &mut OutBytes,
bytes: I,
case: Case,
) -> fmt::Result
where
I: IntoIterator,
I::Item: Borrow<u8>,
{
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));
}
}
}

View File

@ -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)]]
}

View File

@ -19,13 +19,6 @@ extern crate alloc;
extern crate std; extern crate std;
pub mod error; pub mod error;
pub mod hex;
pub mod macros; pub mod macros;
mod parse; mod parse;
pub mod serde; pub mod serde;
/// Mainly reexports based on features.
pub(crate) mod prelude {
#[cfg(feature = "alloc")]
pub(crate) use alloc::string::String;
}