btt 0.5.0

Binary to text encodings
Documentation
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");
	}

	// https://datatracker.ietf.org/doc/html/rfc4648#section-10
	#[test]
	fn rfc4648() {
		test("", "");
		test("f", "66");
		test("fo", "666f");
		test("foo", "666f6f");
		test("foob", "666f6f62");
		test("fooba", "666f6f6261");
		test("foobar", "666f6f626172");
	}
}