#![no_std]
extern crate alloc;
use core::{char, convert::TryInto, num::ParseIntError};
use alloc::{string::String, vec::Vec};
#[cfg(feature = "serde")] use serde::{de::Error as DeError, Deserialize, Deserializer};
pub type ArrayBytesResult<T> = Result<T, Error>;
pub type Bytes = Vec<u8>;
pub type Hex = String;
pub trait TryFromHex
where
Self: Sized,
{
fn try_from_hex(hex: &str) -> ArrayBytesResult<Self>;
}
macro_rules! impl_num_from_hex {
($t:ty) => {
impl TryFromHex for $t {
fn try_from_hex(hex: &str) -> ArrayBytesResult<Self> {
Self::from_str_radix(&hex[if hex.starts_with("0x") { 2 } else { 0 }..], 16)
.map_err(|e| Error::ParseIntError(e))
}
}
};
}
impl_num_from_hex!(isize);
impl_num_from_hex!(i8);
impl_num_from_hex!(i16);
impl_num_from_hex!(i32);
impl_num_from_hex!(i64);
impl_num_from_hex!(i128);
impl_num_from_hex!(usize);
impl_num_from_hex!(u8);
impl_num_from_hex!(u16);
impl_num_from_hex!(u32);
impl_num_from_hex!(u64);
impl_num_from_hex!(u128);
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
InvalidLength { length: usize },
InvalidCharBoundary { index: usize },
ParseIntError(ParseIntError),
}
pub fn slice2array<const N: usize, T>(slice: &[T]) -> ArrayBytesResult<[T; N]>
where
T: Copy,
{
slice.try_into().map_err(|_| Error::InvalidLength { length: slice.len() })
}
pub fn slice2array_unchecked<const N: usize, T>(slice: &[T]) -> [T; N]
where
T: Copy,
{
slice2array(slice).unwrap()
}
pub fn slice_n_into<const N: usize, T, V>(slice: &[T]) -> ArrayBytesResult<V>
where
T: Copy,
V: From<[T; N]>,
{
Ok(slice2array(slice)?.into())
}
pub fn slice_n_into_unchecked<const N: usize, T, V>(slice: &[T]) -> V
where
T: Copy,
V: From<[T; N]>,
{
slice2array_unchecked(slice).into()
}
pub fn vec2array<const N: usize, T>(vec: Vec<T>) -> ArrayBytesResult<[T; N]> {
vec.try_into().map_err(|v: Vec<_>| Error::InvalidLength { length: v.len() })
}
pub fn vec2array_unchecked<const N: usize, T>(vec: Vec<T>) -> [T; N] {
vec2array(vec).unwrap()
}
pub fn vec_n_into<const N: usize, T, V>(vec: Vec<T>) -> ArrayBytesResult<V>
where
V: From<[T; N]>,
{
Ok(vec2array(vec)?.into())
}
pub fn vec_n_into_unchecked<const N: usize, T, V>(vec: Vec<T>) -> V
where
V: From<[T; N]>,
{
vec2array_unchecked(vec).into()
}
pub fn bytes2hex(prefix: &str, bytes: &[u8]) -> Hex {
let mut hex = Hex::with_capacity(prefix.len() + bytes.len() * 2);
for byte in prefix.chars() {
hex.push(byte);
}
for byte in bytes.iter() {
hex.push(char::from_digit((byte >> 4) as _, 16).unwrap());
hex.push(char::from_digit((byte & 0xf) as _, 16).unwrap());
}
hex
}
pub fn hex2array<const N: usize>(hex: &str) -> ArrayBytesResult<[u8; N]> {
vec2array(hex2bytes(hex)?)
}
pub fn hex2array_unchecked<const N: usize>(hex: &str) -> [u8; N] {
hex2bytes_unchecked(hex).try_into().unwrap()
}
pub fn hex2bytes(hex: &str) -> ArrayBytesResult<Bytes> {
if hex.len() % 2 != 0 {
return Err(Error::InvalidLength {
length: if hex.starts_with("0x") { hex.len() - 2 } else { hex.len() },
});
}
let mut bytes = Bytes::new();
for i in (if hex.starts_with("0x") { 2 } else { 0 }..hex.len()).step_by(2) {
for i in i + 1..i + 3 {
if !hex.is_char_boundary(i) {
return Err(Error::InvalidCharBoundary { index: i });
}
}
bytes.push(u8::from_str_radix(&hex[i..i + 2], 16).unwrap());
}
Ok(bytes)
}
pub fn hex2bytes_unchecked(hex: &str) -> Bytes {
(if hex.starts_with("0x") { 2 } else { 0 }..hex.len())
.step_by(2)
.map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
.collect()
}
pub fn hex_into<T, const N: usize>(hex: &str) -> ArrayBytesResult<T>
where
T: From<[u8; N]>,
{
Ok(hex2array(hex)?.into())
}
pub fn hex_into_unchecked<T, const N: usize>(hex: &str) -> T
where
T: From<[u8; N]>,
{
hex2array_unchecked(hex).into()
}
#[cfg(feature = "serde")]
pub fn hex_deserialize_into<'de, D, T, const N: usize>(hex: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
T: From<[u8; N]>,
{
Ok(hex2array_unchecked(<&str>::deserialize(hex)?).into())
}
#[cfg(feature = "serde")]
pub fn de_hex2num<'de, D, T>(hex: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
T: TryFromHex,
{
let hex = <&str>::deserialize(hex)?;
T::try_from_hex(hex).map_err(|_| D::Error::custom(alloc::format!("Invalid hex str `{}`", hex)))
}
#[cfg(feature = "serde")]
pub fn de_hex2bytes<'de, D>(hex: D) -> Result<Bytes, D::Error>
where
D: Deserializer<'de>,
{
let hex = <&str>::deserialize(hex)?;
hex2bytes(hex).map_err(|_| D::Error::custom(alloc::format!("Invalid hex str `{}`", hex)))
}
#[cfg(test)]
mod test {
use crate::*;
macro_rules! bytes {
($v:expr; $n:expr) => {{
let mut v = Bytes::new();
for _ in 0..$n {
v.push($v);
}
v
}};
}
#[derive(Debug, PartialEq)]
struct LJF([u8; 17]);
impl From<[u8; 17]> for LJF {
fn from(array: [u8; 17]) -> Self {
Self(array)
}
}
#[test]
fn slice2array_should_work() {
assert_eq!(slice2array::<8, _>(&[0; 8]), Ok([0; 8]));
}
#[test]
fn slice_n_into_should_work() {
assert_eq!(
slice_n_into::<17, u8, LJF>(b"Love Jane Forever"),
Ok(LJF(*b"Love Jane Forever"))
);
}
#[test]
fn slice_n_into_unchecked_should_work() {
assert_eq!(
slice_n_into_unchecked::<17, u8, LJF>(b"Love Jane Forever"),
LJF(*b"Love Jane Forever")
);
}
#[test]
fn vec2array_should_work() {
assert_eq!(vec2array::<8, _>(bytes![0; 8]), Ok([0; 8]));
}
#[test]
fn vec_n_into_should_work() {
assert_eq!(
vec_n_into::<17, u8, LJF>(b"Love Jane Forever".to_vec()),
Ok(LJF(*b"Love Jane Forever"))
);
}
#[test]
fn vec_n_into_unchecked_should_work() {
assert_eq!(
vec_n_into_unchecked::<17, u8, LJF>(b"Love Jane Forever".to_vec()),
LJF(*b"Love Jane Forever")
);
}
#[test]
fn bytes2hex_should_work() {
assert_eq!(
bytes2hex("0x", b"Love Jane Forever"),
Hex::from("0x4c6f7665204a616e6520466f7265766572")
);
assert_eq!(
bytes2hex("", b"Love Jane Forever"),
Hex::from("4c6f7665204a616e6520466f7265766572")
);
}
#[test]
fn hex2array_should_work() {
assert_eq!(hex2array("0x4c6f7665204a616e6520466f7265766572"), Ok(*b"Love Jane Forever"));
assert_eq!(hex2array("4c6f7665204a616e6520466f7265766572"), Ok(*b"Love Jane Forever"));
}
#[test]
fn hex2bytes_should_work() {
assert_eq!(
hex2bytes("0x4c6f7665204a616e6520466f7265766572"),
Ok(b"Love Jane Forever".to_vec())
);
assert_eq!(
hex2bytes("4c6f7665204a616e6520466f7265766572"),
Ok(b"Love Jane Forever".to_vec())
);
assert_eq!(hex2bytes("æˆ‘çˆ±ä½ ").unwrap_err(), Error::InvalidLength { length: 9 });
assert_eq!(hex2bytes("0xæˆ‘çˆ±ä½ ").unwrap_err(), Error::InvalidLength { length: 9 });
assert_eq!(hex2bytes("æˆ‘çˆ±ä½ ").unwrap_err(), Error::InvalidCharBoundary { index: 1 });
assert_eq!(hex2bytes(" æˆ‘çˆ±ä½ ").unwrap_err(), Error::InvalidCharBoundary { index: 2 });
}
#[test]
fn hex2bytes_unchecked_should_work() {
assert_eq!(
hex2bytes_unchecked("0x4c6f7665204a616e6520466f7265766572"),
*b"Love Jane Forever"
);
assert_eq!(
hex2bytes_unchecked("4c6f7665204a616e6520466f7265766572"),
*b"Love Jane Forever"
);
}
#[test]
fn hex_try_into_should_work() {
assert_eq!(
hex_into::<LJF, 17>("0x4c6f7665204a616e6520466f7265766572"),
Ok(LJF(*b"Love Jane Forever"))
);
assert_eq!(
hex_into::<LJF, 17>("4c6f7665204a616e6520466f7265766572"),
Ok(LJF(*b"Love Jane Forever"))
);
}
#[test]
fn hex_into_should_work() {
assert_eq!(
hex_into_unchecked::<LJF, 17>("0x4c6f7665204a616e6520466f7265766572"),
LJF(*b"Love Jane Forever")
);
assert_eq!(
hex_into_unchecked::<LJF, 17>("4c6f7665204a616e6520466f7265766572"),
LJF(*b"Love Jane Forever")
);
}
#[cfg(feature = "serde")]
#[test]
fn hex_deserialize_into_should_work() {
#[derive(Debug, PartialEq, Deserialize)]
struct WrappedLJF {
#[serde(deserialize_with = "hex_deserialize_into")]
ljf: LJF,
}
assert_eq!(
serde_json::from_str::<WrappedLJF>(
r#"{
"ljf": "0x4c6f7665204a616e6520466f7265766572"
}"#
),
Ok(WrappedLJF { ljf: LJF(*b"Love Jane Forever") })
);
assert_eq!(
serde_json::from_str::<WrappedLJF>(
r#"{
"ljf": "4c6f7665204a616e6520466f7265766572"
}"#
),
Ok(WrappedLJF { ljf: LJF(*b"Love Jane Forever") })
);
}
#[cfg(feature = "serde")]
#[test]
fn de_hex2num_should_work() {
macro_rules! assert_de_hex2num {
($num_type:ty) => {{
#[derive(Debug, PartialEq, Deserialize)]
struct LJF {
#[serde(deserialize_with = "de_hex2num")]
_0: $num_type,
#[serde(deserialize_with = "de_hex2num")]
_1: $num_type,
#[serde(deserialize_with = "de_hex2num")]
_2: $num_type,
#[serde(deserialize_with = "de_hex2num")]
_3: u32,
}
assert_eq!(
serde_json::from_str::<LJF>(
r#"{
"_0": "0x5",
"_1": "0x2",
"_2": "0x0",
"_3": "0x522"
}"#
),
Ok(LJF { _0: 5, _1: 2, _2: 0, _3: 1314 })
);
assert_eq!(
serde_json::from_str::<LJF>(
r#"{
"_0": "5",
"_1": "2",
"_2": "0",
"_3": "522"
}"#
),
Ok(LJF { _0: 5, _1: 2, _2: 0, _3: 1314 })
);
}};
}
assert_de_hex2num!(isize);
assert_de_hex2num!(i8);
assert_de_hex2num!(i16);
assert_de_hex2num!(i32);
assert_de_hex2num!(i64);
assert_de_hex2num!(i128);
assert_de_hex2num!(usize);
assert_de_hex2num!(u8);
assert_de_hex2num!(u16);
assert_de_hex2num!(u32);
assert_de_hex2num!(u64);
assert_de_hex2num!(u128);
}
#[cfg(feature = "serde")]
#[test]
fn de_hex2bytes_should_work() {
#[derive(Debug, PartialEq, Deserialize)]
struct LJF {
#[serde(deserialize_with = "de_hex2bytes")]
ljf: Bytes,
}
assert_eq!(
serde_json::from_str::<LJF>(
r#"{
"ljf": "0x4c6f7665204a616e6520466f7265766572"
}"#
),
Ok(LJF { ljf: (*b"Love Jane Forever").to_vec() })
);
assert_eq!(
serde_json::from_str::<LJF>(
r#"{
"ljf": "4c6f7665204a616e6520466f7265766572"
}"#
),
Ok(LJF { ljf: (*b"Love Jane Forever").to_vec() })
);
}
}