#![no_std]
use core::{
cmp,
mem::{self, MaybeUninit},
ops, slice, str,
};
#[derive(Clone, Copy, Debug)]
pub struct ArraySlice<const N: usize> {
buf: [MaybeUninit<u8>; N],
len: usize,
}
impl<const N: usize> ArraySlice<N> {
pub const fn from_array(array: [u8; N]) -> Self {
ArraySlice::from_bytes(&array)
}
pub const fn from_bytes(slice: &[u8]) -> Self {
if slice.len() > N {
panic!("length of slice exceeds capacity of ArraySlice");
}
let mut buf = [MaybeUninit::uninit(); N];
let mut i = 0;
while i < slice.len() {
buf[i] = MaybeUninit::new(slice[i]);
i += 1;
}
Self {
buf,
len: slice.len(),
}
}
pub const fn with_bytes(self, other: &[u8]) -> Self {
if self.len() + other.len() > N {
panic!("length of slice exceeds remaining space in ArraySlice");
}
let x = self.len();
let mut buf = *self.buf();
let mut i = 0;
while i < other.len() {
buf[x + i] = MaybeUninit::new(other[i]);
i += 1;
}
Self {
buf,
len: other.len() + self.len(),
}
}
#[inline(always)]
pub const fn buf(&self) -> &[MaybeUninit<u8>; N] {
&self.buf
}
#[inline(always)]
pub const fn len(&self) -> usize {
self.len
}
#[inline(always)]
pub const fn remaining(&self) -> usize {
N - self.len
}
#[inline(always)]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
pub const fn as_bytes(&self) -> &[u8] {
unsafe { slice::from_raw_parts(mem::transmute(self.buf().as_ptr()), self.len()) }
}
}
impl<const N: usize> ops::Deref for ArraySlice<N> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_bytes()
}
}
impl<const N: usize, const M: usize> cmp::PartialEq<ArraySlice<M>> for ArraySlice<N> {
fn eq(&self, other: &ArraySlice<M>) -> bool {
self.as_bytes() == other.as_bytes()
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ConstString<const N: usize>(ArraySlice<N>);
impl<const N: usize> ConstString<N> {
pub const unsafe fn from_bytes(slice: &[u8]) -> Self {
Self(ArraySlice::from_bytes(slice))
}
#[inline(always)]
pub const fn from_str(string: &str) -> Self {
unsafe { Self::from_bytes(string.as_bytes()) }
}
pub const unsafe fn with_bytes(self, other: &[u8]) -> Self {
Self(self.0.with_bytes(other))
}
#[inline(always)]
pub const fn with_str(self, other: &str) -> Self {
unsafe { self.with_bytes(other.as_bytes()) }
}
#[inline(always)]
pub const fn with<const M: usize>(self, other: ConstString<M>) -> Self {
self.with_str(other.as_str())
}
pub const fn as_str(&self) -> &str {
unsafe { str::from_utf8_unchecked(self.0.as_bytes()) }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
const FIRST: &str = "mary had a";
const SECOND: &str = " little lamb.";
const BOTH: ConstString<32> = ConstString::from_str(FIRST).with_str(SECOND);
assert_eq!(BOTH, ConstString::from_str("mary had a little lamb."));
}
}