// Copyright 2024 Gabriel Bjørnager Jensen. // // This file is part of bzipper. // // bzipper is free software: you can redistribute // it and/or modify it under the terms of the GNU // Lesser General Public License as published by // the Free Software Foundation, either version 3 // of the License, or (at your option) any later // version. // // bzipper is distributed in the hope that it will // be useful, but WITHOUT ANY WARRANTY; without // even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Less- // er General Public License along with bzipper. If // not, see . #[cfg(test)] mod test; use crate::{ Deserialise, Dstream, Error, FixedStringIter, Serialise, Sstream, }; use alloc::string::String; use core::cmp::Ordering; use core::fmt::{Debug, Display, Formatter}; use core::ops::{Index, IndexMut}; use core::str::FromStr; /// Owned string with maximum size. /// /// This is in contrast to [String], which has no size limit is practice, and [str], which is unsized. #[derive(Clone)] pub struct FixedString { buf: [char; N], len: usize, } impl FixedString { /// Constructs a new fixed string. /// /// The contents of the provided string are copied into the internal buffer. /// All residual characters are instanced as U+0000 `NULL`. /// /// # Errors /// /// If the given string `s` cannot fit into `N` characters, an [`ArrayTooShort`](Error::ArrayTooShort) error is returned. pub fn new(s: &str) -> Result { let mut buf = ['\0'; N]; let len = s.chars().count(); for (i, c) in s.chars().enumerate() { if i >= N { return Err(Error::ArrayTooShort { req: len, len: N }) } buf[i] = c; } Ok(Self { buf, len }) } /// Returns the length of the string. /// /// This does not necessarily equal the value of `N`, as the internal buffer is not required to be used fully. #[inline(always)] #[must_use] pub const fn len(&self) -> usize { self.len } /// Checks if the string is empty, i.e. `self.len() == 0x0`. #[inline(always)] #[must_use] pub const fn is_empty(&self) -> bool { self.len == 0x0 } /// Borrows the character at the specified index. /// /// If no element exists at that position, [`None`] is returned instead. #[inline] #[must_use] pub const fn get(&self, index: usize) -> Option<&char> { if index >= self.len { None } else { Some(&self.buf[index]) } } /// Mutably borrows the character at the specified index. /// /// If no element exists at that position, [`None`] is returned instead. #[inline] #[must_use] pub fn get_mut(&mut self, index: usize) -> Option<&mut char> { if index >= self.len { None } else { Some(&mut self.buf[index]) } } /// Returns an iterator to the contained characters. #[inline(always)] pub fn iter(&self) -> core::slice::Iter<'_, char> { self.buf[0x0..self.len].iter() } /// Returns a mutable iterator to the contained characters. #[inline(always)] pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, char> { self.buf[0x0..self.len].iter_mut() } } impl Debug for FixedString { fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { write!(f, "\"")?; for c in self { write!(f, "{}", c.escape_debug())? } write!(f, "\"")?; Ok(()) } } impl Deserialise for FixedString { type Error = Error; fn deserialise(stream: &mut Dstream) -> Result { let len = usize::deserialise(stream)?; let data = stream.take(len)?; let s = core::str::from_utf8(data) .map_err(|e| Error::BadString { source: e })?; let len = s.chars().count(); if len > N { return Err(Error::ArrayTooShort { req: len, len: N }); } let mut buf = ['\0'; N]; for (i, c) in s.chars().enumerate() { buf[i] = c; } Ok(Self { buf, len }) } } impl Default for FixedString { #[inline(always)] fn default() -> Self { Self { buf: ['\0'; N], len: 0x0, } } } impl Display for FixedString { fn fmt(&self, f: &mut Formatter) -> core::fmt::Result { for c in self { write!(f, "{c}")? } Ok(()) } } impl Eq for FixedString { } impl From<[char; N]> for FixedString { fn from(value: [char; N]) -> Self { Self { buf: value, len: N, } } } impl FromStr for FixedString { type Err = Error; fn from_str(s: &str) -> Result { Self::new(s) } } impl Index for FixedString { type Output = char; fn index(&self, index: usize) -> &Self::Output { self.get(index).unwrap() } } impl IndexMut for FixedString { fn index_mut(&mut self, index: usize) -> &mut Self::Output { self.get_mut(index).unwrap() } } impl IntoIterator for FixedString { type Item = char; type IntoIter = FixedStringIter; fn into_iter(self) -> Self::IntoIter { FixedStringIter { buf: self.buf, len: self.len, pos: Some(0x0), } } } impl<'a, const N: usize> IntoIterator for &'a FixedString { type Item = &'a char; type IntoIter = core::slice::Iter<'a, char>; fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'a, const N: usize> IntoIterator for &'a mut FixedString { type Item = &'a mut char; type IntoIter = core::slice::IterMut<'a, char>; fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } impl Ord for FixedString { fn cmp(&self, other: &Self) -> Ordering { self.partial_cmp(other).unwrap() } } impl PartialEq> for FixedString { fn eq(&self, other: &FixedString) -> bool { if self.len() != other.len() { return false }; for i in 0x0..self.len() { if self.buf[i] != other.buf[i] { return false }; } true } } impl PartialEq<&str> for FixedString { fn eq(&self, other: &&str) -> bool { for (i, c) in other.chars().enumerate() { if self.buf.get(i) != Some(&c) { return false }; } true } } impl PartialOrd> for FixedString { fn partial_cmp(&self, other: &FixedString) -> Option { let len = self.len().max(other.len()); for i in 0x0..len { let lc = self.get(i); let rc = other.get(i); match (lc, rc) { (None, None) => return Some(Ordering::Equal), (Some(_), None) => return Some(Ordering::Greater), (None, Some(_)) => return Some(Ordering::Less), (Some(lc), Some(rc)) => { let ordering = lc.cmp(rc); if ordering != Ordering::Equal { return Some(ordering) }; } } } Some(Ordering::Equal) } } impl Serialise for FixedString { type Error = Error; const SERIALISE_LIMIT: usize = N * 0x4; fn serialise(&self, stream: &mut Sstream) -> Result { let mut count = 0x0; let s: String = self.iter().collect(); count += s.len().serialise(stream)?; count += stream.add(&s.into_bytes())?; Ok(count) } } impl TryFrom<&str> for FixedString { type Error = Error; #[inline(always)] fn try_from(value: &str) -> Result { Self::new(value) } }