1#![no_std]
50
51use core::fmt::{self, Display};
52use core::ops::{Deref, DerefMut};
53use core::str::{self, Utf8Error};
54
55extern crate alloc;
56use alloc::vec::Vec;
57use alloc::boxed::Box;
58
59use tiny_vec::TinyVec;
60
61const MAX_N_STACK_ELEMENTS: usize = tiny_vec::n_elements_for_stack::<u8>();
62
63pub struct TinyString<const N: usize = MAX_N_STACK_ELEMENTS>(TinyVec<u8, N>);
65
66impl<const N: usize> TinyString<N> {
67
68 #[inline]
70 pub const fn new() -> Self {
71 Self(TinyVec::new())
72 }
73
74 pub fn with_capacity(cap: usize) -> Self {
76 Self(TinyVec::with_capacity(cap))
77 }
78
79 pub fn from_utf8(utf8: TinyVec<u8, N>) -> Result<Self,Utf8Error> {
84 str::from_utf8(utf8.as_slice())?;
85 Ok(Self(utf8))
86 }
87
88 #[inline(always)]
93 pub const unsafe fn from_utf8_unchecked(utf8: TinyVec<u8, N>) -> Self {
94 Self(utf8)
95 }
96
97 #[inline]
99 pub const fn len(&self) -> usize { self.0.len() }
100
101 #[inline]
103 pub const fn is_empty(&self) -> bool { self.0.is_empty() }
104
105 #[inline]
107 pub const fn capacity(&self) -> usize { self.0.capacity() }
108
109 #[inline]
111 pub const fn as_str(&self) -> &str {
112 unsafe { str::from_utf8_unchecked(self.0.as_slice()) }
113 }
114
115 #[inline]
117 pub const fn as_mut_str(&mut self) -> &mut str {
118 unsafe { str::from_utf8_unchecked_mut(self.0.as_mut_slice()) }
119 }
120
121 #[inline]
123 pub const fn as_bytes(&self) -> &[u8] {
124 self.0.as_slice()
125 }
126
127 pub fn push(&mut self, c: char) {
129 let len = c.len_utf8();
130 if len == 1 {
131 self.0.push(c as u8);
132 } else {
133 let mut buf = [0_u8; 4];
134 c.encode_utf8(&mut buf);
135 self.0.extend_from_slice(&buf[..len]);
136 }
137 }
138
139 pub fn pop(&mut self) -> Option<char> {
153 let c = self.chars().next_back()?;
154 let new_len = self.len() - c.len_utf8();
155 unsafe {
156 self.0.set_len(new_len);
157 }
158 Some(c)
159 }
160
161 #[inline]
163 pub fn push_str(&mut self, s: &str) {
164 self.0.extend_from_slice_copied(s.as_bytes());
165 }
166
167 #[inline]
169 pub fn shrink_to_fit(&mut self) {
170 self.0.shrink_to_fit();
171 }
172
173 #[inline]
175 pub fn reserve(&mut self, n: usize) {
176 self.0.reserve(n);
177 }
178
179 #[inline]
181 pub fn reserve_exact(&mut self, n: usize) {
182 self.0.reserve_exact(n);
183 }
184
185 pub fn into_boxed_str(self) -> Box<str> {
198 let b = self.0.into_boxed_slice();
199 unsafe { alloc::str::from_boxed_utf8_unchecked(b) }
200 }
201}
202
203impl<const N: usize> Default for TinyString<N> {
204 fn default() -> Self {
205 Self::new()
206 }
207}
208
209impl<const N: usize> Deref for TinyString<N> {
210 type Target = str;
211
212 fn deref(&self) -> &Self::Target {
213 self.as_str()
214 }
215}
216
217impl<const N: usize> DerefMut for TinyString<N> {
218 fn deref_mut(&mut self) -> &mut Self::Target {
219 self.as_mut_str()
220 }
221}
222
223impl<const N: usize> From<&str> for TinyString<N> {
224 fn from(value: &str) -> Self {
225 let mut s = Self::with_capacity(value.len());
226 s.push_str(value);
227 s
228 }
229}
230
231impl<const N: usize> TryFrom<&[u8]> for TinyString<N> {
232 type Error = Utf8Error;
233
234 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
235 str::from_utf8(value)?;
236 Ok(unsafe { Self::from_utf8_unchecked(TinyVec::from_slice_copied(value)) })
237 }
238}
239
240impl<const N: usize> TryFrom<TinyVec<u8, N>> for TinyString<N> {
241 type Error = Utf8Error;
242
243 fn try_from(value: TinyVec<u8, N>) -> Result<Self, Self::Error> {
244 Self::from_utf8(value)
245 }
246}
247
248impl<const N: usize> TryFrom<Vec<u8>> for TinyString<N> {
249 type Error = Utf8Error;
250
251 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
252 str::from_utf8(value.as_slice())?;
253 Ok(unsafe { Self::from_utf8_unchecked(TinyVec::from_vec(value)) })
254 }
255}
256
257impl<const N: usize> From<TinyString<N>> for TinyVec<u8, N> {
258 fn from(value: TinyString<N>) -> Self {
259 value.0
260 }
261}
262
263impl<const N: usize> From<TinyString<N>> for Vec<u8> {
264 fn from(value: TinyString<N>) -> Self {
265 value.0.into_vec()
266 }
267}
268
269impl<const N: usize> From<TinyString<N>> for Box<str> {
270 fn from(value: TinyString<N>) -> Self {
271 value.into_boxed_str()
272 }
273}
274
275impl<const N: usize> FromIterator<char> for TinyString<N> {
276 fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
277 let iter = iter.into_iter();
278 let cap = match iter.size_hint() {
279 (_, Some(n)) => n,
280 (n, _) => n,
281 };
282 let mut s = TinyString::with_capacity(cap);
283 for c in iter {
284 s.push(c);
285 }
286 s
287 }
288}
289
290impl<const N: usize, S> PartialEq<S> for TinyString<N>
291where
292 S: AsRef<[u8]>
293{
294 fn eq(&self, other: &S) -> bool {
295 self.as_bytes() == other.as_ref()
296 }
297}
298
299impl<const N: usize> Eq for TinyString<N> { }
300
301impl<const N: usize> AsRef<[u8]> for TinyString<N> {
302 fn as_ref(&self) -> &[u8] {
303 self.as_bytes()
304 }
305}
306
307impl<const N: usize> AsRef<str> for TinyString<N> {
308 fn as_ref(&self) -> &str {
309 self.as_str()
310 }
311}
312
313impl<const N: usize> AsMut<str> for TinyString<N> {
314 fn as_mut(&mut self) -> &mut str {
315 self.as_mut_str()
316 }
317}
318
319impl<const N: usize> fmt::Debug for TinyString<N> {
320 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
321 write!(f, "{:?}", self.bytes())
322 }
323}
324
325impl<const N: usize> Display for TinyString<N> {
326 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
327 write!(f, "{}", self.as_str())
328 }
329}
330
331#[cfg(test)]
332mod test;