$doc_hide
pub mod fixed_str$len {
extern crate alloc;
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt::{Debug, Display, Formatter, Result as FmtResult, Write};
use core::ops::{self, Deref, DerefMut, Index, IndexMut, Add, AddAssign};
use core::convert::{AsRef, AsMut};
use core::hash::{Hasher, Hash};
use core::borrow::{Borrow, BorrowMut};
use alloc::borrow::Cow;
use core::default::Default;
use core::cmp::Ordering;
use core::str::{Utf8Error, FromStr};
use core::iter::FromIterator;
/// A smart pointer to str with a fixed length of $len,which skip zeroes at the end in the deref,
/// in more performance sensitive situations use the non-zero variant.
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct FixedStr$len {
array: [u8; $len],
}
impl FixedStr$len {
/// Creates an FixedStr$len from an array,returning an error at invalid utf8.
#[inline]
pub fn new(array: [u8; $len]) -> Result<Self, Utf8Error> {
let mut index = 0;
for (i, e) in (&array[..]).iter().rev().enumerate() {
if *e != 0 {
index = $len - i;
break;
}
}
core::str::from_utf8(&array[..index])?;
// this validates the utf8 bytes dropping the resulting str
Ok(Self { array })
}
/// Creates an FixedStr$len without checking if the bytes are valid utf8.
///
/// # Safety
///
/// Ensure to only use this method with valid utf8.
#[inline]
pub const unsafe fn new_unchecked(array: [u8; $len]) -> Self {
Self { array }
}
/// Borrow the internal array as an slice.
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.array[..]
}
/// Borrow the internal array as a mutable slice.
///
/// # Safety
///
/// This is unsafe due to allow modifications that can produce invalid utf8.
#[inline]
pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
&mut self.array[..]
}
/// Consumes and returns the underlying array of bytes utf8 encoded.
#[inline]
pub const fn into_bytes(self) -> [u8; $len] {
self.array
}
/// Fill the last spaces in zero of the buffer with a determinated character.Useful when constructing the array with all bytes in
/// zero and then filling them in an incremental way.
///
/// # Panics
///
/// This will panic on debug if the zero spaces are not sufficient for the non-zero spaces of the character interpreted as
/// `\[u8; 4\]`.
#[inline]
pub fn fill_char(&mut self, character: char) {
let position = self.array.iter().position(|e| *e == 0u8).unwrap_or($len);
let character = unsafe { core::mem::transmute::<char, [u8; 4]>(character) };
// this comprobes that are suficient zero members of self for fill it with character,
// otherwise this might lead to append the unexpected value.
debug_assert!((&self.array[position..]).len() >= character.iter()
.position(|e| *e == 0u8)
.unwrap_or(4));
for (e, c) in self.array[position..].iter_mut().zip(character.iter()) {
*e = *c;
}
}
/// Fill the last spaces in zero of the buffer with a determinated string.Useful when constructing the array with all bytes in
/// zero and then filling them in an incremental way.
///
/// # Panics
///
/// This will panic on debug if the zero spaces are not sufficient for the non-zero spaces of the string interpreted as
/// `&\[u8\]`.
#[inline]
pub fn fill_str<'a, T: Borrow<str> + ?Sized + 'a>(&mut self, s: &'a T) {
let s = (*s).borrow();
let position = self.array.iter().position(|e| *e == 0u8).unwrap_or($len);
// this comprobes that are suficient zero members of self for fill it with s,otherwise this
// might lead to append the unexpected value.
debug_assert!((&self.array[position..]).len() >= s.as_bytes()
.iter()
.position(|e| *e == 0u8)
.unwrap_or(s.len()));
for (e, c) in self.array[position..].iter_mut().zip(s.as_bytes().iter()) {
*e = *c;
}
}
/// Checks that all the bytes are not zero,so no one is skipped at the deref.
#[inline]
pub fn is_full(&self) -> bool {
self.array.iter().all(|e| *e != 0u8)
}
/// Checks that all the bytes are in zero,so they are skipped at the deref.
#[inline]
pub fn is_empty(&self) -> bool {
self.array.iter().all(|e| *e == 0u8)
}
/// Gets the number of elements that are behind the first zero,because those are included in the deref,this function
/// has the same effect of `self.deref().len()` but does not do a transmute.
#[inline]
pub fn len(&self) -> usize {
let mut len = $len;
let mut oindex = 0;
let mut bindex = $len-1;
while oindex != $half_len_rounded_up {
unsafe {
if *self.array.get_unchecked(oindex) == 0 {
len = oindex;
break;
}
if *self.array.get_unchecked(bindex) != 0 {
len = bindex+1;
break;
}
}
oindex += 1;
bindex -= 1;
}
len
}
/// Convert the FixedStr$len into a vector of bytes.
#[inline]
pub fn into_vec(self) -> Vec<u8> {
let mut buf = Vec::with_capacity($len);
unsafe { self.array.as_ptr().copy_to(buf.as_mut_ptr(), $len); buf.set_len($len) }
buf
}
/// Turn the FixedStr$len into a string,moving the bytes.
#[inline]
pub fn into_string(self) -> String {
let mut vec = core::mem::ManuallyDrop::new(self.into_vec());
unsafe { String::from_raw_parts(vec.as_mut_ptr(), vec.len(), vec.capacity()) }
}
/// Construct a FixedStr$len from bytes,without checking if it has length $len.
///
/// # Safety
///
/// This will trigger UB on slice's with length different than $len.
pub unsafe fn from_bytes_unchecked(s: &[u8]) -> Self {
Self::new_unchecked(*core::mem::transmute_copy::<&'_ [u8], &'_ [u8; $len]>(&s))
}
/// Construct a FixedStr$len from a str,without checking if it has length $len.
///
/// # Safety
///
/// This will trigger UB on str's with length different than $len.
#[inline]
pub unsafe fn from_str_unchecked<T: Borrow<str> + ?Sized>(s: &T) -> FixedStr$len {
Self::from_bytes_unchecked((*s).borrow().as_bytes())
}
}
impl Default for FixedStr$len {
/// The principal responsible of not using the incomplete feature [`const_generics`],a conveniency
/// for `Self::new_unchecked([0; $len])`,zero it is not utf8 but this is safe because deref skips
/// all zeroes onwards the last non-zero byte.
fn default() -> Self {
Self { array: [0; $len] }
}
}
impl Display for FixedStr$len {
fn fmt(&self, f: &'_ mut Formatter) -> FmtResult {
write!(f, "{}", self.deref())
}
}
impl Debug for FixedStr$len {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
write!(f, "{:?}", self.deref())
}
}
impl Deref for FixedStr$len {
type Target = str;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe {
core::str::from_utf8_unchecked(&self.array[..self.len()])
}
}
}
impl DerefMut for FixedStr$len {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe {
let a = self.len();
core::str::from_utf8_unchecked_mut(&mut self.array[..a])
}
}
}
impl AsRef<str> for FixedStr$len {
fn as_ref(&self) -> &str {
self.deref()
}
}
impl AsMut<str> for FixedStr$len {
fn as_mut(&mut self) -> &mut str {
self.deref_mut()
}
}
impl From<&[u8]> for FixedStr$len {
/// Construct a FixedStr$len from bytes,if it is greater it take $len bytes,if it is smaller it
/// will leave the remanining spaces of the FixedStr$len in zero.
///
/// # Panics
///
/// This will panic if the length of `s` is greater than $len on debug and always at invalid utf8.
#[inline]
fn from(s: &[u8]) -> Self {
macro_rules! foo {
($s:expr) => {
core::str::from_utf8($s)
.expect("slice had invalid utf8 when trying to convert to FixedStr$len")
};
}
if s.len() == $len {
unsafe {
Self::from_str_unchecked(foo!(s))
}
} else if s.len() < $len {
let mut fixed_str = FixedStr$len::default();
fixed_str.fill_str(foo!(s));
fixed_str
} else if cfg!(debug_assertions) {
panic!("the length of the string was greater than $len on debug")
} else {
unsafe {
Self::from_str_unchecked(&foo!(s)[..$len])
}
}
}
}
impl From<&str> for FixedStr$len {
/// Construct a FixedStr$len from a str,if it is greater it take $len bytes,if it is smaller it
/// will leave the remanining spaces of the FixedStr$len in zero.
///
/// # Panics
///
/// This will panic if the length of `s` is greater than $len on debug.
#[inline]
fn from(s: &str) -> Self {
if s.len() == $len {
unsafe {
Self::from_str_unchecked(s)
}
} else if s.len() < $len {
let mut fixed_str = FixedStr$len::default();
fixed_str.fill_str(s);
fixed_str
} else if cfg!(debug_assertions) {
panic!("the length of the string was greater than $len on debug")
} else {
unsafe {
Self::from_str_unchecked(&s[..$len])
}
}
}
}
impl From<[u8; $len]> for FixedStr$len {
fn from(a: [u8; $len]) -> Self {
Self::new(a).expect("Array of $len has invalid utf8.")
}
}
impl Hash for FixedStr$len {
fn hash<H: Hasher>(&self, state: &mut H) {
self.deref().hash(state)
}
}
impl Borrow<str> for FixedStr$len {
fn borrow(&self) -> &str {
self.deref()
}
}
impl BorrowMut<str> for FixedStr$len {
fn borrow_mut(&mut self) -> &mut str {
self.deref_mut()
}
}
impl<T: Borrow<str> + ?Sized> PartialOrd<T> for FixedStr$len {
fn partial_cmp(&self, other: &T) -> Option<Ordering> {
self.deref().partial_cmp((*other).borrow())
}
}
impl Ord for FixedStr$len {
fn cmp(&self, other: &Self) -> Ordering {
self.deref().cmp(other.deref())
}
}
// implementations "borrowed" from the std
impl ops::Index<ops::Range<usize>> for FixedStr$len {
type Output = str;
#[inline]
fn index(&self, index: ops::Range<usize>) -> &str {
&self[..][index]
}
}
impl ops::Index<ops::RangeTo<usize>> for FixedStr$len {
type Output = str;
#[inline]
fn index(&self, index: ops::RangeTo<usize>) -> &str {
&self[..][index]
}
}
impl ops::Index<ops::RangeFrom<usize>> for FixedStr$len {
type Output = str;
#[inline]
fn index(&self, index: ops::RangeFrom<usize>) -> &str {
&self[..][index]
}
}
impl ops::Index<ops::RangeFull> for FixedStr$len {
type Output = str;
#[inline]
fn index(&self, _: ops::RangeFull) -> &str {
self.deref()
}
}
impl ops::Index<ops::RangeInclusive<usize>> for FixedStr$len {
type Output = str;
#[inline]
fn index(&self, index: ops::RangeInclusive<usize>) -> &str {
Index::index(self.deref(), index)
}
}
impl ops::Index<ops::RangeToInclusive<usize>> for FixedStr$len {
type Output = str;
#[inline]
fn index(&self, index: ops::RangeToInclusive<usize>) -> &str {
Index::index(self.deref(), index)
}
}
impl ops::IndexMut<ops::Range<usize>> for FixedStr$len {
#[inline]
fn index_mut(&mut self, index: ops::Range<usize>) -> &mut str {
&mut self[..][index]
}
}
impl ops::IndexMut<ops::RangeTo<usize>> for FixedStr$len {
#[inline]
fn index_mut(&mut self, index: ops::RangeTo<usize>) -> &mut str {
&mut self[..][index]
}
}
impl ops::IndexMut<ops::RangeFrom<usize>> for FixedStr$len {
#[inline]
fn index_mut(&mut self, index: ops::RangeFrom<usize>) -> &mut str {
&mut self[..][index]
}
}
impl ops::IndexMut<ops::RangeFull> for FixedStr$len {
#[inline]
fn index_mut(&mut self, _: ops::RangeFull) -> &mut str {
self.deref_mut()
}
}
impl ops::IndexMut<ops::RangeInclusive<usize>> for FixedStr$len {
#[inline]
fn index_mut(&mut self, index: ops::RangeInclusive<usize>) -> &mut str {
IndexMut::index_mut(self.deref_mut(), index)
}
}
impl ops::IndexMut<ops::RangeToInclusive<usize>> for FixedStr$len {
#[inline]
fn index_mut(&mut self, index: ops::RangeToInclusive<usize>) -> &mut str {
IndexMut::index_mut(self.deref_mut(), index)
}
}
impl Eq for FixedStr$len {}
impl<T: Borrow<str> + ?Sized> PartialEq<T> for FixedStr$len {
#[inline]
fn eq<'a>(&self, other: &'a T) -> bool { PartialEq::eq(&self[..], (*other).borrow()) }
#[inline]
fn ne<'a>(&self, other: &'a T) -> bool { PartialEq::ne(&self[..], (*other).borrow()) }
}
/// Fill the zero bytes onwards the end with a given string,then returns itself.
impl<'a, T: Borrow<str> + ?Sized + 'a> Add<&'a T> for FixedStr$len {
type Output = Self;
#[inline]
fn add(mut self, other: &'a T) -> Self {
self.fill_str(other);
self
}
}
/// Fill the zero bytes onwards the end with a given string.
impl<'a, T: Borrow<str> + ?Sized + 'a> AddAssign<&'a T> for FixedStr$len {
#[inline]
fn add_assign(&mut self, other: &'a T) {
self.fill_str(other);
}
}
/// Fill the zero bytes onwards the end with a given char,then returns itself.
impl Add<char> for FixedStr$len {
type Output = Self;
#[inline]
fn add(mut self, other: char) -> Self {
self.fill_char(other);
self
}
}
/// Fill the zero bytes onwards the end with a given char.
impl AddAssign<char> for FixedStr$len {
#[inline]
fn add_assign(&mut self, other: char) {
self.fill_char(other);
}
}
/// Fill the zero spaces onwards the end with the items of an iterator,doing nothing when
/// there are no zero bytes onwards the end to replace.
impl<'a, T: Borrow<str> + ?Sized + 'a> Extend<&'a T> for FixedStr$len {
fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
iter.into_iter().for_each(move |s| self.fill_str(s));
}
}
/// Creates a FixedStr$len from an iterator of strings,doing nothing when
/// there are no zero bytes onwards the end to replace.
impl<'a, T: Borrow<str> + ?Sized + 'a> FromIterator<&'a T> for FixedStr$len {
fn from_iter<I: IntoIterator<Item = &'a T>>(iter: I) -> Self {
let mut buf = FixedStr$len::default();
buf.extend(iter);
buf
}
}
impl FromStr for FixedStr$len {
type Err = core::convert::Infallible;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(s.into())
}
}
/// Implementation needed for use the macro [`write!`],it stop writing when
/// there are no zero bytes onwards the end to replace.
impl Write for FixedStr$len {
#[inline]
fn write_str(&mut self, s: &str) -> FmtResult {
Ok(self.fill_str(s))
}
#[inline]
fn write_char(&mut self, c: char) -> FmtResult {
Ok(self.fill_char(c))
}
}