hex_conservative/
lib.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Hex encoding and decoding.
4//!
5//! General purpose hex encoding/decoding library with a conservative MSRV and dependency policy.
6//!
7//! ## Basic Usage
8//! ```
9//! # #[cfg(feature = "alloc")] {
10//! // Use the `package` key to improve import ergonomics (`hex` instead of `hex-conservative`).
11//! // hex = { package = "hex-conservative", version = "*" }
12//! # use hex_conservative as hex; // No need for this if using `package` as above.
13//! use hex::{DisplayHex, FromHex};
14//!
15//! // Decode an arbitrary length hex string into a vector.
16//! let v = Vec::from_hex("deadbeef").expect("valid hex digits");
17//! // Or a known length hex string into a fixed size array.
18//! let a = <[u8; 4]>::from_hex("deadbeef").expect("valid length and valid hex digits");
19//!
20//! // We support `LowerHex` and `UpperHex` out of the box for `[u8]` slices.
21//! println!("An array as lower hex: {:x}", a.as_hex());
22//! // And for vecs since `Vec` derefs to byte slice.
23//! println!("A vector as upper hex: {:X}", v.as_hex());
24//!
25//! // Allocate a new string (also `to_upper_hex_string`).
26//! let s = v.to_lower_hex_string();
27//!
28//! // Please note, mixed case strings will still parse successfully but we only
29//! // support displaying hex in a single case.
30//! assert_eq!(
31//!     Vec::from_hex("dEaDbEeF").expect("valid mixed case hex digits"),
32//!     Vec::from_hex("deadbeef").expect("valid hex digits"),
33//! );
34//! # }
35//! ```
36
37#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
38// Experimental features we need.
39#![cfg_attr(docsrs, feature(doc_cfg))]
40// Coding conventions
41#![warn(missing_docs)]
42
43#[cfg(feature = "alloc")]
44extern crate alloc;
45
46pub mod buf_encoder;
47pub mod display;
48mod error;
49mod iter;
50pub mod parse;
51
52pub use display::DisplayHex;
53pub use iter::{BytesToHexIter, HexToBytesIter};
54pub use parse::{FromHex, HexToArrayError, HexToBytesError};
55
56/// Reexports of extension traits.
57pub mod exts {
58    pub use super::display::DisplayHex;
59    pub use super::parse::FromHex;
60}
61
62/// Mainly reexports based on features.
63pub(crate) mod prelude {
64    #[cfg(feature = "alloc")]
65    pub(crate) use alloc::string::String;
66}
67
68/// Possible case of hex.
69#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
70pub enum Case {
71    /// Produce lower-case chars (`[0-9a-f]`).
72    ///
73    /// This is the default.
74    Lower,
75
76    /// Produce upper-case chars (`[0-9A-F]`).
77    Upper,
78}
79
80impl Default for Case {
81    fn default() -> Self { Case::Lower }
82}
83
84impl Case {
85    /// Returns the encoding table.
86    ///
87    /// The returned table may only contain displayable ASCII chars.
88    #[inline]
89    #[rustfmt::skip]
90    pub(crate) fn table(self) -> &'static [u8; 16] {
91        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'];
92        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'];
93
94        match self {
95            Case::Lower => &LOWER,
96            Case::Upper => &UPPER,
97        }
98    }
99}
100
101/// Encodes single byte as two ASCII chars using the given table.
102///
103/// The function guarantees only returning values from the provided table.
104#[inline]
105pub(crate) fn byte_to_hex(byte: u8, table: &[u8; 16]) -> [u8; 2] {
106    [table[usize::from(byte.wrapping_shr(4))], table[usize::from(byte & 0x0F)]]
107}
108
109/// Quick and dirty macro for parsing hex in tests.
110///
111/// For improved ergonomics import with: `use hex_conservative::test_hex_unwrap as hex;`
112#[macro_export]
113macro_rules! test_hex_unwrap (($hex:expr) => (<Vec<u8> as $crate::FromHex>::from_hex($hex).unwrap()));
114
115#[cfg(test)]
116mod tests {
117    use crate::test_hex_unwrap as hex;
118
119    #[test]
120    fn parse_hex_into_vector() {
121        let got = hex!("deadbeef");
122        let want = vec![0xde, 0xad, 0xbe, 0xef];
123        assert_eq!(got, want)
124    }
125}