use core::{
fmt::{self, Debug},
mem,
ops::Deref,
slice
};
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct Alphabet<const LEN: usize>([u8; LEN]);
impl<const LEN: usize> Alphabet<LEN> {
#[track_caller]
pub const fn new(alphabet: &[u8; LEN]) -> Result<&Self, AlphabetError> {
let mut i = 0;
while i < LEN {
if alphabet[i] >= 128 {
return Err(AlphabetError::NonAscii(alphabet[i]));
}
let mut j = 0;
while j < i {
if alphabet[i] == alphabet[j] {
return Err(AlphabetError::Duplicate {
character: alphabet[i],
first: j,
second: i
});
}
j += 1;
}
i += 1;
}
const {
assert!(LEN < 128);
}
#[allow(clippy::missing_transmute_annotations)]
Ok(unsafe { mem::transmute(alphabet) })
}
#[allow(clippy::missing_safety_doc)]
#[track_caller]
pub const unsafe fn new_unchecked(alphabet: &[u8; LEN]) -> &Self {
debug_assert!(Self::new(alphabet).is_ok());
unsafe { mem::transmute(alphabet) }
}
pub const fn as_dyn_alphabet(&self) -> &DynAlphabet {
unsafe { mem::transmute(slice::from_raw_parts(self.0.as_ptr(), LEN)) }
}
pub const fn as_bytes(&self) -> &[u8; LEN] {
&self.0
}
pub const fn as_str(&self) -> &str {
unsafe { str::from_utf8_unchecked(self.as_bytes()) }
}
pub const fn decoding_table(&self) -> DecodingTable<LEN> {
DecodingTable::from_alphabet(self)
}
}
impl<const LEN: usize> Debug for Alphabet<LEN> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Alphabet<{LEN}>({})", self.as_str().escape_debug())
}
}
impl<const LEN: usize> AsRef<Alphabet<LEN>> for Alphabet<LEN> {
fn as_ref(&self) -> &Alphabet<LEN> {
self
}
}
impl<const LEN: usize> AsRef<DynAlphabet> for Alphabet<LEN> {
fn as_ref(&self) -> &DynAlphabet {
self.as_dyn_alphabet()
}
}
impl<const LEN: usize> AsRef<[u8; LEN]> for Alphabet<LEN> {
fn as_ref(&self) -> &[u8; LEN] {
&self.0
}
}
impl<const LEN: usize> AsRef<[u8]> for Alphabet<LEN> {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl<const LEN: usize> Deref for Alphabet<LEN> {
type Target = [u8; LEN];
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct DynAlphabet([u8]);
impl DynAlphabet {
#[inline]
pub const fn as_bytes(&self) -> &[u8] {
&self.0
}
#[inline]
pub const fn as_str(&self) -> &str {
unsafe { str::from_utf8_unchecked(self.as_bytes()) }
}
}
impl Debug for DynAlphabet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Alphabet<{}>({})", self.0.len(), self.as_str().escape_debug())
}
}
impl AsRef<[u8]> for DynAlphabet {
#[inline]
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl Deref for DynAlphabet {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct DecodingTable<const LEN: usize>([u8; 256]);
impl<const LEN: usize> DecodingTable<LEN> {
#[inline]
pub const fn from_alphabet(alphabet: &Alphabet<LEN>) -> Self {
const {
assert!(LEN < 128);
}
let mut table = Self([0xff; 256]);
let mut i = 0;
while i < LEN {
let _ = table.set(alphabet.0[i], i as u8);
i += 1;
}
table
}
#[inline]
pub(crate) const fn from_slices(slices: &[&[u8]; LEN]) -> Option<Self> {
const {
assert!(LEN < 128);
}
let mut table = Self([0xff; 256]);
let mut i = 0;
while i < LEN {
let characters = slices[i];
if characters.is_empty() {
return None;
}
let mut j = 0;
while j < characters.len() {
let _ = table.set(characters[j], i as u8);
j += 1;
}
i += 1;
}
Some(table)
}
#[inline]
pub const fn merge(&self, other: &Self) -> Option<Self> {
let mut table = Self([0xff; 256]);
let mut i = 0;
while i < 256 {
let v = match (self.0[i], other.0[i]) {
(a, b) if a == b => a,
(a, 0xff) => a,
(0xff, b) => b,
_ => return None
};
if v != 0xff {
let _ = table.set(i as u8, v);
}
i += 1;
}
Some(table)
}
#[inline]
pub const fn as_bytes(&self) -> &[u8; 256] {
&self.0
}
pub const fn as_dyn_decoding_table(&self) -> &DynDecodingTable {
unsafe { mem::transmute(&self.0) }
}
#[inline]
pub fn decode(&self, character: u8) -> Option<u8> {
let b = self[character as usize];
if b != 0xff {
debug_assert!((b as usize) < LEN);
Some(b)
} else {
None
}
}
#[inline]
pub(crate) const fn check(&self, alphabet: &Alphabet<LEN>) -> bool {
let mut i = 0;
while i < LEN {
if self.0[alphabet.0[i] as usize] != i as u8 {
return false;
}
i += 1;
}
true
}
#[track_caller]
pub(crate) const fn set(&mut self, character: u8, value: u8) -> &mut Self {
assert!((value as usize) < LEN);
assert!(self.0[character as usize] == 0xff);
self.0[character as usize] = value;
self
}
}
impl<const LEN: usize> AsRef<DecodingTable<LEN>> for DecodingTable<LEN> {
fn as_ref(&self) -> &DecodingTable<LEN> {
self
}
}
impl<const LEN: usize> AsRef<DynDecodingTable> for DecodingTable<LEN> {
fn as_ref(&self) -> &DynDecodingTable {
self.as_dyn_decoding_table()
}
}
impl<const LEN: usize> AsRef<[u8; 256]> for DecodingTable<LEN> {
fn as_ref(&self) -> &[u8; 256] {
&self.0
}
}
impl<const LEN: usize> Deref for DecodingTable<LEN> {
type Target = [u8; 256];
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct DynDecodingTable([u8; 256]);
impl AsRef<[u8; 256]> for DynDecodingTable {
#[inline]
fn as_ref(&self) -> &[u8; 256] {
&self.0
}
}
impl Deref for DynDecodingTable {
type Target = [u8; 256];
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum AlphabetError {
NonAscii(u8),
Duplicate { character: u8, first: usize, second: usize }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn alphabet() {
assert_eq!(Alphabet::new(b"").unwrap().as_str(), "");
assert_eq!(Alphabet::new(b"123").unwrap().as_str(), "123");
assert_eq!(Alphabet::new(b"\0\n\t\x7f").unwrap().as_str(), "\0\n\t\x7f");
assert_eq!(Alphabet::new(b"\x80"), Err(AlphabetError::NonAscii(0x80)));
assert_eq!(
Alphabet::new(b"aa"),
Err(AlphabetError::Duplicate { character: b'a', first: 0, second: 1 })
);
}
}