#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
macro_rules! maybe_const {
($($fun:tt)*) => {
#[cfg(rust_v_1_46)]
#[track_caller]
#[inline]
const $($fun)*
#[cfg(not(rust_v_1_46))]
$($fun)*
};
}
macro_rules! pub_maybe_const {
($($fun:tt)*) => {
#[doc(hidden)]
#[cfg(rust_v_1_46)]
#[track_caller]
#[inline]
pub const $($fun)*
#[doc(hidden)]
#[cfg(not(rust_v_1_46))]
pub $($fun)*
}
}
#[cfg(not(rust_v_1_46))]
fn invalid_digit(digit: u8) {
panic!("invalid hex digit: ASCII {}", digit);
}
#[cfg(all(not(rust_v_1_57), rust_v_1_46))]
#[track_caller]
const fn invalid_digit(digit: u8) {
let digit = digit as usize;
#[allow(unknown_lints)]
#[allow(unconditional_panic)]
let _invalid_digit = [(); 0][digit + 10000];
}
#[cfg(rust_v_1_57)]
#[track_caller]
const fn invalid_digit(digit: u8) {
let mut buf = [b'i', b'n', b'v', b'a', b'l', b'i', b'd', b' ', b'h', b'e', b'x', b' ', b'd', b'i', b'g', b'i', b't', b':', b' ', b'A', b'S', b'C', b'I', b'I', b' ', b' ', b' ', b' '];
if digit >= 100 {
buf[buf.len() - 3] = digit / 100 + b'0';
}
if digit >= 10 {
buf[buf.len() - 2] = (digit % 100) / 10 + b'0';
}
buf[buf.len() - 1] = digit % 10 + b'0';
let message = unsafe { core::str::from_utf8_unchecked(&buf) };
panic!("{}", message);
}
maybe_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,
_ => {
invalid_digit(digit);
0
}
}
}
}
pub_maybe_const! {
fn decode_byte(hex: &str, pos: usize) -> u8 {
let c1 = decode_digit(hex.as_bytes()[pos * 2]);
let c2 = decode_digit(hex.as_bytes()[pos* 2 + 1]);
c1 << 4 | c2
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! hex_impl {
($hex:expr) => {
{
const HEX: &str = $hex;
const _HEX_LENGTH_MUST_BE_EVEN: () = [()][$hex.len() % 2];
let mut out = [0u8; HEX.len() / 2];
let mut pos = 0;
loop {
if pos >= out.len() {
break;
}
out[pos] = $crate::decode_byte(HEX, pos);
pos += 1
}
out
}
}
}
#[doc(hidden)]
#[cfg(not(feature = "rust_v_1_46"))]
pub fn msrv_opt_in() {}
#[doc(hidden)]
#[cfg(feature = "rust_v_1_46")]
pub const fn msrv_opt_in() {}
#[cfg(not(rust_v_1_46))]
#[macro_export]
macro_rules! hex {
($hex:expr) => {
$crate::hex_impl!($hex)
}
}
#[cfg(rust_v_1_46)]
#[macro_export]
macro_rules! hex {
($hex:expr) => {
{
const TMP: [u8; $hex.len() / 2] = $crate::hex_impl!($hex);
$crate::msrv_opt_in();
TMP
}
}
}
#[cfg(feature = "rust_v_1_46")]
#[cfg_attr(docsrs, doc(cfg(feature = "rust_v_1_46")))]
#[macro_export]
macro_rules! hex_const {
($name:ident = $hex:expr) => {
const $name: [u8; $hex.len() / 2] = $crate::hex_impl!($hex);
}
}
#[cfg(feature = "rust_v_1_46")]
#[cfg_attr(docsrs, doc(cfg(feature = "rust_v_1_46")))]
#[macro_export]
macro_rules! hex_static {
($name:ident = $hex:expr) => {
static $name: [u8; $hex.len() / 2] = $crate::hex_impl!($hex);
}
}
#[cfg(test)]
mod tests {
use super::hex;
#[test]
fn msrv_empty() {
let arr = hex!("");
assert_eq!(&arr, &[]);
}
#[test]
fn msrv_one() {
let arr = hex!("2a");
assert_eq!(&arr, &[42u8]);
}
#[test]
fn msrv_several() {
let arr = hex!("2a15ff");
assert_eq!(&arr, &[42u8, 21, 255]);
}
#[test]
fn const_val_works() {
const VAL: &str = "2a15ff";
let arr = hex!(VAL);
assert_eq!(&arr, &[42u8, 21, 255]);
}
#[test]
#[cfg(not(rust_v_1_46))]
#[should_panic]
fn invalid_digit_120() {
hex!("xx");
}
#[test]
#[cfg(not(rust_v_1_46))]
#[should_panic]
fn invalid_digit_32() {
hex!(" x");
}
}