tiny_str/
lib.rs

1/*  Copyright (C) 2025 Saúl Valdelvira
2 *
3 *  This program is free software: you can redistribute it and/or modify
4 *  it under the terms of the GNU General Public License as published by
5 *  the Free Software Foundation, version 3.
6 *
7 *  This program is distributed in the hope that it will be useful,
8 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 *  GNU General Public License for more details.
11 *
12 *  You should have received a copy of the GNU General Public License
13 *  along with this program.  If not, see <https://www.gnu.org/licenses/>. */
14
15//! Tiny string
16//!
17//! A string that can store a small amount of bytes on the stack.
18//!
19//! This struct provides a string-like API, but performs SSO (Small String Optimization)
20//! This means that a `TinyString<N>` stores up to N bytes on the stack.
21//! If the string grows bigger than that, it moves the contents to the heap.
22//!
23#![cfg_attr(not(feature = "alloc"), doc = "
24# WARNING
25The `alloc` feature is disabled. This means that a `TinyString` won't be able to
26grow over it's stack capacity.
27
28The following functions from [TinyString] can cause the program to panic if the string
29exceeds its capacity.
30- [with_capacity]
31- [repeat](TinyString::repeat)
32- [push]
33- [push_str]
34- [reserve]
35- [reserve_exact]
36- [extend_from_within](TinyString::extend_from_within)
37- [insert](TinyString::insert)
38- [insert_str](TinyString::insert_str)
39
40## Alternatives
41| May Panic | No Panic |
42| --------- | -------- |
43|  [push]   | [push_within_capacity](TinyString::push_within_capacity) |
44|  [push_str]   | [push_within_capacity](TinyString::push_str_within_capacity) |
45|  [reserve]   | [try_reserve](TinyString::try_reserve) |
46| [with_capacity] | [try_with_capacity](TinyString::try_with_capacity) |
47| [reserve] | [try_reserve](TinyString::try_reserve) |
48| [reserve_exact] | [try_reserve_exact](TinyString::try_reserve_exact) |
49
50[push]: TinyString::push
51[push_str]: TinyString::push_str
52[reserve]: TinyString::reserve
53[reserve_exact]: TinyString::reserve_exact
54[with_capacity]: TinyString::with_capacity
55")]
56//!
57//! # Example
58//! ```
59//! use tiny_str::TinyString;
60//!
61//! let mut s = TinyString::<10>::new();
62//!
63//! for (i, c) in (b'0'..=b'9').enumerate() {
64//!     s.push(c as char);
65//!     assert_eq!(s.len(), i + 1);
66//! }
67//!
68//! // Up to this point, no heap allocations are needed.
69//! // The string is stored on the stack.
70//!
71//! s.push_str("abc"); // This moves the string to the heap
72//!
73//! assert_eq!(&s[..], "0123456789abc")
74//! ```
75//!
76//! # Memory layout
77//! [TinyString] is based on [TinyVec], just like [String] is based on [Vec].
78//!
79//! You can read the [tiny_vec] crate documentation to learn about the internal
80//! representation of the data.
81//!
82#![cfg_attr(not(feature = "alloc"), doc = "
83[String]: <https://doc.rust-lang.org/alloc/string/struct.String.html>
84[Vec]: <https://doc.rust-lang.org/alloc/vec/struct.Vec.html>")]
85
86#![no_std]
87
88#![cfg_attr(feature = "use-nightly-features", feature(extend_one))]
89
90use core::fmt::{self, Display};
91use core::hash::Hash;
92use core::ops::{Bound, Deref, DerefMut, Range, RangeBounds};
93use core::str::{self, FromStr, Utf8Error};
94
95#[cfg(feature = "alloc")]
96extern crate alloc;
97#[cfg(feature = "alloc")]
98use alloc::{
99    vec::Vec,
100    boxed::Box,
101    string::String,
102};
103
104use tiny_vec::TinyVec;
105pub use tiny_vec::ResizeError;
106pub mod iter;
107
108pub mod drain;
109
110mod cow;
111pub use cow::Cow;
112
113const MAX_N_STACK_ELEMENTS: usize = tiny_vec::n_elements_for_stack::<u8>();
114
115/// A string that can store a small amount of bytes on the stack.
116pub struct TinyString<const N: usize = MAX_N_STACK_ELEMENTS> {
117    buf: TinyVec<u8, N>,
118}
119
120impl<const N: usize> TinyString<N> {
121    fn slice_range<R>(&self, range: R, len: usize) -> Range<usize>
122    where
123        R: RangeBounds<usize>
124    {
125        let start = match range.start_bound() {
126            Bound::Included(n) => *n,
127            Bound::Excluded(n) => *n + 1,
128            Bound::Unbounded => 0,
129        };
130
131        let end = match range.end_bound() {
132            Bound::Included(n) => *n + 1,
133            Bound::Excluded(n) => *n,
134            Bound::Unbounded => len,
135        };
136
137        assert!(start <= end);
138        assert!(end <= len);
139        assert!(self.is_char_boundary(start));
140        assert!(self.is_char_boundary(end));
141
142        Range { start, end }
143    }
144}
145
146impl<const N: usize> TinyString<N> {
147
148    /// Creates a new [TinyString]
149    #[inline]
150    pub const fn new() -> Self {
151        Self { buf: TinyVec::new() }
152    }
153
154    /// Creates a new [TinyString] with the given capacity
155    pub fn with_capacity(cap: usize) -> Self {
156        Self { buf: TinyVec::with_capacity(cap) }
157    }
158
159    /// Like [with_capacity](Self::with_capacity), but it returns a [Result].
160    ///
161    /// If an allocation error hapens when reserving the memory, returns
162    /// a [ResizeError] unlike [with_capacity](Self::with_capacity), which
163    /// panics in such case.
164    pub fn try_with_capacity(cap: usize) -> Result<Self,ResizeError> {
165        Ok(Self { buf: TinyVec::try_with_capacity(cap)? })
166    }
167
168    /// Creates a new [TinyString] from the given utf8 buffer.
169    ///
170    /// # Errors
171    /// If the byte buffer contains invalid uft8
172    pub fn from_utf8(utf8: TinyVec<u8, N>) -> Result<Self,Utf8Error> {
173        str::from_utf8(utf8.as_slice())?;
174        Ok(Self { buf: utf8 })
175    }
176
177    /// Creates a new [TinyString] from the given utf8 buffer.
178    ///
179    /// # Safety
180    /// The caller must ensure that the given contains valid utf8
181    #[inline(always)]
182    pub const unsafe fn from_utf8_unchecked(utf8: TinyVec<u8, N>) -> Self {
183        Self { buf: utf8 }
184    }
185
186    /// Creates a new `TinyString` by repeating the given `slice` `n` times.
187    ///
188    /// # Panics
189    /// If the capacity requires overflows `isize::MAX`
190    ///
191    /// # Example
192    /// ```
193    /// use tiny_str::TinyString;
194    /// let s = TinyString::<10>::repeat("abc", 5);
195    /// assert_eq!(s.as_str(), "abcabcabcabcabc");
196    /// ```
197    pub fn repeat(slice: &str, n: usize) -> Self {
198        Self {
199            buf: TinyVec::repeat(slice.as_bytes(), n)
200        }
201    }
202
203    /// Returns the number of elements inside this string
204    #[inline]
205    pub const fn len(&self) -> usize { self.buf.len() }
206
207    /// Returns true if the string is empty
208    #[inline]
209    pub const fn is_empty(&self) -> bool { self.buf.is_empty() }
210
211    /// Returns the allocated capacity for this string
212    #[inline]
213    pub const fn capacity(&self) -> usize { self.buf.capacity() }
214
215    /// Returns true if the string is currently using stack memory.
216    ///
217    /// This means that [Self::len] <= `N`
218    ///
219    /// # Example
220    /// ```
221    /// use tiny_str::TinyString;
222    ///
223    /// let mut vec = TinyString::<5>::new();
224    ///
225    /// for n in 0..5 {
226    ///     vec.push('a')
227    /// }
228    ///
229    /// assert!(vec.lives_on_stack());
230    /// vec.push('a');
231    /// assert!(!vec.lives_on_stack());
232    /// ```
233    #[inline]
234    pub const fn lives_on_stack(&self) -> bool { self.buf.lives_on_stack() }
235
236    /// Returns a str slice
237    #[inline]
238    pub const fn as_str(&self) -> &str {
239        unsafe { str::from_utf8_unchecked(self.buf.as_slice()) }
240    }
241
242    /// Returns a mutable str slice
243    #[inline]
244    pub const fn as_mut_str(&mut self) -> &mut str {
245        unsafe { str::from_utf8_unchecked_mut(self.buf.as_mut_slice()) }
246    }
247
248    /// Returns a const pointer to the buffer
249    ///
250    /// This method shadows [str::as_ptr], to avoid a deref
251    #[inline]
252    pub const fn as_ptr(&self) -> *const u8 {
253        self.buf.as_ptr()
254    }
255
256    /// Returns a mutable pointer to the buffer
257    ///
258    /// This method shadows [str::as_mut_ptr], to avoid a deref
259    #[inline]
260    pub const fn as_mut_ptr(&mut self) -> *mut u8 {
261        self.buf.as_mut_ptr()
262    }
263
264    /// Returns the string as a byte slice
265    #[inline]
266    pub const fn as_bytes(&self) -> &[u8] {
267        self.buf.as_slice()
268    }
269
270    /// Returns the string as a byte slice
271    ///
272    /// Returns the string as a mutable bytes slice
273    ///
274    /// # Safety
275    /// Modifying this byte slice is dangerous, because it can leave the
276    /// buffer on an inconsistent state.
277    /// Strings must be valid UTF8. So manually changing the byte contents
278    /// of the string could lead to bugs.
279    ///
280    /// # Example
281    /// ```
282    /// use tiny_str::TinyString;
283    ///
284    /// let mut s = TinyString::<10>::from("hello");
285    /// unsafe {
286    ///     let slice = s.as_mut_bytes();
287    ///     assert_eq!(&[104, 101, 108, 108, 111][..], &slice[..]);
288    ///     slice.reverse();
289    /// }
290    /// assert_eq!(s, "olleh");
291    /// ```
292    #[inline]
293    pub const unsafe fn as_mut_bytes(&mut self) -> &mut [u8] {
294        self.buf.as_mut_slice()
295    }
296
297    /// Returns a mutable reference to the contents of this `TinyString`
298    ///
299    /// # Safety
300    /// Modifying this [TinyVec] is dangerous, because it can leave the
301    /// buffer on an inconsistent state.
302    /// Strings must be valid UTF8. So mutating the vector without respecting
303    /// that could lead to bugs.
304    ///
305    /// # Example
306    /// ```
307    /// use tiny_str::TinyString;
308    ///
309    /// let mut s = TinyString::<10>::from("hello");
310    /// unsafe {
311    ///     let vec = s.as_mut_vec();
312    ///     assert_eq!(&[104, 101, 108, 108, 111][..], &vec[..]);
313    ///     vec.drain(1..3);
314    /// }
315    /// assert_eq!(s, "hlo");
316    /// ```
317    #[inline]
318    pub const unsafe fn as_mut_vec(&mut self) -> &mut TinyVec<u8, N> {
319        &mut self.buf
320    }
321
322    /// Pushes a character into the string
323    pub fn push(&mut self, c: char) {
324        let len = c.len_utf8();
325        if len == 1 {
326            self.buf.push(c as u8);
327        } else {
328            let mut buf = [0_u8; 4];
329            c.encode_utf8(&mut buf);
330            self.buf.copy_from_slice(&buf[..len]);
331        }
332    }
333
334    /// Tries to push a character. If the string doesn't have enough capacity to store
335    /// the new char, returns an [Err] variant.
336    ///
337    /// # Errors
338    /// If pushing the character would've caused the buffer to grow.
339    pub fn push_within_capacity(&mut self, c: char) -> Result<(), char> {
340        let len = c.len_utf8();
341        if self.buf.len() + len > self.buf.capacity() {
342            return Err(c)
343        }
344        if len == 1 {
345            unsafe { self.buf.push_unchecked(c as u8) };
346        } else {
347            let mut buf = [0_u8; 4];
348            c.encode_utf8(&mut buf);
349            self.buf.copy_from_slice(&buf[..len]);
350        }
351        Ok(())
352    }
353
354
355    /// Returns the last char of this string, if present
356    ///
357    /// # Example
358    /// ```
359    /// use tiny_str::TinyString;
360    ///
361    /// let mut s = TinyString::<10>::new();
362    ///
363    /// s.push_str("abcd");
364    ///
365    /// assert_eq!(s.pop(), Some('d'));
366    /// assert_eq!(s, "abc");
367    /// ```
368    pub fn pop(&mut self) -> Option<char> {
369        let c = self.chars().next_back()?;
370        let new_len = self.len() - c.len_utf8();
371        unsafe {
372            self.buf.set_len(new_len);
373        }
374        Some(c)
375    }
376
377    /// Pushes a str slice into this string
378    #[inline]
379    pub fn push_str(&mut self, s: &str) {
380        self.buf.copy_from_slice(s.as_bytes());
381    }
382
383    /// Tries to push a str slice. If this `TinyString` doesn't have enough
384    /// capacity to store the new slice, returns an [Err] variant.
385    ///
386    /// # Errors
387    /// If pushing the string would've caused the buffer to grow.
388    pub fn push_str_within_capacity<'a>(&mut self, s: &'a str) -> Result<(), &'a str> {
389        if self.buf.len() + s.len() > self.buf.capacity() {
390            Err(s)
391        } else {
392            self.buf.copy_from_slice(s.as_bytes());
393            Ok(())
394        }
395    }
396    /// Shrinks the capacity of this `String` with a lower bound.
397    ///
398    /// The capacity will remain at least as large as both the length
399    /// and the supplied value.
400    ///
401    /// If the current capacity is less than the lower limit, this is a no-op.
402    ///
403    /// # Examples
404    ///
405    /// ```
406    /// use tiny_str::TinyString;
407    ///
408    /// let mut s = TinyString::<5>::from("foobar");
409    ///
410    /// s.reserve(100);
411    /// assert!(s.capacity() >= 100);
412    ///
413    /// s.shrink_to(10);
414    /// assert!(s.capacity() >= 10, "{}", s.capacity());
415    /// s.shrink_to(0);
416    /// assert!(s.capacity() >= 6);
417    /// ```
418    #[inline]
419    #[cfg(feature = "alloc")]
420    pub fn shrink_to(&mut self, min_capacity: usize) {
421        self.buf.shrink_to(min_capacity)
422    }
423
424    /// Shrinks the capacity of `self` to match its length.
425    ///
426    /// # Examples
427    /// ```
428    /// use tiny_str::TinyString;
429    /// let mut s = TinyString::<5>::from("foobar");
430    ///
431    /// s.reserve(100);
432    /// assert!(s.capacity() >= 100);
433    ///
434    /// s.shrink_to_fit();
435    /// assert_eq!(6, s.capacity());
436    /// ```
437    #[inline]
438    #[cfg(feature = "alloc")]
439    pub fn shrink_to_fit(&mut self) {
440        self.buf.shrink_to_fit();
441    }
442
443    /// Clears the string
444    ///
445    /// # Example
446    /// ```
447    /// use tiny_str::TinyString;
448    ///
449    /// let mut s: TinyString<5> = TinyString::from("Hello");
450    /// s.clear();
451    ///
452    /// assert!(s.is_empty());
453    /// assert_eq!(s.as_str(), "");
454    /// ```
455    #[inline]
456    pub fn clear(&mut self) {
457        self.buf.clear();
458    }
459
460    /// Reserves space for, at least, n bytes
461    #[inline]
462    pub fn reserve(&mut self, n: usize) {
463        self.buf.reserve(n);
464    }
465
466    /// Like [reserve](Self::reserve), but on failure returns an [Err] variant
467    /// with a [ResizeError], instead of panicking.
468    #[inline]
469    pub fn try_reserve(&mut self, n: usize) -> Result<(), ResizeError> {
470        self.buf.try_reserve(n)
471    }
472
473    /// Reserves space for exactly n more bytes
474    #[inline]
475    pub fn reserve_exact(&mut self, n: usize) {
476        self.buf.reserve_exact(n);
477    }
478
479    /// Like [reserve_exact](Self::reserve_exact), but on failure returns an [Err] variant
480    /// with a [ResizeError], instead of panicking.
481    #[inline]
482    pub fn try_reserve_exact(&mut self, n: usize) -> Result<(), ResizeError> {
483        self.buf.try_reserve_exact(n)
484    }
485
486    /// Converts this TinyString into a boxed str
487    ///
488    /// # Example
489    /// ```
490    /// use tiny_str::TinyString;
491    ///
492    /// let mut s = TinyString::<10>::new();
493    /// s.push_str("abc");
494    ///
495    /// let b = s.into_boxed_str();
496    /// assert_eq!(&*b, "abc");
497    /// ```
498    #[cfg(feature = "alloc")]
499    pub fn into_boxed_str(self) -> Box<str> {
500        let b = self.buf.into_boxed_slice();
501        unsafe { alloc::str::from_boxed_utf8_unchecked(b) }
502    }
503
504    /// Copies the slice from the given range to the back
505    /// of this string.
506    ///
507    /// # Panics
508    /// - If the range is invalid for [0, self.len)
509    /// - If either the start or the end of the range fall
510    ///   outside a char boundary
511    ///
512    /// # Example
513    /// ```
514    /// use tiny_str::TinyString;
515    ///
516    /// let mut s = TinyString::<10>::from("abcdefg");
517    ///
518    /// s.extend_from_within(3..=5);
519    ///
520    /// assert_eq!(s, "abcdefgdef");
521    /// ```
522    pub fn extend_from_within<R>(&mut self, range: R)
523    where
524        R: RangeBounds<usize>
525    {
526        let Range { start, end } = self.slice_range(range, self.len());
527        self.buf.extend_from_within_copied(start..end);
528    }
529
530    /// Consumes and leaks the `TinyString`, returning a mutable reference to the contents,
531    /// `&'a mut str`.
532    ///
533    /// This method shrinks the buffer, and moves it to the heap in case it lived
534    /// on the stack.
535    ///
536    /// This function is mainly useful for data that lives for the remainder of
537    /// the program's life. Dropping the returned reference will cause a memory
538    /// leak.
539    ///
540    /// # Example
541    /// ```
542    /// let x = tiny_str::TinyString::<10>::from("ABCDEFG");
543    ///
544    /// let static_ref: &'static mut str = x.leak();
545    /// static_ref.make_ascii_lowercase();
546    ///
547    /// assert_eq!(static_ref, "abcdefg");
548    /// # // FIXME(https://github.com/rust-lang/miri/issues/3670):
549    /// # // use -Zmiri-disable-leak-check instead of unleaking in tests meant to leak.
550    /// # drop(unsafe{Box::from_raw(static_ref)})
551    /// ```
552    #[cfg(feature = "alloc")]
553    pub fn leak<'a>(mut self) -> &'a mut str {
554        self.buf.move_to_heap_exact();
555        self.buf.shrink_to_fit_heap_only();
556        unsafe {
557            let bytes = self.buf.leak();
558            str::from_utf8_unchecked_mut(bytes)
559        }
560    }
561
562    /// Splits the string into two at the given byte index.
563    ///
564    /// Returns a newly allocated `String`. `self` contains bytes `[0, at)`, and
565    /// the returned `String` contains bytes `[at, len)`. `at` must be on the
566    /// boundary of a UTF-8 code point.
567    ///
568    /// Note that the capacity of `self` does not change.
569    ///
570    /// # Panics
571    ///
572    /// Panics if `at` is not on a `UTF-8` code point boundary, or if it is beyond the last
573    /// code point of the string.
574    ///
575    /// # Examples
576    /// ```
577    /// let mut hello = tiny_str::TinyString::<8>::from("Hello, World!");
578    /// let world = hello.split_off(7);
579    /// assert_eq!(hello, "Hello, ");
580    /// assert_eq!(world, "World!");
581    /// ```
582    #[inline]
583    #[must_use = "use `.truncate()` if you don't need the other half"]
584    pub fn split_off(&mut self, at: usize) -> TinyString<N> {
585        assert!(self.is_char_boundary(at));
586        let other = self.buf.split_off(at);
587        unsafe { TinyString::from_utf8_unchecked(other) }
588    }
589
590    /// Shortens this `TinyString` to the specified length.
591    ///
592    /// If `new_len` is greater than or equal to the string's current length, this has no
593    /// effect.
594    ///
595    /// Note that this method has no effect on the allocated capacity
596    /// of the string
597    ///
598    /// # Panics
599    ///
600    /// Panics if `new_len` does not lie on a [`char`] boundary.
601    ///
602    /// # Example
603    /// ```
604    /// let mut s = tiny_str::TinyString::<6>::from("hello");
605    ///
606    /// s.truncate(2);
607    ///
608    /// assert_eq!(s, "he");
609    /// ```
610    pub fn truncate(&mut self, new_len: usize) {
611        assert!(self.is_char_boundary(new_len));
612        self.buf.truncate(new_len);
613    }
614
615    /// Inserts a character into this `TinyString` at a byte position.
616    ///
617    /// This is an *O*(*n*) operation as it requires copying every element in the
618    /// buffer.
619    ///
620    /// # Panics
621    ///
622    /// Panics if `index` is larger than the `TinyString`'s length, or if it does not
623    /// lie on a [`char`] boundary.
624    ///
625    /// # Example
626    /// ```
627    /// let mut s = tiny_str::TinyString::<10>::from("Hello world :)");
628    ///
629    /// s.insert(5, '@');
630    ///
631    /// assert_eq!(s, "Hello@ world :)");
632    /// ```
633    pub fn insert(&mut self, index: usize, ch: char) {
634        assert!(self.is_char_boundary(index));
635        let mut buf = [0; 4];
636        ch.encode_utf8(&mut buf);
637        let len = ch.len_utf8();
638        self.buf.insert_slice(index, &buf[..len]).unwrap_or_else(|_| {
639            unreachable!("We've checked the index in the assertion above")
640        })
641    }
642
643    /// Inserts a string slice into this `TinyString` at a byte position.
644    ///
645    /// This is an *O*(*n*) operation as it requires copying every element in the
646    /// buffer.
647    ///
648    /// # Panics
649    ///
650    /// Panics if `index` is larger than the `TinyString`'s length, or if it does not
651    /// lie on a [`char`] boundary.
652    ///
653    /// # Example
654    /// ```
655    /// let mut s = tiny_str::TinyString::<8>::from("Heworld");
656    ///
657    /// s.insert_str(2, "llo ");
658    ///
659    /// assert_eq!("Hello world", s);
660    /// ```
661    pub fn insert_str(&mut self, index: usize, s: &str) {
662        assert!(self.is_char_boundary(index));
663        self.buf.insert_slice(index, s.as_bytes()).unwrap_or_else(|_| {
664            unreachable!("We've checked the index in the assertion above")
665        })
666    }
667}
668
669impl<const N: usize> Default for TinyString<N> {
670    fn default() -> Self {
671        Self::new()
672    }
673}
674
675impl<const N: usize> Deref for TinyString<N> {
676    type Target = str;
677
678    fn deref(&self) -> &Self::Target {
679        self.as_str()
680    }
681}
682
683impl<const N: usize> DerefMut for TinyString<N> {
684    fn deref_mut(&mut self) -> &mut Self::Target {
685        self.as_mut_str()
686    }
687}
688
689impl<const N: usize> From<&str> for TinyString<N> {
690    fn from(value: &str) -> Self {
691        let mut s = Self::with_capacity(value.len());
692        s.push_str(value);
693        s
694    }
695}
696
697impl<const N: usize> TryFrom<&[u8]> for TinyString<N> {
698    type Error = Utf8Error;
699
700    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
701        str::from_utf8(value)?;
702        Ok(unsafe { Self::from_utf8_unchecked(TinyVec::from_slice_copied(value)) })
703    }
704}
705
706impl<const N: usize> TryFrom<TinyVec<u8, N>> for TinyString<N> {
707    type Error = Utf8Error;
708
709    fn try_from(value: TinyVec<u8, N>) -> Result<Self, Self::Error> {
710        Self::from_utf8(value)
711    }
712}
713
714#[cfg(feature = "alloc")]
715impl<const N: usize> TryFrom<Vec<u8>> for TinyString<N> {
716    type Error = Utf8Error;
717
718    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
719        str::from_utf8(value.as_slice())?;
720        Ok(unsafe { Self::from_utf8_unchecked(TinyVec::from_vec(value)) })
721    }
722}
723
724#[cfg(feature = "alloc")]
725impl<const N: usize> From<String> for TinyString<N> {
726    fn from(value: String) -> Self {
727        let vec = Vec::from(value);
728        let vec = TinyVec::<_, N>::from_vec(vec);
729        unsafe { Self::from_utf8_unchecked(vec) }
730    }
731}
732
733impl<const N: usize> From<TinyString<N>> for TinyVec<u8, N> {
734    fn from(value: TinyString<N>) -> Self {
735        value.buf
736    }
737}
738
739#[cfg(feature = "alloc")]
740impl<const N: usize> From<TinyString<N>> for Vec<u8> {
741    fn from(value: TinyString<N>) -> Self {
742        value.buf.into_vec()
743    }
744}
745
746#[cfg(feature = "alloc")]
747impl<const N: usize> From<TinyString<N>> for Box<str> {
748    fn from(value: TinyString<N>) -> Self {
749        value.into_boxed_str()
750    }
751}
752
753#[cfg(feature = "alloc")]
754impl<const N: usize> From<Box<str>> for TinyString<N> {
755    fn from(value: Box<str>) -> Self {
756        let vec = value.as_bytes();
757        let s = TinyVec::from(vec);
758        unsafe { Self::from_utf8_unchecked(s) }
759    }
760}
761
762macro_rules! impl_from_iter {
763    ($( $( { $($tok:tt)* } )? $t:ty),* $(,)?) => {
764        $(
765            impl< $($($tok)*, )? const N: usize> FromIterator<$t> for TinyString<N> {
766                fn from_iter<T: IntoIterator<Item = $t>>(iter: T) -> Self {
767                    let mut s = Self::new();
768                    s.extend(iter);
769                    s
770                }
771            }
772        )*
773    };
774}
775
776impl_from_iter!(
777    char,
778    {'a} &'a char,
779    {'a} &'a str,
780    {'a, const M: usize} Cow<'a, M>
781);
782
783#[cfg(feature = "alloc")]
784impl_from_iter!(Box<str>);
785
786impl<const N: usize> Extend<char> for TinyString<N> {
787    fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
788        let iter = iter.into_iter();
789        let (lower, _) = iter.size_hint();
790        self.reserve(lower);
791        for c in iter {
792            self.push(c);
793        }
794    }
795
796    #[cfg(feature = "use-nightly-features")]
797    #[inline]
798    fn extend_one(&mut self, item: char) {
799        self.push(item);
800    }
801
802    #[cfg(feature = "use-nightly-features")]
803    #[inline]
804    fn extend_reserve(&mut self, additional: usize) {
805        self.reserve(additional);
806    }
807}
808
809impl<'a, const N: usize> Extend<&'a char> for TinyString<N> {
810    fn extend<T: IntoIterator<Item = &'a char>>(&mut self, iter: T) {
811        iter.into_iter().for_each(|slice| self.push(*slice));
812    }
813
814    #[cfg(feature = "use-nightly-features")]
815    #[inline]
816    fn extend_one(&mut self, item: &'a char) {
817        self.push(*item);
818    }
819
820    #[cfg(feature = "use-nightly-features")]
821    #[inline]
822    fn extend_reserve(&mut self, additional: usize) {
823        self.reserve(additional);
824    }
825}
826
827impl<'a, const N: usize> Extend<&'a str> for TinyString<N> {
828    fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) {
829        iter.into_iter().for_each(|slice| self.push_str(slice));
830    }
831
832    #[cfg(feature = "use-nightly-features")]
833    #[inline]
834    fn extend_one(&mut self, item: &'a str) {
835        self.push_str(item);
836    }
837}
838
839#[cfg(feature = "alloc")]
840impl<const N: usize> Extend<Box<str>> for TinyString<N> {
841    fn extend<T: IntoIterator<Item = Box<str>>>(&mut self, iter: T) {
842        iter.into_iter().for_each(|slice| self.push_str(&slice));
843    }
844
845    #[cfg(feature = "use-nightly-features")]
846    #[inline]
847    fn extend_one(&mut self, item: Box<str>) {
848        self.push_str(&item);
849    }
850}
851
852impl<'a, const N: usize, const M: usize> Extend<Cow<'a, M>> for TinyString<N> {
853    fn extend<T: IntoIterator<Item = Cow<'a, M>>>(&mut self, iter: T) {
854        iter.into_iter().for_each(|slice| self.push_str(&slice));
855    }
856
857    #[cfg(feature = "use-nightly-features")]
858    #[inline]
859    fn extend_one(&mut self, item: Cow<'a, M>) {
860        self.push_str(&item);
861    }
862}
863
864impl<const N: usize, const M: usize> PartialEq<TinyString<M>> for TinyString<N> {
865    fn eq(&self, other: &TinyString<M>) -> bool {
866        self.as_bytes() == other.as_bytes()
867    }
868}
869
870impl<const N: usize> Eq for TinyString<N> { }
871
872impl<'a, const N: usize, const M: usize> PartialEq<Cow<'a, M>> for TinyString<N> {
873    fn eq(&self, other: &Cow<'a, M>) -> bool {
874        self.as_bytes() == other.as_bytes()
875    }
876}
877
878#[cfg(feature = "alloc")]
879impl<const N: usize> PartialEq<String> for TinyString<N> {
880    fn eq(&self, other: &String) -> bool {
881        self.as_bytes() == other.as_bytes()
882    }
883}
884
885impl<const N: usize, const M: usize> PartialEq<TinyVec<u8, M>> for TinyString<N> {
886    fn eq(&self, other: &TinyVec<u8, M>) -> bool {
887        self.as_bytes() == other.as_slice()
888    }
889}
890
891impl<'a, const N: usize, const M: usize> PartialEq<tiny_vec::Cow<'a, u8, M>> for TinyString<N> {
892    fn eq(&self, other: &tiny_vec::Cow<'a, u8, M>) -> bool {
893        self.as_bytes() == other.as_slice()
894    }
895}
896
897#[cfg(feature = "alloc")]
898impl<const N: usize> PartialEq<Vec<u8>> for TinyString<N> {
899    fn eq(&self, other: &Vec<u8>) -> bool {
900        self.as_bytes() == other.as_slice()
901    }
902}
903
904impl<const N: usize> PartialEq<str> for TinyString<N> {
905    fn eq(&self, other: &str) -> bool {
906        self.as_str() == other
907    }
908}
909
910impl<'a, const N: usize> PartialEq<&'a str> for TinyString<N> {
911    fn eq(&self, other: &&'a str) -> bool {
912        self.as_str() == *other
913    }
914}
915
916impl<const N: usize> PartialEq<[u8]> for TinyString<N> {
917    fn eq(&self, other: &[u8]) -> bool {
918        self.as_bytes() == other
919    }
920}
921
922impl<const N: usize> PartialEq<TinyString<N>> for &str {
923    fn eq(&self, other: &TinyString<N>) -> bool {
924        self.as_bytes() == other.as_bytes()
925    }
926}
927
928impl<const N: usize> PartialOrd<TinyString<N>> for TinyString<N> {
929    #[inline]
930    fn partial_cmp(&self, other: &TinyString<N>) -> Option<core::cmp::Ordering> {
931        Some(self.cmp(other))
932    }
933}
934
935impl<const N: usize> Ord for TinyString<N> {
936    #[inline]
937    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
938        self.buf.cmp(&other.buf)
939    }
940}
941
942impl<const N: usize> Clone for TinyString<N> {
943    #[inline]
944    fn clone(&self) -> Self {
945        Self { buf: self.buf.clone() }
946    }
947
948    #[inline]
949    fn clone_from(&mut self, source: &Self) {
950        self.buf.clone_from(&source.buf);
951    }
952}
953
954impl<const N: usize> AsRef<[u8]> for TinyString<N> {
955    #[inline]
956    fn as_ref(&self) -> &[u8] {
957        self.as_bytes()
958    }
959}
960
961impl<const N: usize> AsRef<str> for TinyString<N> {
962    #[inline]
963    fn as_ref(&self) -> &str {
964        self.as_str()
965    }
966}
967
968impl<const N: usize> AsMut<str> for TinyString<N> {
969    #[inline]
970    fn as_mut(&mut self) -> &mut str {
971        self.as_mut_str()
972    }
973}
974
975impl<const N: usize> AsRef<TinyString<N>> for TinyString<N> {
976    #[inline]
977    fn as_ref(&self) -> &TinyString<N> {
978        self
979    }
980}
981
982impl<const N: usize> AsMut<TinyString<N>> for TinyString<N> {
983    #[inline]
984    fn as_mut(&mut self) -> &mut TinyString<N> {
985        self
986    }
987}
988
989impl<const N: usize> Hash for TinyString<N> {
990    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
991        self.buf.hash(state);
992    }
993}
994
995impl<const N: usize> fmt::Debug for TinyString<N> {
996    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
997        write!(f, "{:?}", self.bytes())
998    }
999}
1000
1001impl<const N: usize> Display for TinyString<N> {
1002    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1003        write!(f, "{}", self.as_str())
1004    }
1005}
1006
1007impl<const N: usize> FromStr for TinyString<N> {
1008    type Err = core::convert::Infallible;
1009
1010    fn from_str(s: &str) -> Result<Self, Self::Err> {
1011        Ok(Self::from(s))
1012    }
1013}
1014
1015impl<const N: usize> core::fmt::Write for TinyString<N> {
1016    fn write_str(&mut self, s: &str) -> fmt::Result {
1017        self.push_str(s);
1018        Ok(())
1019    }
1020
1021    fn write_char(&mut self, c: char) -> fmt::Result {
1022        self.push(c);
1023        Ok(())
1024    }
1025}
1026
1027#[cfg(feature = "serde")]
1028impl<const N: usize> serde::Serialize for TinyString<N> {
1029    #[inline]
1030    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1031    where
1032        S: serde::Serializer
1033    {
1034        self.buf.serialize(serializer)
1035    }
1036}
1037
1038#[cfg(feature = "serde")]
1039impl<'de, const N: usize> serde::Deserialize<'de> for TinyString<N> {
1040    #[inline]
1041    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1042    where
1043        D: serde::Deserializer<'de>
1044    {
1045        let buf = TinyVec::<u8, N>::deserialize(deserializer)?;
1046        Ok(Self { buf })
1047    }
1048}
1049
1050#[cfg(test)]
1051mod test;