consensus_encoding: Add decode_from_hex_with
What changed, and why it matters
This commit adds a new public helper function called decode_from_hex_with to a Rust Bitcoin encoding library. It is a pure feature addition that mirrors existing decode helpers and gives callers a way to decode hex strings by specifying a decoder type directly, rather than through an existing Decode trait. There is no bug fix, no change to existing behavior, and no security relevance in the code itself.
No security action required. Treat as a routine API addition.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change introduces decode_from_hex_with
Changed components
consensus_encoding/src/decode/mod.rsconsensus_encoding/src/lib.rsconsensus_encoding/api/all-features.txtconsensus_encoding/tests/decode.rsInspect captured patch +37 / −5
diff --git a/consensus_encoding/api/all-features.txt b/consensus_encoding/api/all-features.txt
index 277f9814..b4944685 100644
--- a/consensus_encoding/api/all-features.txt
+++ b/consensus_encoding/api/all-features.txt
@@ -1560,6 +1560,7 @@ pub fn bitcoin_consensus_encoding::check_decoder<D: bitcoin_consensus_encoding::
pub fn bitcoin_consensus_encoding::check_encode<T: bitcoin_consensus_encoding::Encode + ?core::marker::Sized>(value: &T, expected: &[u8])
pub fn bitcoin_consensus_encoding::check_encoder<T: bitcoin_consensus_encoding::Encoder + ?core::marker::Sized>(encoder: &mut T, expected: &[u8])
pub fn bitcoin_consensus_encoding::decode_from_hex<T: bitcoin_consensus_encoding::Decode>(hex: &str) -> core::result::Result<T, bitcoin_consensus_encoding::error::FromHexError<<<T as bitcoin_consensus_encoding::Decode>::Decoder as bitcoin_consensus_encoding::Decoder>::Error>>
+pub fn bitcoin_consensus_encoding::decode_from_hex_with<D: bitcoin_consensus_encoding::Decoder + core::default::Default>(hex: &str) -> core::result::Result<<D as bitcoin_consensus_encoding::Decoder>::Output, bitcoin_consensus_encoding::error::FromHexError<<D as bitcoin_consensus_encoding::Decoder>::Error>>
pub fn bitcoin_consensus_encoding::decode_from_read<T, R>(reader: R) -> core::result::Result<T, bitcoin_consensus_encoding::error::ReadError<<<T as bitcoin_consensus_encoding::Decode>::Decoder as bitcoin_consensus_encoding::Decoder>::Error>> where T: bitcoin_consensus_encoding::Decode, R: std::io::BufRead
pub fn bitcoin_consensus_encoding::decode_from_read_unbuffered<T, R>(reader: R) -> core::result::Result<T, bitcoin_consensus_encoding::error::ReadError<<<T as bitcoin_consensus_encoding::Decode>::Decoder as bitcoin_consensus_encoding::Decoder>::Error>> where T: bitcoin_consensus_encoding::Decode, R: std::io::Read
pub fn bitcoin_consensus_encoding::decode_from_read_unbuffered_with<T, R, const BUFFER_SIZE: usize>(reader: R) -> core::result::Result<T, bitcoin_consensus_encoding::error::ReadError<<<T as bitcoin_consensus_encoding::Decode>::Decoder as bitcoin_consensus_encoding::Decoder>::Error>> where T: bitcoin_consensus_encoding::Decode, R: std::io::Read
diff --git a/consensus_encoding/src/decode/mod.rs b/consensus_encoding/src/decode/mod.rs
index 137617e7..a1776fba 100644
--- a/consensus_encoding/src/decode/mod.rs
+++ b/consensus_encoding/src/decode/mod.rs
@@ -143,11 +143,37 @@ impl DecoderStatus {
pub fn decode_from_hex<T: Decode>(
hex: &str,
) -> Result<T, FromHexError<<T::Decoder as Decoder>::Error>> {
+ decode_from_hex_internal(hex, T::decoder())
+}
+
+/// Decodes an object from a hex string without heap allocations using a [`Decoder`] type.
+///
+/// Unlike [`decode_from_hex`], this takes a generic [`Decoder`] parameter, allowing use with
+/// decoders which don't have a dedicated [`Decode`] implementer (e.g. [`CompactSizeDecoder`]).
+///
+/// # Errors
+///
+/// [`FromHexError`] if the string has an odd number of characters, any character is not a
+/// valid hex digit, or if decoding the type fails, including if bytes remain unconsumed
+/// after the decoder completes.
+///
+/// [`CompactSizeDecoder`]: crate::CompactSizeDecoder
+#[cfg(feature = "hex")]
+pub fn decode_from_hex_with<D: Decoder + Default>(
+ hex: &str,
+) -> Result<D::Output, FromHexError<D::Error>> {
+ decode_from_hex_internal(hex, D::default())
+}
+
+#[cfg(feature = "hex")]
+fn decode_from_hex_internal<D: Decoder>(
+ hex: &str,
+ mut decoder: D,
+) -> Result<D::Output, FromHexError<D::Error>> {
let iter = hex::HexSliceToBytesIter::new(hex)
.map_err(FromHexErrorInner::OddLength)
.map_err(FromHexError)?;
- let mut decoder = T::decoder();
let mut buffer = [0u8; 4096];
let mut index = 0;
diff --git a/consensus_encoding/src/lib.rs b/consensus_encoding/src/lib.rs
index cd8d6a36..8981aa0b 100644
--- a/consensus_encoding/src/lib.rs
+++ b/consensus_encoding/src/lib.rs
@@ -48,6 +48,7 @@
//! * [`decode_from_read_with`]: Counterpart to [`decode_from_read`].
//! * [`decode_from_slice_with`]: Counterpart to [`decode_from_slice`].
//! * [`decode_from_slice_unbounded_with`]: Counterpart to [`decode_from_slice_unbounded`].
+//! * [`decode_from_hex_with`]: Counterpart to [`decode_from_hex`].
//!
//! And on the encoding side we provide:
//!
@@ -62,8 +63,8 @@
//!
//! * `std` - Enables std lib I/O driver functions and `std::error::Error` impls (implies `alloc`).
//! * `alloc` - Enables [`encode_to_vec`], `Vec`-based decoders, and allocation-based helpers.
-//! * `hex` - Enables [`decode_from_hex`], [`encode_to_hex`] and [`drain_to_hex`]. Encoding also
-//! requires `alloc`.
+//! * `hex` - Enables [`decode_from_hex`], [`decode_from_hex_with`], [`encode_to_hex`] and
+//! [`drain_to_hex`]. Encoding also requires `alloc`.
#![no_std]
// Coding conventions.
@@ -93,7 +94,7 @@ pub mod serde_as_consensus;
pub use self::compact_size::{CompactSizeDecoder, CompactSizeEncoder, CompactSizeU64Decoder};
#[cfg(feature = "hex")]
#[doc(inline)]
-pub use self::decode::decode_from_hex;
+pub use self::decode::{decode_from_hex, decode_from_hex_with};
#[doc(inline)]
pub use self::decode::decoders::{ArrayDecoder, Decoder2, Decoder3, Decoder4, Decoder6};
#[cfg(feature = "alloc")]
diff --git a/consensus_encoding/tests/decode.rs b/consensus_encoding/tests/decode.rs
index 05c65e3d..11639ea2 100644
--- a/consensus_encoding/tests/decode.rs
+++ b/consensus_encoding/tests/decode.rs
@@ -7,7 +7,7 @@ use std::io::{Cursor, Read};
use bitcoin_consensus_encoding as encoding;
#[cfg(feature = "hex")]
-use bitcoin_consensus_encoding::decode_from_hex;
+use bitcoin_consensus_encoding::{decode_from_hex, decode_from_hex_with};
#[cfg(feature = "alloc")]
use encoding::check_decode;
use encoding::{
@@ -295,6 +295,10 @@ fn decode_from_hex_test() {
assert_eq!(result.unwrap().0, [0x01, 0x02, 0x03, 0x04]);
let result: Result<TestArray, _> = decode_from_hex("DEADBEEF");
assert_eq!(result.unwrap().0, [0xDE, 0xAD, 0xBE, 0xEF]);
+ let result = decode_from_hex_with::<TestArrayDecoder>("01020304");
+ assert_eq!(result.unwrap().0, [0x01, 0x02, 0x03, 0x04]);
+ let result = decode_from_hex_with::<TestArrayDecoder>("DEADBEEF");
+ assert_eq!(result.unwrap().0, [0xDE, 0xAD, 0xBE, 0xEF]);
}
#[test]
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.