@ -1,5 +1,3 @@
#![ feature(round_char_boundary) ]
use std ::{
use std ::{
mem ::{ self , MaybeUninit , } ,
mem ::{ self , MaybeUninit , } ,
slice ,
slice ,
@ -12,7 +10,9 @@ use std::{
ops ,
ops ,
} ;
} ;
/// A `str` that lives entirely on the stack.
mod ext ; use ext ::* ;
/// A `String` that lives entirely on the stack.
#[ derive(Debug, Clone) ]
#[ derive(Debug, Clone) ]
#[ cfg_attr(feature= " copy " , derive(Copy)) ]
#[ cfg_attr(feature= " copy " , derive(Copy)) ]
#[ repr(C) ] // Needed for SmallString
#[ repr(C) ] // Needed for SmallString
@ -20,9 +20,29 @@ pub struct StackString<const SIZE: usize>{
fill_ptr : usize ,
fill_ptr : usize ,
buffer : MaybeUninit < [ u8 ; SIZE ] > ,
buffer : MaybeUninit < [ u8 ; SIZE ] > ,
}
}
#[ derive(Debug) ]
/// Error returned when trying to create a `StackString` from a string that is too large to fit in it.
#[ derive(Debug, PartialEq, Eq, PartialOrd, Ord) ]
pub struct StrTooLargeError < const SIZE : usize > ( usize ) ;
pub struct StrTooLargeError < const SIZE : usize > ( usize ) ;
mod small ;
pub use small ::SmallString ;
impl < const SIZE : usize > StrTooLargeError < SIZE >
{
/// The invalid string's size
#[ inline ]
pub const fn invalid_size ( & self ) -> usize
{
self . 0
}
/// The maximum allowed size
#[ inline ]
pub const fn max_size ( ) -> usize
{
SIZE
}
}
impl < const SIZE : usize > error ::Error for StrTooLargeError < SIZE > { }
impl < const SIZE : usize > error ::Error for StrTooLargeError < SIZE > { }
impl < const SIZE : usize > fmt ::Display for StrTooLargeError < SIZE >
impl < const SIZE : usize > fmt ::Display for StrTooLargeError < SIZE >
{
{
@ -34,12 +54,9 @@ impl<const SIZE: usize> fmt::Display for StrTooLargeError<SIZE>
}
}
mod small ;
pub use small ::SmallString ;
//TODO: Document
impl < const SIZE : usize > StackString < SIZE >
impl < const SIZE : usize > StackString < SIZE >
{
{
/// Try to create a `StackString` from an exact-sized buffer.
#[ inline ]
#[ inline ]
pub fn try_from_utf8_array ( sz : [ u8 ; SIZE ] ) -> Result < Self , std ::str ::Utf8Error >
pub fn try_from_utf8_array ( sz : [ u8 ; SIZE ] ) -> Result < Self , std ::str ::Utf8Error >
{
{
@ -49,23 +66,26 @@ impl<const SIZE: usize> StackString<SIZE>
buffer : MaybeUninit ::new ( sz ) ,
buffer : MaybeUninit ::new ( sz ) ,
} )
} )
}
}
/// The maximum capacity of this instance
#[ inline ]
#[ inline ]
pub const fn capacity ( & self ) -> usize
pub const fn capacity ( & self ) -> usize
{
{
SIZE
SIZE
}
}
/// The length of the string.
#[ inline ]
#[ inline ]
pub const fn len ( & self ) -> usize
pub const fn len ( & self ) -> usize
{
{
self . fill_ptr
self . fill_ptr
}
}
/// The amount of memory left before the instance becomes full.
#[ inline ]
#[ inline ]
pub const fn available ( & self ) -> usize
pub const fn available ( & self ) -> usize
{
{
SIZE - self . fill_ptr
SIZE - self . fill_ptr
}
}
/// Create a new, empty `StackString`
#[ inline ]
#[ inline ]
pub const fn new ( ) -> Self
pub const fn new ( ) -> Self
{
{
@ -94,10 +114,12 @@ impl<const SIZE: usize> StackString<SIZE>
fn as_raw_buf ( & self ) -> & [ u8 ]
fn as_raw_buf ( & self ) -> & [ u8 ]
{
{
unsafe {
unsafe {
// MaybeUninit::slice_assume_init_ref(&self.buffer.as_bytes()[..self.fill_ptr]);
slice ::from_raw_parts ( self . buffer . as_ptr ( ) as * const u8 , self . fill_ptr )
slice ::from_raw_parts ( self . buffer . as_ptr ( ) as * const u8 , self . fill_ptr )
}
}
}
}
/// A mutable reference to the `str` inside
#[ inline ]
#[ inline ]
pub fn as_mut_str ( & mut self ) -> & mut str
pub fn as_mut_str ( & mut self ) -> & mut str
{
{
@ -105,6 +127,7 @@ impl<const SIZE: usize> StackString<SIZE>
std ::str ::from_utf8_unchecked_mut ( self . as_raw_buf_mut ( ) )
std ::str ::from_utf8_unchecked_mut ( self . as_raw_buf_mut ( ) )
}
}
}
}
/// A reference to the `str` inside.
#[ inline ]
#[ inline ]
pub fn as_str ( & self ) -> & str
pub fn as_str ( & self ) -> & str
{
{
@ -143,7 +166,7 @@ impl<const SIZE: usize> StackString<SIZE>
av
av
} else {
} else {
// Can only fit part in, find the char boundary below `av` and append that.
// Can only fit part in, find the char boundary below `av` and append that.
return self . append_from_str ( & s [ .. s . floor_char_boundary ( av ) ] ) ; //TODO: implement floor_char_boundary() ourselves, and we probably don't need this recursive call.
return self . append_from_str ( & s [ .. s . floor_char_boundary _impl ( av ) ] ) ; //TODO: implement floor_char_boundary() ourselves, and we probably don't need this recursive call.
} ;
} ;
self . fill_ptr + = sl ;
self . fill_ptr + = sl ;
sl
sl
@ -192,7 +215,7 @@ impl<const SIZE: usize> StackString<SIZE>
/// * `Some(substr)` - If all of `s` fits in to `self`: the substring of `self` that now contains `s`.
/// * `Some(substr)` - If all of `s` fits in to `self`: the substring of `self` that now contains `s`.
/// * `None` - If `s` was too large to fit entirely in to `self`
/// * `None` - If `s` was too large to fit entirely in to `self`
#[ inline ]
#[ inline ]
pub fn try_append_whole_str < ' i > ( & ' i mut self , s : & str ) -> Option < & ' i mut str >
pub fn try_append_whole_str < ' i > ( & ' i mut self , s : & str ) -> Result < & ' i mut str , StrTooLargeError < SIZE > >
{
{
let av = self . available ( ) ;
let av = self . available ( ) ;
if s . len ( ) < = av {
if s . len ( ) < = av {
@ -200,9 +223,9 @@ impl<const SIZE: usize> StackString<SIZE>
debug_assert_eq! ( len , s . len ( ) , "Bad append" ) ;
debug_assert_eq! ( len , s . len ( ) , "Bad append" ) ;
let _ = len ;
let _ = len ;
Some ( & mut self . as_mut_str ( ) [ av .. ] )
Ok ( & mut self . as_mut_str ( ) [ av .. ] )
} else {
} else {
None
Err( StrTooLargeError ( s . len ( ) ) )
}
}
}
}
}
}
@ -233,11 +256,9 @@ impl<const SIZE: usize> std::str::FromStr for StackString<SIZE>
Err ( StrTooLargeError ( s . len ( ) ) )
Err ( StrTooLargeError ( s . len ( ) ) )
} else {
} else {
let mut o = Self ::new ( ) ;
let mut o = Self ::new ( ) ;
if o . try_append_whole_str ( s ) . is_none ( ) {
o . try_append_whole_str ( s ) ? ;
Err ( StrTooLargeError ( s . len ( ) ) )
Ok ( o )
} else {
Ok ( o )
}
}
}
}
}
}
}