#[cfg(feature = "alloc")]
pub(crate) mod crc32;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MaybeStaticSlice<T: 'static> {
kind: MaybeStaticSliceKind<T>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
enum MaybeStaticSliceKind<T: 'static> {
Static(&'static [T]),
#[cfg(feature = "alloc")]
Heap(alloc::boxed::Box<[T]>),
}
impl<T: 'static> MaybeStaticSlice<T> {
#[inline]
pub const fn statik(data: &'static [T]) -> MaybeStaticSlice<T> {
let kind = MaybeStaticSliceKind::Static(data);
MaybeStaticSlice { kind }
}
#[cfg(feature = "alloc")]
#[inline]
pub const fn heap(data: alloc::boxed::Box<[T]>) -> MaybeStaticSlice<T> {
let kind = MaybeStaticSliceKind::Heap(data);
MaybeStaticSlice { kind }
}
#[inline]
pub const fn as_slice(&self) -> &[T] {
match self.kind {
MaybeStaticSliceKind::Static(slice) => slice,
#[cfg(feature = "alloc")]
MaybeStaticSliceKind::Heap(ref slice) => slice,
}
}
}
impl<T: 'static> From<&'static [T]> for MaybeStaticSlice<T> {
#[inline]
fn from(data: &'static [T]) -> MaybeStaticSlice<T> {
MaybeStaticSlice::statik(data)
}
}
#[cfg(feature = "alloc")]
impl<T: 'static> From<alloc::boxed::Box<[T]>> for MaybeStaticSlice<T> {
#[inline]
fn from(data: alloc::boxed::Box<[T]>) -> MaybeStaticSlice<T> {
MaybeStaticSlice::heap(data)
}
}
impl<T: 'static> core::ops::Deref for MaybeStaticSlice<T> {
type Target = [T];
#[inline]
fn deref(&self) -> &[T] {
self.as_slice()
}
}
#[derive(Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct SmallStr<const ARRAY_CAPACITY_MAX: usize> {
kind: SmallStrKind<ARRAY_CAPACITY_MAX>,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
enum SmallStrKind<const ARRAY_CAPACITY_MAX: usize> {
Array(ArrayStr<ARRAY_CAPACITY_MAX>),
Static(&'static str),
#[cfg(feature = "alloc")]
Heap(alloc::boxed::Box<str>),
}
impl<const ARRAY_CAPACITY_MAX: usize> SmallStr<ARRAY_CAPACITY_MAX> {
#[inline]
pub fn new(s: &str) -> Option<SmallStr<ARRAY_CAPACITY_MAX>> {
SmallStr::try_array(s).or_else(|| {
#[cfg(not(feature = "alloc"))]
{
None
}
#[cfg(feature = "alloc")]
{
Some(SmallStr::from(alloc::boxed::Box::<str>::from(s)))
}
})
}
#[cfg(feature = "alloc")]
#[inline]
pub fn new_or_heap(s: &str) -> SmallStr<ARRAY_CAPACITY_MAX> {
SmallStr::try_array(s).unwrap_or_else(|| {
SmallStr::from(alloc::boxed::Box::<str>::from(s))
})
}
#[inline]
pub const fn try_array(s: &str) -> Option<SmallStr<ARRAY_CAPACITY_MAX>> {
let Some(astr) = ArrayStr::new(s) else { return None };
let kind = SmallStrKind::Array(astr);
Some(SmallStr { kind })
}
#[inline]
pub const fn array(s: &str) -> SmallStr<ARRAY_CAPACITY_MAX> {
let Some(astr) = ArrayStr::new(s) else { panic!("string too big") };
let kind = SmallStrKind::Array(astr);
SmallStr { kind }
}
#[inline]
pub const fn statik(s: &'static str) -> SmallStr<ARRAY_CAPACITY_MAX> {
let kind = SmallStrKind::Static(s);
SmallStr { kind }
}
#[inline]
pub const fn array_capacity_max() -> usize {
ARRAY_CAPACITY_MAX
}
#[inline]
pub const fn as_str(&self) -> &str {
match self.kind {
SmallStrKind::Array(ref astr) => astr.as_str(),
SmallStrKind::Static(s) => s,
#[cfg(feature = "alloc")]
SmallStrKind::Heap(ref s) => s,
}
}
}
impl<const ARRAY_CAPACITY_MAX: usize> From<ArrayStr<ARRAY_CAPACITY_MAX>>
for SmallStr<ARRAY_CAPACITY_MAX>
{
#[inline]
fn from(s: ArrayStr<ARRAY_CAPACITY_MAX>) -> SmallStr<ARRAY_CAPACITY_MAX> {
let kind = SmallStrKind::Array(s);
SmallStr { kind }
}
}
impl<const ARRAY_CAPACITY_MAX: usize> From<&'static str>
for SmallStr<ARRAY_CAPACITY_MAX>
{
#[inline]
fn from(s: &'static str) -> SmallStr<ARRAY_CAPACITY_MAX> {
SmallStr::statik(s)
}
}
#[cfg(feature = "alloc")]
impl<const ARRAY_CAPACITY_MAX: usize> From<alloc::boxed::Box<str>>
for SmallStr<ARRAY_CAPACITY_MAX>
{
#[inline]
fn from(s: alloc::boxed::Box<str>) -> SmallStr<ARRAY_CAPACITY_MAX> {
let kind = SmallStrKind::Heap(s);
SmallStr { kind }
}
}
impl<const ARRAY_CAPACITY_MAX: usize> core::ops::Deref
for SmallStr<ARRAY_CAPACITY_MAX>
{
type Target = str;
#[inline]
fn deref(&self) -> &str {
SmallStr::<ARRAY_CAPACITY_MAX>::as_str(self)
}
}
impl<const ARRAY_CAPACITY_MAX: usize> AsRef<str>
for SmallStr<ARRAY_CAPACITY_MAX>
{
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl<const ARRAY_CAPACITY_MAX: usize> PartialEq<str>
for SmallStr<ARRAY_CAPACITY_MAX>
{
#[inline]
fn eq(&self, rhs: &str) -> bool {
self.as_str() == rhs
}
}
impl<const ARRAY_CAPACITY_MAX: usize> PartialEq<&str>
for SmallStr<ARRAY_CAPACITY_MAX>
{
#[inline]
fn eq(&self, rhs: &&str) -> bool {
self.as_str() == *rhs
}
}
impl<const ARRAY_CAPACITY_MAX: usize> PartialEq<SmallStr<ARRAY_CAPACITY_MAX>>
for str
{
#[inline]
fn eq(&self, rhs: &SmallStr<ARRAY_CAPACITY_MAX>) -> bool {
self == rhs.as_str()
}
}
impl<const ARRAY_CAPACITY_MAX: usize> PartialOrd<str>
for SmallStr<ARRAY_CAPACITY_MAX>
{
#[inline]
fn partial_cmp(&self, rhs: &str) -> Option<core::cmp::Ordering> {
self.as_str().partial_cmp(rhs)
}
}
impl<const ARRAY_CAPACITY_MAX: usize> PartialOrd<&str>
for SmallStr<ARRAY_CAPACITY_MAX>
{
#[inline]
fn partial_cmp(&self, rhs: &&str) -> Option<core::cmp::Ordering> {
self.as_str().partial_cmp(*rhs)
}
}
impl<const ARRAY_CAPACITY_MAX: usize> PartialOrd<SmallStr<ARRAY_CAPACITY_MAX>>
for str
{
#[inline]
fn partial_cmp(
&self,
rhs: &SmallStr<ARRAY_CAPACITY_MAX>,
) -> Option<core::cmp::Ordering> {
self.partial_cmp(rhs.as_str())
}
}
impl<const ARRAY_CAPACITY_MAX: usize> core::fmt::Debug
for SmallStr<ARRAY_CAPACITY_MAX>
{
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
core::fmt::Debug::fmt(self.as_str(), f)
}
}
impl<const ARRAY_CAPACITY_MAX: usize> core::fmt::Display
for SmallStr<ARRAY_CAPACITY_MAX>
{
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
core::fmt::Display::fmt(self.as_str(), f)
}
}
#[cfg(feature = "defmt")]
impl<const ARRAY_CAPACITY_MAX: usize> defmt::Format
for SmallStr<ARRAY_CAPACITY_MAX>
{
#[inline]
fn format(&self, f: defmt::Formatter) {
defmt::write!(f, "{=str}", self.as_str())
}
}
#[derive(Clone, Copy, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct ArrayStr<const N: usize> {
len: u8,
bytes: [u8; N],
}
impl<const N: usize> ArrayStr<N> {
#[inline]
pub const fn new(s: &str) -> Option<ArrayStr<N>> {
let len = s.len();
if len > N {
return None;
}
let mut bytes = [0; N];
let mut i = 0;
while i < s.as_bytes().len() {
bytes[i] = s.as_bytes()[i];
i += 1;
}
debug_assert!(N <= u8::MAX as usize, "size of ArrayStr is too big");
Some(ArrayStr { len: len as u8, bytes })
}
#[inline]
pub const fn capacity() -> usize {
N
}
#[inline]
pub fn push_str(&mut self, s: &str) -> bool {
let len = self.len as usize;
let Some(new_len) = len.checked_add(s.len()) else { return false };
if new_len > N {
return false;
}
let mut i = len;
while i < new_len {
self.bytes[i] = s.as_bytes()[i - len];
i += 1;
}
debug_assert!(N <= u8::MAX as usize, "size of ArrayStr is too big");
self.len = new_len as u8;
true
}
#[inline]
pub const fn as_str(&self) -> &str {
unsafe {
core::str::from_utf8_unchecked(core::slice::from_raw_parts(
self.bytes.as_ptr(),
self.len as usize,
))
}
}
}
impl<const N: usize> core::ops::Deref for ArrayStr<N> {
type Target = str;
#[inline]
fn deref(&self) -> &str {
ArrayStr::<N>::as_str(self)
}
}
impl<const N: usize> AsRef<str> for ArrayStr<N> {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl<const N: usize> PartialEq<str> for ArrayStr<N> {
#[inline]
fn eq(&self, rhs: &str) -> bool {
self.as_str() == rhs
}
}
impl<const N: usize> PartialEq<&str> for ArrayStr<N> {
#[inline]
fn eq(&self, rhs: &&str) -> bool {
self.as_str() == *rhs
}
}
impl<const N: usize> PartialEq<ArrayStr<N>> for str {
#[inline]
fn eq(&self, rhs: &ArrayStr<N>) -> bool {
self == rhs.as_str()
}
}
impl<const N: usize> PartialOrd<str> for ArrayStr<N> {
#[inline]
fn partial_cmp(&self, rhs: &str) -> Option<core::cmp::Ordering> {
self.as_str().partial_cmp(rhs)
}
}
impl<const N: usize> PartialOrd<&str> for ArrayStr<N> {
#[inline]
fn partial_cmp(&self, rhs: &&str) -> Option<core::cmp::Ordering> {
self.as_str().partial_cmp(*rhs)
}
}
impl<const N: usize> PartialOrd<ArrayStr<N>> for str {
#[inline]
fn partial_cmp(&self, rhs: &ArrayStr<N>) -> Option<core::cmp::Ordering> {
self.partial_cmp(rhs.as_str())
}
}
impl<const N: usize> core::fmt::Debug for ArrayStr<N> {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
core::fmt::Debug::fmt(self.as_str(), f)
}
}
impl<const N: usize> core::fmt::Display for ArrayStr<N> {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
core::fmt::Display::fmt(self.as_str(), f)
}
}
impl<const N: usize> core::fmt::Write for ArrayStr<N> {
#[inline]
fn write_str(&mut self, s: &str) -> core::fmt::Result {
if self.push_str(s) {
Ok(())
} else {
Err(core::fmt::Error)
}
}
}
#[cfg(feature = "defmt")]
impl<const N: usize> defmt::Format for ArrayStr<N> {
#[inline]
fn format(&self, f: defmt::Formatter) {
defmt::write!(f, "{=str}", self.as_str())
}
}
#[cfg(feature = "std")]
pub(crate) fn os_str_bytes<'o, O>(os_str: &'o O) -> Option<&'o [u8]>
where
O: ?Sized + AsRef<std::ffi::OsStr>,
{
let os_str = os_str.as_ref();
#[cfg(unix)]
{
use std::os::unix::ffi::OsStrExt;
Some(os_str.as_bytes())
}
#[cfg(not(unix))]
{
os_str.to_str().map(|s| s.as_bytes())
}
}
#[cfg(test)]
mod tests {
use core::fmt::Write;
use super::*;
#[test]
fn fmt_write() {
let mut dst = ArrayStr::<5>::new("").unwrap();
assert!(write!(&mut dst, "abcd").is_ok());
assert!(write!(&mut dst, "e").is_ok());
assert!(write!(&mut dst, "f").is_err());
}
#[test]
fn array_str_size() {
assert_eq!(7, core::mem::size_of::<ArrayStr::<6>>());
}
}