use crate::{
chunked::{ChunkedDecoder, ChunkedEncoder, ChunkedEncoderImpl},
padding::Unpadded
};
const DECODING_TABLE: &DecodingTable<16> = unsafe {
&Alphabet::new_unchecked(b"0123456789abcdef")
.decoding_table()
.merge(&Alphabet::new_unchecked(b"0123456789ABCDEF").decoding_table())
.unwrap()
};
type HexEncoderImpl<A, D, P> = ChunkedEncoder<A, D, P, HexImpl, 16, 1, 2>;
type HexDecoderImpl<D, P> = ChunkedDecoder<D, P, HexImpl, 16, 1, 2>;
impl_encoding!(HexEncoder, HexDecoder, 16, HexEncoderImpl, HexDecoderImpl);
impl_encoder!(HexLower, 16, b"0123456789abcdef", DECODING_TABLE, Unpadded);
impl_encoder!(HexUpper, 16, b"0123456789ABCDEF", DECODING_TABLE, Unpadded);
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
struct HexImpl;
impl ChunkedEncoderImpl<1, 2> for HexImpl {
#[inline]
fn padding_len(len: usize) -> usize {
debug_assert!(len.is_multiple_of(2));
0
}
#[inline]
fn encoded_len(len: usize, padded: bool) -> Option<usize> {
debug_assert!(!padded);
len.checked_mul(2)
}
#[inline]
fn decoded_len(src: &[u8], padding: Option<u8>) -> Option<usize> {
debug_assert!(padding.is_none());
src.len().is_multiple_of(2).then_some(src.len() / 2)
}
#[inline]
fn encode_chunk_raw(src: &[u8; 1], dst: &mut [u8; 2]) {
dst[0] = src[0] >> 4;
dst[1] = src[0] & 0xf;
}
#[inline]
fn decode_raw_chunk(src: &[u8; 2], dst: &mut [u8; 1]) -> Result<(), ()> {
dst[0] = (src[0] << 4) | src[1];
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[track_caller]
fn test(src: impl AsRef<[u8]>, expected: &str) {
let mut buf = [0; 64];
assert_eq!(HexLower.encode_into(src.as_ref(), &mut buf).unwrap(), expected);
assert_eq!(
HexLower.decode_into(expected.to_ascii_uppercase().as_bytes(), &mut buf).unwrap(),
src.as_ref()
);
}
#[test]
fn fhfg() {
test([], "");
test([0x5f], "5f");
test([0, 1, 2, 3, 4, 5, 6, 7], "0001020304050607");
}
#[test]
fn rfc4648() {
test("", "");
test("f", "66");
test("fo", "666f");
test("foo", "666f6f");
test("foob", "666f6f62");
test("fooba", "666f6f6261");
test("foobar", "666f6f626172");
}
}