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::ops::{Bound, Deref, DerefMut, Range, RangeBounds};
92use core::str::{self, FromStr, Utf8Error};
93
94#[cfg(feature = "alloc")]
95extern crate alloc;
96#[cfg(feature = "alloc")]
97use alloc::{
98    vec::Vec,
99    boxed::Box,
100    string::String,
101};
102
103use tiny_vec::TinyVec;
104pub use tiny_vec::ResizeError;
105pub mod iter;
106
107pub mod drain;
108pub mod cow;
109pub use cow::Cow;
110
111const MAX_N_STACK_ELEMENTS: usize = tiny_vec::n_elements_for_stack::<u8>();
112
113/// A string that can store a small amount of bytes on the stack.
114#[derive(Clone)]
115pub struct TinyString<const N: usize = MAX_N_STACK_ELEMENTS> {
116    buf: TinyVec<u8, N>,
117}
118
119impl<const N: usize> TinyString<N> {
120    fn slice_range<R>(&self, range: R, len: usize) -> Range<usize>
121    where
122        R: RangeBounds<usize>
123    {
124        let start = match range.start_bound() {
125            Bound::Included(n) => *n,
126            Bound::Excluded(n) => *n + 1,
127            Bound::Unbounded => 0,
128        };
129
130        let end = match range.end_bound() {
131            Bound::Included(n) => *n + 1,
132            Bound::Excluded(n) => *n,
133            Bound::Unbounded => len,
134        };
135
136        assert!(start <= end);
137        assert!(end <= len);
138        assert!(self.is_char_boundary(start));
139        assert!(self.is_char_boundary(end));
140
141        Range { start, end }
142    }
143}
144
145impl<const N: usize> TinyString<N> {
146
147    /// Creates a new [TinyString]
148    #[inline]
149    pub const fn new() -> Self {
150        Self { buf: TinyVec::new() }
151    }
152
153    /// Creates a new [TinyString] with the given capacity
154    pub fn with_capacity(cap: usize) -> Self {
155        Self { buf: TinyVec::with_capacity(cap) }
156    }
157
158    /// Like [with_capacity](Self::with_capacity), but it returns a [Result].
159    ///
160    /// If an allocation error hapens when reserving the memory, returns
161    /// a [ResizeError] unlike [with_capacity](Self::with_capacity), which
162    /// panics in such case.
163    pub fn try_with_capacity(cap: usize) -> Result<Self,ResizeError> {
164        Ok(Self { buf: TinyVec::try_with_capacity(cap)? })
165    }
166
167    /// Creates a new [TinyString] from the given utf8 buffer.
168    ///
169    /// # Errors
170    /// If the byte buffer contains invalid uft8
171    pub fn from_utf8(utf8: TinyVec<u8, N>) -> Result<Self,Utf8Error> {
172        str::from_utf8(utf8.as_slice())?;
173        Ok(Self { buf: utf8 })
174    }
175
176    /// Creates a new [TinyString] from the given utf8 buffer.
177    ///
178    /// # Safety
179    /// The caller must ensure that the given contains valid utf8
180    #[inline(always)]
181    pub const unsafe fn from_utf8_unchecked(utf8: TinyVec<u8, N>) -> Self {
182        Self { buf: utf8 }
183    }
184
185    /// Creates a new `TinyString` by repeating the given `slice` `n` times.
186    ///
187    /// # Panics
188    /// If the capacity requires overflows `isize::MAX`
189    ///
190    /// # Example
191    /// ```
192    /// use tiny_str::TinyString;
193    /// let s = TinyString::<10>::repeat("abc", 5);
194    /// assert_eq!(s.as_str(), "abcabcabcabcabc");
195    /// ```
196    pub fn repeat(slice: &str, n: usize) -> Self {
197        let len = slice.len() * n;
198        let mut s = Self::with_capacity(len);
199        let bytes = slice.as_bytes();
200        for _ in 0..n {
201            s.buf.extend_from_slice_copied(bytes);
202        }
203        s
204    }
205
206    /// Returns the number of elements inside this string
207    #[inline]
208    pub const fn len(&self) -> usize { self.buf.len() }
209
210    /// Returns true if the string is empty
211    #[inline]
212    pub const fn is_empty(&self) -> bool { self.buf.is_empty() }
213
214    /// Returns the allocated capacity for this string
215    #[inline]
216    pub const fn capacity(&self) -> usize { self.buf.capacity() }
217
218    /// Returns true if the string is currently using stack memory.
219    ///
220    /// This means that [Self::len] <= `N`
221    ///
222    /// # Example
223    /// ```
224    /// use tiny_str::TinyString;
225    ///
226    /// let mut vec = TinyString::<5>::new();
227    ///
228    /// for n in 0..5 {
229    ///     vec.push('a')
230    /// }
231    ///
232    /// assert!(vec.lives_on_stack());
233    /// vec.push('a');
234    /// assert!(!vec.lives_on_stack());
235    /// ```
236    #[inline]
237    pub const fn lives_on_stack(&self) -> bool { self.buf.lives_on_stack() }
238
239    /// Returns a str slice
240    #[inline]
241    pub const fn as_str(&self) -> &str {
242        unsafe { str::from_utf8_unchecked(self.buf.as_slice()) }
243    }
244
245    /// Returns a mutable str slice
246    #[inline]
247    pub const fn as_mut_str(&mut self) -> &mut str {
248        unsafe { str::from_utf8_unchecked_mut(self.buf.as_mut_slice()) }
249    }
250
251    /// Returns a const pointer to the buffer
252    ///
253    /// This method shadows [str::as_ptr], to avoid a deref
254    #[inline]
255    pub const fn as_ptr(&self) -> *const u8 {
256        self.buf.as_ptr()
257    }
258
259    /// Returns a mutable pointer to the buffer
260    ///
261    /// This method shadows [str::as_mut_ptr], to avoid a deref
262    #[inline]
263    pub const fn as_mut_ptr(&mut self) -> *mut u8 {
264        self.buf.as_mut_ptr()
265    }
266
267    /// Returns the string as a byte slice
268    #[inline]
269    pub const fn as_bytes(&self) -> &[u8] {
270        self.buf.as_slice()
271    }
272
273    /// Returns the string as a byte slice
274    ///
275    /// Returns the string as a mutable bytes slice
276    ///
277    /// # Safety
278    /// Modifying this byte slice is dangerous, because it can leave the
279    /// buffer on an inconsistent state.
280    /// Strings must be valid UTF8. So manually changing the byte contents
281    /// of the string could lead to bugs.
282    ///
283    /// # Example
284    /// ```
285    /// use tiny_str::TinyString;
286    ///
287    /// let mut s = TinyString::<10>::from("hello");
288    /// unsafe {
289    ///     let slice = s.as_mut_bytes();
290    ///     assert_eq!(&[104, 101, 108, 108, 111][..], &slice[..]);
291    ///     slice.reverse();
292    /// }
293    /// assert_eq!(s, "olleh");
294    /// ```
295    #[inline]
296    pub const unsafe fn as_mut_bytes(&mut self) -> &mut [u8] {
297        self.buf.as_mut_slice()
298    }
299
300    /// Returns a mutable reference to the contents of this `TinyString`
301    ///
302    /// # Safety
303    /// Modifying this [TinyVec] is dangerous, because it can leave the
304    /// buffer on an inconsistent state.
305    /// Strings must be valid UTF8. So mutating the vector without respecting
306    /// that could lead to bugs.
307    ///
308    /// # Example
309    /// ```
310    /// use tiny_str::TinyString;
311    ///
312    /// let mut s = TinyString::<10>::from("hello");
313    /// unsafe {
314    ///     let vec = s.as_mut_vec();
315    ///     assert_eq!(&[104, 101, 108, 108, 111][..], &vec[..]);
316    ///     vec.drain(1..3);
317    /// }
318    /// assert_eq!(s, "hlo");
319    /// ```
320    #[inline]
321    pub const unsafe fn as_mut_vec(&mut self) -> &mut TinyVec<u8, N> {
322        &mut self.buf
323    }
324
325    /// Pushes a character into the string
326    pub fn push(&mut self, c: char) {
327        let len = c.len_utf8();
328        if len == 1 {
329            self.buf.push(c as u8);
330        } else {
331            let mut buf = [0_u8; 4];
332            c.encode_utf8(&mut buf);
333            self.buf.extend_from_slice(&buf[..len]);
334        }
335    }
336
337    /// Tries to push a character. If the string doesn't have enough capacity to store
338    /// the new char, returns an [Err] variant.
339    ///
340    /// # Errors
341    /// If pushing the character would've caused the buffer to grow.
342    pub fn push_within_capacity(&mut self, c: char) -> Result<(), char> {
343        let len = c.len_utf8();
344        if self.buf.len() + len > self.buf.capacity() {
345            return Err(c)
346        }
347        if len == 1 {
348            unsafe { self.buf.push_unchecked(c as u8) };
349        } else {
350            let mut buf = [0_u8; 4];
351            c.encode_utf8(&mut buf);
352            unsafe { self.buf.extend_from_slice_copied_unchecked(&buf[..len]) };
353        }
354        Ok(())
355    }
356
357
358    /// Returns the last char of this string, if present
359    ///
360    /// # Example
361    /// ```
362    /// use tiny_str::TinyString;
363    ///
364    /// let mut s = TinyString::<10>::new();
365    ///
366    /// s.push_str("abcd");
367    ///
368    /// assert_eq!(s.pop(), Some('d'));
369    /// assert_eq!(s, "abc");
370    /// ```
371    pub fn pop(&mut self) -> Option<char> {
372        let c = self.chars().next_back()?;
373        let new_len = self.len() - c.len_utf8();
374        unsafe {
375            self.buf.set_len(new_len);
376        }
377        Some(c)
378    }
379
380    /// Pushes a str slice into this string
381    #[inline]
382    pub fn push_str(&mut self, s: &str) {
383        self.buf.extend_from_slice_copied(s.as_bytes());
384    }
385
386    /// Tries to push a str slice. If this `TinyString` doesn't have enough
387    /// capacity to store the new slice, returns an [Err] variant.
388    ///
389    /// # Errors
390    /// If pushing the string would've caused the buffer to grow.
391    pub fn push_str_within_capacity<'a>(&mut self, s: &'a str) -> Result<(), &'a str> {
392        if self.buf.len() + s.len() > self.buf.capacity() {
393            Err(s)
394        } else {
395            unsafe { self.buf.extend_from_slice_copied_unchecked(s.as_bytes()) };
396            Ok(())
397        }
398    }
399
400    /// Shrinks the capacity of this string to fit exactly it's length
401    #[inline]
402    #[cfg(feature = "alloc")]
403    pub fn shrink_to_fit(&mut self) {
404        self.buf.shrink_to_fit();
405    }
406
407    /// Clears the string
408    ///
409    /// # Example
410    /// ```
411    /// use tiny_str::TinyString;
412    ///
413    /// let mut s: TinyString<5> = TinyString::from("Hello");
414    /// s.clear();
415    ///
416    /// assert!(s.is_empty());
417    /// assert_eq!(s.as_str(), "");
418    /// ```
419    #[inline]
420    pub fn clear(&mut self) {
421        self.buf.clear();
422    }
423
424    /// Reserves space for, at least, n bytes
425    #[inline]
426    pub fn reserve(&mut self, n: usize) {
427        self.buf.reserve(n);
428    }
429
430    /// Like [reserve](Self::reserve), but on failure returns an [Err] variant
431    /// with a [ResizeError], instead of panicking.
432    #[inline]
433    pub fn try_reserve(&mut self, n: usize) -> Result<(), ResizeError> {
434        self.buf.try_reserve(n)
435    }
436
437    /// Reserves space for exactly n more bytes
438    #[inline]
439    pub fn reserve_exact(&mut self, n: usize) {
440        self.buf.reserve_exact(n);
441    }
442
443    /// Like [reserve_exact](Self::reserve_exact), but on failure returns an [Err] variant
444    /// with a [ResizeError], instead of panicking.
445    #[inline]
446    pub fn try_reserve_exact(&mut self, n: usize) -> Result<(), ResizeError> {
447        self.buf.try_reserve_exact(n)
448    }
449
450    /// Converts this TinyString into a boxed str
451    ///
452    /// # Example
453    /// ```
454    /// use tiny_str::TinyString;
455    ///
456    /// let mut s = TinyString::<10>::new();
457    /// s.push_str("abc");
458    ///
459    /// let b = s.into_boxed_str();
460    /// assert_eq!(&*b, "abc");
461    /// ```
462    #[cfg(feature = "alloc")]
463    pub fn into_boxed_str(self) -> Box<str> {
464        let b = self.buf.into_boxed_slice();
465        unsafe { alloc::str::from_boxed_utf8_unchecked(b) }
466    }
467
468    /// Copies the slice from the given range to the back
469    /// of this string.
470    ///
471    /// # Panics
472    /// - If the range is invalid for [0, self.len)
473    /// - If either the start or the end of the range fall
474    ///   outside a char boundary
475    ///
476    /// # Example
477    /// ```
478    /// use tiny_str::TinyString;
479    ///
480    /// let mut s = TinyString::<10>::from("abcdefg");
481    ///
482    /// s.extend_from_within(3..=5);
483    ///
484    /// assert_eq!(s, "abcdefgdef");
485    /// ```
486    pub fn extend_from_within<R>(&mut self, range: R)
487    where
488        R: RangeBounds<usize>
489    {
490        let Range { start, end } = self.slice_range(range, self.len());
491        self.buf.extend_from_within_copied(start..end);
492    }
493
494    /// Consumes and leaks the `TinyString`, returning a mutable reference to the contents,
495    /// `&'a mut str`.
496    ///
497    /// This method shrinks the buffer, and moves it to the heap in case it lived
498    /// on the stack.
499    ///
500    /// This function is mainly useful for data that lives for the remainder of
501    /// the program's life. Dropping the returned reference will cause a memory
502    /// leak.
503    ///
504    /// # Example
505    /// ```
506    /// let x = tiny_str::TinyString::<10>::from("ABCDEFG");
507    ///
508    /// let static_ref: &'static mut str = x.leak();
509    /// static_ref.make_ascii_lowercase();
510    ///
511    /// assert_eq!(static_ref, "abcdefg");
512    /// # // FIXME(https://github.com/rust-lang/miri/issues/3670):
513    /// # // use -Zmiri-disable-leak-check instead of unleaking in tests meant to leak.
514    /// # drop(unsafe{Box::from_raw(static_ref)})
515    /// ```
516    #[cfg(feature = "alloc")]
517    pub fn leak<'a>(mut self) -> &'a mut str {
518        self.buf.move_to_heap_exact();
519        self.buf.shrink_to_fit_heap_only();
520        unsafe {
521            let bytes = self.buf.leak();
522            str::from_utf8_unchecked_mut(bytes)
523        }
524    }
525
526    /// Splits the string into two at the given byte index.
527    ///
528    /// Returns a newly allocated `String`. `self` contains bytes `[0, at)`, and
529    /// the returned `String` contains bytes `[at, len)`. `at` must be on the
530    /// boundary of a UTF-8 code point.
531    ///
532    /// Note that the capacity of `self` does not change.
533    ///
534    /// # Panics
535    ///
536    /// Panics if `at` is not on a `UTF-8` code point boundary, or if it is beyond the last
537    /// code point of the string.
538    ///
539    /// # Examples
540    /// ```
541    /// let mut hello = tiny_str::TinyString::<8>::from("Hello, World!");
542    /// let world = hello.split_off(7);
543    /// assert_eq!(hello, "Hello, ");
544    /// assert_eq!(world, "World!");
545    /// ```
546    #[inline]
547    #[must_use = "use `.truncate()` if you don't need the other half"]
548    pub fn split_off(&mut self, at: usize) -> TinyString<N> {
549        assert!(self.is_char_boundary(at));
550        let other = self.buf.split_off(at);
551        unsafe { TinyString::from_utf8_unchecked(other) }
552    }
553
554    /// Shortens this `TinyString` to the specified length.
555    ///
556    /// If `new_len` is greater than or equal to the string's current length, this has no
557    /// effect.
558    ///
559    /// Note that this method has no effect on the allocated capacity
560    /// of the string
561    ///
562    /// # Panics
563    ///
564    /// Panics if `new_len` does not lie on a [`char`] boundary.
565    ///
566    /// # Example
567    /// ```
568    /// let mut s = tiny_str::TinyString::<6>::from("hello");
569    ///
570    /// s.truncate(2);
571    ///
572    /// assert_eq!(s, "he");
573    /// ```
574    pub fn truncate(&mut self, new_len: usize) {
575        assert!(self.is_char_boundary(new_len));
576        self.buf.truncate(new_len);
577    }
578
579    /// Inserts a character into this `TinyString` at a byte position.
580    ///
581    /// This is an *O*(*n*) operation as it requires copying every element in the
582    /// buffer.
583    ///
584    /// # Panics
585    ///
586    /// Panics if `index` is larger than the `TinyString`'s length, or if it does not
587    /// lie on a [`char`] boundary.
588    ///
589    /// # Example
590    /// ```
591    /// let mut s = tiny_str::TinyString::<10>::from("Hello world :)");
592    ///
593    /// s.insert(5, '@');
594    ///
595    /// assert_eq!(s, "Hello@ world :)");
596    /// ```
597    pub fn insert(&mut self, index: usize, ch: char) {
598        assert!(self.is_char_boundary(index));
599        let mut buf = [0; 4];
600        ch.encode_utf8(&mut buf);
601        let len = ch.len_utf8();
602        self.buf.insert_slice(index, &buf[..len]).unwrap_or_else(|_| {
603            unreachable!("We've checked the index in the assertion above")
604        })
605    }
606
607    /// Inserts a string slice into this `TinyString` at a byte position.
608    ///
609    /// This is an *O*(*n*) operation as it requires copying every element in the
610    /// buffer.
611    ///
612    /// # Panics
613    ///
614    /// Panics if `index` is larger than the `TinyString`'s length, or if it does not
615    /// lie on a [`char`] boundary.
616    ///
617    /// # Example
618    /// ```
619    /// let mut s = tiny_str::TinyString::<8>::from("Heworld");
620    ///
621    /// s.insert_str(2, "llo ");
622    ///
623    /// assert_eq!("Hello world", s);
624    /// ```
625    pub fn insert_str(&mut self, index: usize, s: &str) {
626        assert!(self.is_char_boundary(index));
627        self.buf.insert_slice(index, s.as_bytes()).unwrap_or_else(|_| {
628            unreachable!("We've checked the index in the assertion above")
629        })
630    }
631}
632
633impl<const N: usize> Default for TinyString<N> {
634    fn default() -> Self {
635        Self::new()
636    }
637}
638
639impl<const N: usize> Deref for TinyString<N> {
640    type Target = str;
641
642    fn deref(&self) -> &Self::Target {
643        self.as_str()
644    }
645}
646
647impl<const N: usize> DerefMut for TinyString<N> {
648    fn deref_mut(&mut self) -> &mut Self::Target {
649        self.as_mut_str()
650    }
651}
652
653impl<const N: usize> From<&str> for TinyString<N> {
654    fn from(value: &str) -> Self {
655        let mut s = Self::with_capacity(value.len());
656        s.push_str(value);
657        s
658    }
659}
660
661impl<const N: usize> TryFrom<&[u8]> for TinyString<N> {
662    type Error = Utf8Error;
663
664    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
665        str::from_utf8(value)?;
666        Ok(unsafe { Self::from_utf8_unchecked(TinyVec::from_slice_copied(value)) })
667    }
668}
669
670impl<const N: usize> TryFrom<TinyVec<u8, N>> for TinyString<N> {
671    type Error = Utf8Error;
672
673    fn try_from(value: TinyVec<u8, N>) -> Result<Self, Self::Error> {
674        Self::from_utf8(value)
675    }
676}
677
678#[cfg(feature = "alloc")]
679impl<const N: usize> TryFrom<Vec<u8>> for TinyString<N> {
680    type Error = Utf8Error;
681
682    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
683        str::from_utf8(value.as_slice())?;
684        Ok(unsafe { Self::from_utf8_unchecked(TinyVec::from_vec(value)) })
685    }
686}
687
688#[cfg(feature = "alloc")]
689impl<const N: usize> From<String> for TinyString<N> {
690    fn from(value: String) -> Self {
691        let vec = Vec::from(value);
692        let vec = TinyVec::<_, N>::from_vec(vec);
693        unsafe { Self::from_utf8_unchecked(vec) }
694    }
695}
696
697impl<const N: usize> From<TinyString<N>> for TinyVec<u8, N> {
698    fn from(value: TinyString<N>) -> Self {
699        value.buf
700    }
701}
702
703#[cfg(feature = "alloc")]
704impl<const N: usize> From<TinyString<N>> for Vec<u8> {
705    fn from(value: TinyString<N>) -> Self {
706        value.buf.into_vec()
707    }
708}
709
710#[cfg(feature = "alloc")]
711impl<const N: usize> From<TinyString<N>> for Box<str> {
712    fn from(value: TinyString<N>) -> Self {
713        value.into_boxed_str()
714    }
715}
716
717#[cfg(feature = "alloc")]
718impl<const N: usize> From<Box<str>> for TinyString<N> {
719    fn from(value: Box<str>) -> Self {
720        let vec = value.as_bytes();
721        let s = TinyVec::from(vec);
722        unsafe { Self::from_utf8_unchecked(s) }
723    }
724}
725
726
727impl<const N: usize> FromIterator<char> for TinyString<N> {
728    fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
729        let mut s = Self::new();
730        s.extend(iter);
731        s
732    }
733}
734
735impl<const N: usize> Extend<char> for TinyString<N> {
736    fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
737        let iter = iter.into_iter();
738        let cap = match iter.size_hint() {
739            (_, Some(n)) => n,
740            (n, _) => n,
741        };
742        self.reserve(cap);
743        for c in iter {
744            self.push(c);
745        }
746    }
747
748    #[cfg(feature = "use-nightly-features")]
749    fn extend_one(&mut self, item: char) {
750        self.push(item);
751    }
752}
753
754impl<const N: usize, S> PartialEq<S> for TinyString<N>
755where
756    S: AsRef<str>,
757{
758    fn eq(&self, other: &S) -> bool {
759        self.as_str() == other.as_ref()
760    }
761}
762
763impl<const N: usize> PartialEq<TinyString<N>> for &str {
764    fn eq(&self, other: &TinyString<N>) -> bool {
765        self.as_bytes() == other.as_bytes()
766    }
767}
768
769impl<const N: usize> Eq for TinyString<N> { }
770
771impl<const N: usize> AsRef<[u8]> for TinyString<N> {
772    fn as_ref(&self) -> &[u8] {
773        self.as_bytes()
774    }
775}
776
777impl<const N: usize> AsRef<str> for TinyString<N> {
778    fn as_ref(&self) -> &str {
779        self.as_str()
780    }
781}
782
783impl<const N: usize> AsMut<str> for TinyString<N> {
784    fn as_mut(&mut self) -> &mut str {
785        self.as_mut_str()
786    }
787}
788
789impl<const N: usize> fmt::Debug for TinyString<N> {
790    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
791        write!(f, "{:?}", self.bytes())
792    }
793}
794
795impl<const N: usize> Display for TinyString<N> {
796    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
797        write!(f, "{}", self.as_str())
798    }
799}
800
801impl<const N: usize> FromStr for TinyString<N> {
802    type Err = core::convert::Infallible;
803
804    fn from_str(s: &str) -> Result<Self, Self::Err> {
805        Ok(Self::from(s))
806    }
807}
808
809#[cfg(test)]
810mod test;