#![no_std]
#![warn(missing_docs)]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::style))]
use core::{fmt, mem};
mod pair;
pub use pair::CharPair;
mod arch;
mod const_fn;
pub use const_fn::*;
#[cfg(feature = "alloc")]
mod alloc;
#[cfg(feature = "alloc")]
pub use alloc::*;
type CharTable = &'static [u8; 16];
const CHAR_TABLE_LOWER: CharTable = b"0123456789abcdef";
const CHAR_TABLE_UPPER: CharTable = b"0123456789ABCDEF";
pub const fn required_encode_len(len: usize) -> usize {
len.saturating_mul(2)
}
pub const fn required_decode_len(len: usize) -> usize {
len.saturating_div(2)
}
#[inline(always)]
pub fn hex_upper(input: &[u8], out: &mut [mem::MaybeUninit<u8>]) -> usize {
arch::hex(CHAR_TABLE_UPPER, input, out)
}
#[inline(always)]
pub fn hex_lower(input: &[u8], out: &mut [mem::MaybeUninit<u8>]) -> usize {
arch::hex(CHAR_TABLE_LOWER, input, out)
}
#[inline(always)]
pub fn unhex(input: &[u8], out: &mut [mem::MaybeUninit<u8>]) -> Result<usize, DecodeError> {
arch::unhex(input, out)
}
#[derive(Debug, Copy, Clone)]
pub enum DecodeError {
InvalidChar(u8)
}
impl DecodeError {
#[cold]
#[inline(never)]
pub(crate) const fn unexpected_char(ch: u8) -> Self {
Self::InvalidChar(ch)
}
}
pub struct Encoder<'a> {
table: CharTable,
data: &'a [u8],
}
impl<'a> Encoder<'a> {
#[inline(always)]
pub const fn upper(data: &'a [u8]) -> Self {
Self {
table: CHAR_TABLE_UPPER,
data,
}
}
#[inline(always)]
pub const fn lower(data: &'a [u8]) -> Self {
Self {
table: CHAR_TABLE_LOWER,
data,
}
}
#[inline(always)]
pub fn next_byte(&mut self) -> Option<CharPair> {
match self.data.split_first() {
Some((byt, rest)) => {
self.data = rest;
Some(dec2hex(self.table, *byt))
},
None => None
}
}
}
impl Iterator for Encoder<'_> {
type Item = CharPair;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
self.next_byte()
}
#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.data.len(), Some(self.data.len()))
}
}
impl ExactSizeIterator for Encoder<'_> {
#[inline(always)]
fn len(&self) -> usize {
self.data.len()
}
}
impl fmt::Display for Encoder<'_> {
#[inline(always)]
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
for byt in self.data {
fmt.write_str(dec2hex(self.table, *byt).as_str())?;
}
Ok(())
}
}
impl fmt::Debug for Encoder<'_> {
#[inline(always)]
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.write_str("\"")?;
for byt in self.data {
fmt.write_str(dec2hex(self.table, *byt).as_str())?;
}
fmt.write_str("\"")
}
}
pub struct Decoder<'a>(&'a [u8]);
impl<'a> Decoder<'a> {
#[inline(always)]
pub const fn new(data: &'a str) -> Option<Self> {
if data.len() % 2 != 0 {
None
} else {
Some(Self(data.as_bytes()))
}
}
#[inline]
fn inner_next_byte(&mut self) -> Result<u8, DecodeError> {
let chunk = unsafe {
*(self.0.as_ptr() as *const [u8; 2])
};
self.0 = &self.0[2..];
unhex_pair(chunk)
}
#[inline]
pub fn next_byte(&mut self) -> Option<Result<u8, DecodeError>> {
if self.0.is_empty() {
return None;
}
Some(self.inner_next_byte())
}
}
impl Iterator for Decoder<'_> {
type Item = Result<u8, DecodeError>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.next_byte()
}
#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.0.len(), Some(self.0.len()))
}
}
impl ExactSizeIterator for Decoder<'_> {
#[inline(always)]
fn len(&self) -> usize {
self.0.len()
}
}