btt/
lib.rs

1#![no_std]
2
3#[cfg(feature = "alloc")]
4extern crate alloc;
5
6use core::{
7	fmt::{self, Debug},
8	str
9};
10
11pub mod base85;
12pub mod hex;
13
14pub trait Encoder<const LEN: usize>: Decoder<LEN> {
15	fn alphabet(&self) -> Alphabet<'_, LEN>;
16	fn encoded_len(&self, len: usize) -> Option<usize>;
17	fn encode_into<'a>(&self, dst: &'a mut [u8], src: &[u8]) -> &'a str;
18
19	#[cfg(feature = "alloc")]
20	#[track_caller]
21	fn encode(&self, src: &[u8]) -> alloc::string::String {
22		let mut dst = alloc::vec![0; self.encoded_len(src.len()).unwrap()];
23		let s = self.encode_into(&mut dst, src);
24		debug_assert!(s.len() == dst.len());
25		unsafe { alloc::string::String::from_utf8_unchecked(dst) }
26	}
27}
28
29pub trait Decoder<const LEN: usize> {
30	type Error: From<InvalidLength>;
31
32	fn decoded_len(&self, len: usize) -> Result<usize, InvalidLength>;
33	fn decode_into<'a>(&self, dst: &'a mut [u8], src: &[u8]) -> Result<&'a [u8], Self::Error>;
34
35	#[cfg(feature = "alloc")]
36	#[track_caller]
37	fn decode(&self, src: &[u8]) -> Result<alloc::vec::Vec<u8>, Self::Error> {
38		let mut dst = alloc::vec![0; self.decoded_len(src.len())?];
39		let s = self.decode_into(&mut dst, src)?;
40		debug_assert!(s.len() == dst.len());
41		Ok(dst)
42	}
43}
44
45#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
46#[repr(transparent)]
47pub struct Alphabet<'a, const LEN: usize>(&'a [u8; LEN]);
48
49impl<'a, const LEN: usize> Alphabet<'a, LEN> {
50	#[track_caller]
51	pub const fn new(alphabet: &'a [u8; LEN]) -> Result<Self, AlphabetError> {
52		let mut i = 0;
53		while i < alphabet.len() {
54			if !alphabet[i].is_ascii() {
55				return Err(AlphabetError::NonAscii(alphabet[i]));
56			}
57
58			let mut j = 0;
59			while j < i {
60				if alphabet[i] == alphabet[j] {
61					return Err(AlphabetError::Duplicate {
62						character: alphabet[i],
63						first: j,
64						second: i
65					});
66				}
67
68				j += 1;
69			}
70
71			i += 1;
72		}
73
74		Ok(Self(alphabet))
75	}
76
77	#[allow(clippy::missing_safety_doc)]
78	#[track_caller]
79	pub const unsafe fn new_unchecked(alphabet: &'a [u8; LEN]) -> Self {
80		debug_assert!(Self::new(alphabet).is_ok());
81		Self(alphabet)
82	}
83
84	pub const fn as_bytes(&self) -> &'a [u8; LEN] {
85		self.0
86	}
87
88	pub const fn as_str(&self) -> &'a str {
89		unsafe { str::from_utf8_unchecked(self.0) }
90	}
91}
92
93impl<const LEN: usize> Debug for Alphabet<'_, LEN> {
94	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95		write!(f, "Alphabet<{LEN}>({})", self.as_str().escape_debug())
96	}
97}
98
99#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
100pub struct InvalidLength;
101
102#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
103pub enum AlphabetError {
104	NonAscii(u8),
105	Duplicate { character: u8, first: usize, second: usize }
106}
107
108#[cfg(test)]
109mod tests {
110	use super::*;
111
112	#[test]
113	fn test() {
114		assert_eq!(Alphabet::new(b"").unwrap().as_str(), "");
115		assert_eq!(Alphabet::new(b"123").unwrap().as_str(), "123");
116		assert_eq!(Alphabet::new(b"\0\n\t\x7f").unwrap().as_str(), "\0\n\t\x7f");
117		assert_eq!(Alphabet::new(b"\x80"), Err(AlphabetError::NonAscii(0x80)));
118
119		assert_eq!(
120			Alphabet::new(b"aa"),
121			Err(AlphabetError::Duplicate { character: b'a', first: 0, second: 1 })
122		);
123	}
124}