From 855c8fa7fac20816395003f744b6779e165e37e7 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Wed, 28 Jan 2026 12:50:13 -0800 Subject: [PATCH 1/2] Add const hex macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a `hex!` macro for parsing hex strings in const contexts. The implementation is inspired by the hex_lit crate by Martin Habovštiak (https://github.com/Kixunil/hex_lit). --- CHANGELOG.md | 4 ++++ src/lib.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b61392..5d7834c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.3.2 - 2025-11-28 + +- Add `hex!` macro for const hex literal parsing. + # 0.3.1 - 2025-11-24 - Remove `doc_auto_cfg` because it breaks the docs builds on crates.io diff --git a/src/lib.rs b/src/lib.rs index a71b9b6..01a8bde 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,16 @@ //! //! General purpose hex encoding/decoding library with a conservative MSRV and dependency policy. //! -//! ## Basic Usage +//! ## Const hex literals +//! +//! ``` +//! use hex_conservative::hex; +//! +//! const GENESIS: [u8; 32] = hex!("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); +//! ``` +//! +//! ## Runtime hex parsing +//! //! ``` //! # #[cfg(feature = "alloc")] { //! // In your manifest use the `package` key to improve import ergonomics. @@ -59,6 +68,38 @@ pub mod parse; #[cfg(feature = "serde")] pub mod serde; +/// Parses hex strings in const contexts. +/// +/// Returns `[u8; N]` arrays. The string must have even length. +#[macro_export] +macro_rules! hex { + ($hex:expr) => {{ + const _: () = assert!($hex.len() % 2 == 0, "hex string must have even length"); + + const fn decode_digit(digit: u8) -> u8 { + match digit { + b'0'..=b'9' => digit - b'0', + b'a'..=b'f' => digit - b'a' + 10, + b'A'..=b'F' => digit - b'A' + 10, + _ => panic!("invalid hex digit"), + } + } + + let mut output = [0u8; $hex.len() / 2]; + let bytes = $hex.as_bytes(); + + let mut i = 0; + while i < output.len() { + let high = decode_digit(bytes[i * 2]); + let low = decode_digit(bytes[i * 2 + 1]); + output[i] = (high << 4) | low; + i += 1; + } + + output + }}; +} + /// Re-exports of the common crate traits. pub mod prelude { #[doc(inline)] @@ -156,13 +197,33 @@ mod table { macro_rules! test_hex_unwrap (($hex:expr) => ( as $crate::FromHex>::from_hex($hex).unwrap())); #[cfg(test)] +#[cfg(feature = "alloc")] mod tests { - use crate::test_hex_unwrap as hex; + use alloc::vec::Vec; #[test] fn parse_hex_into_vector() { - let got = hex!("deadbeef"); + let got = crate::test_hex_unwrap!("deadbeef"); let want = vec![0xde, 0xad, 0xbe, 0xef]; assert_eq!(got, want) } + + #[test] + fn hex_macro() { + let data = hex!("deadbeef"); + assert_eq!(data, [0xde, 0xad, 0xbe, 0xef]); + } + + #[test] + fn hex_macro_case_insensitive() { + assert_eq!(hex!("DEADBEEF"), hex!("deadbeef")); + } + + #[test] + fn hex_macro_const_context() { + const HASH: [u8; 32] = + hex!("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); + assert_eq!(HASH[0], 0x00); + assert_eq!(HASH[31], 0x6f); + } } From 54ec5a43f62da406e7791ccffcff0081584da1f4 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Wed, 28 Jan 2026 12:50:21 -0800 Subject: [PATCH 2/2] Bump version to 0.3.2 --- CHANGELOG.md | 2 +- Cargo-minimal.lock | 2 +- Cargo-recent.lock | 2 +- Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d7834c..1f4c4a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# 0.3.2 - 2025-11-28 +# 0.3.2 - 2026-01-28 - Add `hex!` macro for const hex literal parsing. diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock index cde7527..739e85c 100644 --- a/Cargo-minimal.lock +++ b/Cargo-minimal.lock @@ -16,7 +16,7 @@ checksum = "5edd69c67b2f8e0911629b7e6b8a34cb3956613cd7c6e6414966dee349c2db4f" [[package]] name = "hex-conservative" -version = "0.3.1" +version = "0.3.2" dependencies = [ "arrayvec", "serde", diff --git a/Cargo-recent.lock b/Cargo-recent.lock index d9e0b99..d236b07 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -10,7 +10,7 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "hex-conservative" -version = "0.3.1" +version = "0.3.2" dependencies = [ "arrayvec", "serde", diff --git a/Cargo.toml b/Cargo.toml index ef2dda0..17baad7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hex-conservative" -version = "0.3.1" +version = "0.3.2" authors = ["Martin Habovštiak ", "Andrew Poelstra "] license = "CC0-1.0" repository = "https://github.com/rust-bitcoin/hex-conservative"