use crate::data::array_types::{self, make_lowercase, position};
use std::ops::{Index, RangeInclusive};
#[repr(transparent)]
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct ByteMap([u8; 256]);
impl Default for ByteMap {
#[inline]
fn default() -> Self {
Self([0; 256])
}
}
impl ByteMap {
#[inline]
#[must_use]
pub const fn new(mapping: [u8; 256]) -> Self {
Self(mapping)
}
#[inline]
#[must_use]
pub const fn all(dest: u8) -> Self {
Self([dest; 256])
}
#[must_use]
pub const fn identity() -> Self {
let mut mapping = [0; 256];
let mut i = 0u8;
loop {
mapping[i as usize] = i;
if i == u8::MAX {
return Self(mapping);
}
i += 1;
}
}
#[inline]
#[must_use]
pub const fn into_inner(self) -> [u8; 256] {
self.0
}
#[must_use]
pub const fn preserve<const S: usize>(self, bytes: &[u8; S]) -> Self {
self.map(bytes, bytes)
}
#[must_use]
pub const fn map_to_one<const S: usize>(mut self, from: &[u8; S], to: u8) -> Self {
Self::assert_unique(from);
let mut i = 0;
while i < S {
self.set_byte(from[i], to);
i += 1;
}
self
}
#[must_use]
pub const fn map<const S: usize>(mut self, from: &[u8; S], to: &[u8; S]) -> Self {
Self::assert_unique(from);
let mut i = 0;
while i < S {
self.set_byte(from[i], to[i]);
i += 1;
}
self
}
#[must_use]
pub const fn preserve_range(self, range: RangeInclusive<u8>) -> Self {
self.map_range(*range.start()..=*range.end(), range)
}
#[must_use]
#[allow(clippy::cast_possible_truncation)]
pub const fn map_range_to_one(mut self, from: RangeInclusive<u8>, to: u8) -> Self {
let from = *from.start() as usize..*from.end() as usize + 1;
let mut src = from.start;
while src < from.end {
self.set_byte(src as u8, to);
src += 1;
}
self
}
#[must_use]
#[allow(clippy::cast_possible_truncation)]
pub const fn map_range(mut self, from: RangeInclusive<u8>, to: RangeInclusive<u8>) -> Self {
let from_len = match from.end().checked_sub(*from.start()) {
Some(diff) => diff as usize + 1,
None => 0,
};
let to_len = match to.end().checked_sub(*to.start()) {
Some(diff) => diff as usize + 1,
None => 0,
};
assert!(from_len == to_len, "Attempted to map to a range of a different length!");
let from = *from.start() as usize..*from.end() as usize + 1;
let mut src = from.start;
while src < from.end {
let dest = *to.start() as usize + src - from.start;
self.set_byte(src as u8, dest as u8);
src += 1;
}
self
}
#[allow(clippy::cast_possible_truncation)]
#[must_use]
pub(crate) const fn indexing<const S: usize>(mut self, bytes: &[u8; S]) -> Self {
assert!(S <= 256, "Cannot index more than 256 distinct bytes!");
Self::assert_unique(bytes);
let mut i = 0;
while i < S {
self.set_byte(bytes[i], i as u8);
i += 1;
}
self
}
#[inline]
pub(crate) const fn set_byte(&mut self, src: u8, dest: u8) {
self.0[src as usize] = dest;
}
#[inline]
const fn assert_unique<const S: usize>(bytes: &[u8; S]) {
assert!(
array_types::is_unique(bytes),
"Attempted to map a byte multiple times in the same call!"
);
}
#[inline]
#[must_use]
const fn copy_mapped_val(&self, b: u8) -> u8 {
self.0[b as usize]
}
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct ByteIndexMap<const S: usize> {
index_map: ByteMap,
byte_keys: [u8; S],
}
impl<const S: usize> ByteIndexMap<S> {
#[allow(clippy::cast_possible_truncation)]
#[must_use]
pub const fn new(byte_keys: [u8; S], catch_all: u8) -> Self {
let catch_all_index =
position(&byte_keys, catch_all).expect("The catch_all must be present in the byte_keys.") as u8;
Self {
index_map: ByteMap::all(catch_all_index).indexing(&byte_keys),
byte_keys,
}
}
#[allow(clippy::cast_possible_truncation)]
#[must_use]
pub const fn new_ignoring_case(mut byte_keys: [u8; S], catch_all: u8) -> Self {
byte_keys = array_types::make_uppercase(&byte_keys);
let catch_all_index = position(&byte_keys, catch_all.to_ascii_uppercase())
.expect("The catch_all must be present in the byte_keys.") as u8;
Self {
index_map: ByteMap::all(catch_all_index)
.indexing(&byte_keys)
.indexing(&make_lowercase(&byte_keys)),
byte_keys,
}
}
#[inline]
#[must_use]
pub const fn add_synonym(mut self, new_key: u8, previous_key: u8) -> Self {
self.index_map.set_byte(new_key, self.index_map.copy_mapped_val(previous_key));
self
}
#[inline]
pub(crate) const fn set_byte_ignoring_case(&mut self, byte: u8, index: u8) {
self.index_map.set_byte(byte.to_ascii_lowercase(), index);
self.index_map.set_byte(byte.to_ascii_uppercase(), index);
}
#[inline]
#[must_use]
pub const fn add_synonym_ignore_case(mut self, new_key: u8, previous_key: u8) -> Self {
self.set_byte_ignoring_case(new_key, self.index_map.copy_mapped_val(previous_key));
self
}
#[inline]
#[must_use]
#[allow(clippy::len_without_is_empty)]
pub const fn len(&self) -> usize {
S
}
#[inline]
#[must_use]
pub const fn byte_keys(&self) -> &[u8; S] {
&self.byte_keys
}
#[inline]
#[must_use]
pub const fn to_index(&self, b: u8) -> usize {
self.index_map.0[b as usize] as usize
}
#[inline]
#[must_use]
pub const fn to_byte(&self, index: usize) -> u8 {
self.byte_keys[index]
}
#[inline]
#[must_use]
pub const fn in_byte_keys(&self, b: u8) -> bool {
b == self.to_byte(self.to_index(b))
}
#[inline]
#[must_use]
pub const fn as_map(&self) -> &ByteMap {
&self.index_map
}
}
impl Index<u8> for ByteMap {
type Output = u8;
#[inline]
fn index(&self, index: u8) -> &u8 {
&self.0[index as usize]
}
}
impl<const S: usize> Index<u8> for ByteIndexMap<S> {
type Output = u8;
#[inline]
fn index(&self, index: u8) -> &u8 {
&self.index_map[index]
}
}