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