use std::ops::Add;
#[cfg(not(feature = "compact"))]
pub const INLINE_LENGTH: usize = 30;
#[cfg(feature = "compact")]
pub const INLINE_LENGTH: usize = 15;
#[repr(packed)]
#[derive(Default, Clone, Copy)]
struct InlineString {
len: u8,
buf: [u8; INLINE_LENGTH],
}
#[derive(Clone)]
enum ShortStringInner {
Inline(InlineString),
Heap(String),
}
impl Default for ShortStringInner {
fn default() -> Self {
Self::Inline(InlineString::default())
}
}
#[derive(Default, Clone)]
pub struct ShortString {
inner: ShortStringInner,
}
impl ShortString {
pub fn new() -> Self {
Self {
inner: ShortStringInner::Inline(InlineString {
buf: [0; INLINE_LENGTH],
len: 0,
}),
}
}
pub fn as_bytes(&self) -> &[u8] {
match &self.inner {
ShortStringInner::Inline(inlined) => &inlined.buf[..(inlined.len as usize)],
ShortStringInner::Heap(buf) => buf.as_bytes(),
}
}
pub fn as_mut_str(&mut self) -> &mut str {
match &mut self.inner {
ShortStringInner::Inline(inlined) => unsafe {
std::str::from_utf8_unchecked_mut(&mut inlined.buf[..(inlined.len as usize)])
},
ShortStringInner::Heap(buf) => unsafe { buf.as_mut_str() },
}
}
pub fn as_str(&self) -> &str {
match &self.inner {
ShortStringInner::Inline(inlined) => unsafe {
std::str::from_utf8_unchecked(&inlined.buf[..(inlined.len as usize)])
},
ShortStringInner::Heap(buf) => unsafe { buf.as_str() },
}
}
pub fn capacity(&self) -> usize {
match &self.inner {
ShortStringInner::Inline(inlined) => inlined.buf.len(),
ShortStringInner::Heap(buf) => buf.capacity(),
}
}
pub fn clear(&mut self) {
match &mut self.inner {
ShortStringInner::Inline(inlined) => {
inlined.len = 0;
}
ShortStringInner::Heap(buf) => {
buf.clear();
}
}
}
pub fn into_string(self) -> String {
match self.inner {
ShortStringInner::Inline(inlined) => unsafe {
String::from_utf8_unchecked(Vec::from(&inlined.buf[..(inlined.len as usize)]))
},
ShortStringInner::Heap(buf) => buf,
}
}
pub fn push(&mut self, ch: char) {
match &mut self.inner {
ShortStringInner::Inline(inlined) => {
let mut bytes = [0u8; 4];
let ch_bytes = ch.encode_utf8(&mut bytes).as_bytes();
let length = inlined.len as usize;
if length + ch_bytes.len() > inlined.buf.len() {
self.inner = ShortStringInner::Heap(unsafe {
String::from_utf8_unchecked(Vec::from(&inlined.buf[..(length as usize)]))
});
self.push(ch);
} else {
inlined.buf[length..length + ch_bytes.len()].copy_from_slice(ch_bytes);
inlined.len += ch_bytes.len() as u8;
}
}
ShortStringInner::Heap(string) => string.push(ch),
}
}
}
impl std::fmt::Debug for ShortString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl std::fmt::Display for ShortString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl PartialEq for ShortString {
fn eq(&self, other: &Self) -> bool {
self.as_str() == other.as_str()
}
}
impl Eq for ShortString {}
impl PartialOrd for ShortString {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.as_str().partial_cmp(other.as_str())
}
}
impl Ord for ShortString {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_str().cmp(other.as_str())
}
}
impl std::hash::Hash for ShortString {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.as_str().hash(state)
}
}
impl std::ops::Deref for ShortString {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl From<&str> for ShortString {
fn from(s: &str) -> Self {
let len = s.len();
if len <= INLINE_LENGTH {
let mut buf = [0; INLINE_LENGTH];
buf[..len].copy_from_slice(s.as_bytes());
Self {
inner: ShortStringInner::Inline(InlineString {
buf,
len: len as u8,
}),
}
} else {
Self {
inner: ShortStringInner::Heap(s.into()),
}
}
}
}
impl From<String> for ShortString {
fn from(s: String) -> Self {
Self {
inner: ShortStringInner::Heap(s),
}
}
}
impl From<&String> for ShortString {
fn from(s: &String) -> Self {
Self::from(s.as_str())
}
}
impl Into<String> for ShortString {
fn into(self) -> String {
self.into_string()
}
}
impl AsRef<str> for ShortString {
fn as_ref(&self) -> &str {
self.as_str()
}
}