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