use core::{
fmt::{Debug, Display},
ops::Deref,
};
pub const MAX_CONST_STRING_LENGTH: usize = 1024;
#[derive(Clone)]
pub struct ConstString {
data: [u8; MAX_CONST_STRING_LENGTH],
len: usize,
}
const fn memcpy<const N: usize>(
mut source: &[u8],
mut dest: [u8; N],
mut offset: usize,
) -> [u8; N] {
if offset > dest.len() {
panic!("out-of-bounds memcpy");
}
while !source.is_empty() {
dest[offset] = source[0];
offset += 1;
(_, source) = source.split_at(1);
}
dest
}
impl ConstString {
pub const fn new(s: &str) -> ConstString {
let mut data = [0u8; MAX_CONST_STRING_LENGTH];
data = memcpy(s.as_bytes(), data, 0);
ConstString { data, len: s.len() }
}
pub const fn from_decimal_number(mut number: usize) -> ConstString {
let mut data = [0u8; MAX_CONST_STRING_LENGTH];
let digits = number.checked_ilog10();
let digits = match digits {
Some(digits) => digits as usize + 1,
None => 1,
};
if digits > MAX_CONST_STRING_LENGTH {
panic!("from_decimal_number: too many digits");
}
let mut position = digits;
while position > 0 {
position -= 1;
data[position] = b'0' + (number % 10) as u8;
number /= 10;
}
Self { data, len: digits }
}
pub const fn select(cond: bool, true_value: &str, false_value: &str) -> Self {
match cond {
true => Self::new(true_value),
false => Self::new(false_value),
}
}
pub const fn const_clone(&self) -> Self {
Self {
data: self.data,
len: self.len,
}
}
pub const fn concat(&self, other: ConstString) -> ConstString {
let mut new = self.const_clone();
new.data = memcpy(other.as_bytes(), new.data, self.len);
new.len += other.len;
new
}
pub const fn as_bytes(&self) -> &[u8] {
self.data.split_at(self.len).0
}
pub const fn as_str(&self) -> &str {
unsafe { core::str::from_utf8_unchecked(self.as_bytes()) }
}
}
impl Deref for ConstString {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_bytes()
}
}
impl Display for ConstString {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl Debug for ConstString {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:?}", self.as_str())
}
}
#[test]
fn test_from_decimal() {
for i in (0..=100).chain(1000..=1001) {
assert_eq!(ConstString::from_decimal_number(i).as_str(), i.to_string());
}
}