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;
108
109mod cow;
110pub use cow::Cow;
111
112const MAX_N_STACK_ELEMENTS: usize = tiny_vec::n_elements_for_stack::<u8>();
113
114/// A string that can store a small amount of bytes on the stack.
115#[derive(Clone)]
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.extend_from_slice_copied(&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.extend_from_slice_copied(&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.extend_from_slice_copied(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.extend_from_slice_copied(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 pub fn shrink_to(&mut self, min_capacity: usize) {
420 self.buf.shrink_to(min_capacity)
421 }
422
423 /// Shrinks the capacity of `self` to match its length.
424 ///
425 /// # Examples
426 /// ```
427 /// use tiny_str::TinyString;
428 /// let mut s = TinyString::<5>::from("foobar");
429 ///
430 /// s.reserve(100);
431 /// assert!(s.capacity() >= 100);
432 ///
433 /// s.shrink_to_fit();
434 /// assert_eq!(6, s.capacity());
435 /// ```
436 #[inline]
437 #[cfg(feature = "alloc")]
438 pub fn shrink_to_fit(&mut self) {
439 self.buf.shrink_to_fit();
440 }
441
442 /// Clears the string
443 ///
444 /// # Example
445 /// ```
446 /// use tiny_str::TinyString;
447 ///
448 /// let mut s: TinyString<5> = TinyString::from("Hello");
449 /// s.clear();
450 ///
451 /// assert!(s.is_empty());
452 /// assert_eq!(s.as_str(), "");
453 /// ```
454 #[inline]
455 pub fn clear(&mut self) {
456 self.buf.clear();
457 }
458
459 /// Reserves space for, at least, n bytes
460 #[inline]
461 pub fn reserve(&mut self, n: usize) {
462 self.buf.reserve(n);
463 }
464
465 /// Like [reserve](Self::reserve), but on failure returns an [Err] variant
466 /// with a [ResizeError], instead of panicking.
467 #[inline]
468 pub fn try_reserve(&mut self, n: usize) -> Result<(), ResizeError> {
469 self.buf.try_reserve(n)
470 }
471
472 /// Reserves space for exactly n more bytes
473 #[inline]
474 pub fn reserve_exact(&mut self, n: usize) {
475 self.buf.reserve_exact(n);
476 }
477
478 /// Like [reserve_exact](Self::reserve_exact), but on failure returns an [Err] variant
479 /// with a [ResizeError], instead of panicking.
480 #[inline]
481 pub fn try_reserve_exact(&mut self, n: usize) -> Result<(), ResizeError> {
482 self.buf.try_reserve_exact(n)
483 }
484
485 /// Converts this TinyString into a boxed str
486 ///
487 /// # Example
488 /// ```
489 /// use tiny_str::TinyString;
490 ///
491 /// let mut s = TinyString::<10>::new();
492 /// s.push_str("abc");
493 ///
494 /// let b = s.into_boxed_str();
495 /// assert_eq!(&*b, "abc");
496 /// ```
497 #[cfg(feature = "alloc")]
498 pub fn into_boxed_str(self) -> Box<str> {
499 let b = self.buf.into_boxed_slice();
500 unsafe { alloc::str::from_boxed_utf8_unchecked(b) }
501 }
502
503 /// Copies the slice from the given range to the back
504 /// of this string.
505 ///
506 /// # Panics
507 /// - If the range is invalid for [0, self.len)
508 /// - If either the start or the end of the range fall
509 /// outside a char boundary
510 ///
511 /// # Example
512 /// ```
513 /// use tiny_str::TinyString;
514 ///
515 /// let mut s = TinyString::<10>::from("abcdefg");
516 ///
517 /// s.extend_from_within(3..=5);
518 ///
519 /// assert_eq!(s, "abcdefgdef");
520 /// ```
521 pub fn extend_from_within<R>(&mut self, range: R)
522 where
523 R: RangeBounds<usize>
524 {
525 let Range { start, end } = self.slice_range(range, self.len());
526 self.buf.extend_from_within_copied(start..end);
527 }
528
529 /// Consumes and leaks the `TinyString`, returning a mutable reference to the contents,
530 /// `&'a mut str`.
531 ///
532 /// This method shrinks the buffer, and moves it to the heap in case it lived
533 /// on the stack.
534 ///
535 /// This function is mainly useful for data that lives for the remainder of
536 /// the program's life. Dropping the returned reference will cause a memory
537 /// leak.
538 ///
539 /// # Example
540 /// ```
541 /// let x = tiny_str::TinyString::<10>::from("ABCDEFG");
542 ///
543 /// let static_ref: &'static mut str = x.leak();
544 /// static_ref.make_ascii_lowercase();
545 ///
546 /// assert_eq!(static_ref, "abcdefg");
547 /// # // FIXME(https://github.com/rust-lang/miri/issues/3670):
548 /// # // use -Zmiri-disable-leak-check instead of unleaking in tests meant to leak.
549 /// # drop(unsafe{Box::from_raw(static_ref)})
550 /// ```
551 #[cfg(feature = "alloc")]
552 pub fn leak<'a>(mut self) -> &'a mut str {
553 self.buf.move_to_heap_exact();
554 self.buf.shrink_to_fit_heap_only();
555 unsafe {
556 let bytes = self.buf.leak();
557 str::from_utf8_unchecked_mut(bytes)
558 }
559 }
560
561 /// Splits the string into two at the given byte index.
562 ///
563 /// Returns a newly allocated `String`. `self` contains bytes `[0, at)`, and
564 /// the returned `String` contains bytes `[at, len)`. `at` must be on the
565 /// boundary of a UTF-8 code point.
566 ///
567 /// Note that the capacity of `self` does not change.
568 ///
569 /// # Panics
570 ///
571 /// Panics if `at` is not on a `UTF-8` code point boundary, or if it is beyond the last
572 /// code point of the string.
573 ///
574 /// # Examples
575 /// ```
576 /// let mut hello = tiny_str::TinyString::<8>::from("Hello, World!");
577 /// let world = hello.split_off(7);
578 /// assert_eq!(hello, "Hello, ");
579 /// assert_eq!(world, "World!");
580 /// ```
581 #[inline]
582 #[must_use = "use `.truncate()` if you don't need the other half"]
583 pub fn split_off(&mut self, at: usize) -> TinyString<N> {
584 assert!(self.is_char_boundary(at));
585 let other = self.buf.split_off(at);
586 unsafe { TinyString::from_utf8_unchecked(other) }
587 }
588
589 /// Shortens this `TinyString` to the specified length.
590 ///
591 /// If `new_len` is greater than or equal to the string's current length, this has no
592 /// effect.
593 ///
594 /// Note that this method has no effect on the allocated capacity
595 /// of the string
596 ///
597 /// # Panics
598 ///
599 /// Panics if `new_len` does not lie on a [`char`] boundary.
600 ///
601 /// # Example
602 /// ```
603 /// let mut s = tiny_str::TinyString::<6>::from("hello");
604 ///
605 /// s.truncate(2);
606 ///
607 /// assert_eq!(s, "he");
608 /// ```
609 pub fn truncate(&mut self, new_len: usize) {
610 assert!(self.is_char_boundary(new_len));
611 self.buf.truncate(new_len);
612 }
613
614 /// Inserts a character into this `TinyString` at a byte position.
615 ///
616 /// This is an *O*(*n*) operation as it requires copying every element in the
617 /// buffer.
618 ///
619 /// # Panics
620 ///
621 /// Panics if `index` is larger than the `TinyString`'s length, or if it does not
622 /// lie on a [`char`] boundary.
623 ///
624 /// # Example
625 /// ```
626 /// let mut s = tiny_str::TinyString::<10>::from("Hello world :)");
627 ///
628 /// s.insert(5, '@');
629 ///
630 /// assert_eq!(s, "Hello@ world :)");
631 /// ```
632 pub fn insert(&mut self, index: usize, ch: char) {
633 assert!(self.is_char_boundary(index));
634 let mut buf = [0; 4];
635 ch.encode_utf8(&mut buf);
636 let len = ch.len_utf8();
637 self.buf.insert_slice(index, &buf[..len]).unwrap_or_else(|_| {
638 unreachable!("We've checked the index in the assertion above")
639 })
640 }
641
642 /// Inserts a string slice into this `TinyString` at a byte position.
643 ///
644 /// This is an *O*(*n*) operation as it requires copying every element in the
645 /// buffer.
646 ///
647 /// # Panics
648 ///
649 /// Panics if `index` is larger than the `TinyString`'s length, or if it does not
650 /// lie on a [`char`] boundary.
651 ///
652 /// # Example
653 /// ```
654 /// let mut s = tiny_str::TinyString::<8>::from("Heworld");
655 ///
656 /// s.insert_str(2, "llo ");
657 ///
658 /// assert_eq!("Hello world", s);
659 /// ```
660 pub fn insert_str(&mut self, index: usize, s: &str) {
661 assert!(self.is_char_boundary(index));
662 self.buf.insert_slice(index, s.as_bytes()).unwrap_or_else(|_| {
663 unreachable!("We've checked the index in the assertion above")
664 })
665 }
666}
667
668impl<const N: usize> Default for TinyString<N> {
669 fn default() -> Self {
670 Self::new()
671 }
672}
673
674impl<const N: usize> Deref for TinyString<N> {
675 type Target = str;
676
677 fn deref(&self) -> &Self::Target {
678 self.as_str()
679 }
680}
681
682impl<const N: usize> DerefMut for TinyString<N> {
683 fn deref_mut(&mut self) -> &mut Self::Target {
684 self.as_mut_str()
685 }
686}
687
688impl<const N: usize> From<&str> for TinyString<N> {
689 fn from(value: &str) -> Self {
690 let mut s = Self::with_capacity(value.len());
691 s.push_str(value);
692 s
693 }
694}
695
696impl<const N: usize> TryFrom<&[u8]> for TinyString<N> {
697 type Error = Utf8Error;
698
699 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
700 str::from_utf8(value)?;
701 Ok(unsafe { Self::from_utf8_unchecked(TinyVec::from_slice_copied(value)) })
702 }
703}
704
705impl<const N: usize> TryFrom<TinyVec<u8, N>> for TinyString<N> {
706 type Error = Utf8Error;
707
708 fn try_from(value: TinyVec<u8, N>) -> Result<Self, Self::Error> {
709 Self::from_utf8(value)
710 }
711}
712
713#[cfg(feature = "alloc")]
714impl<const N: usize> TryFrom<Vec<u8>> for TinyString<N> {
715 type Error = Utf8Error;
716
717 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
718 str::from_utf8(value.as_slice())?;
719 Ok(unsafe { Self::from_utf8_unchecked(TinyVec::from_vec(value)) })
720 }
721}
722
723#[cfg(feature = "alloc")]
724impl<const N: usize> From<String> for TinyString<N> {
725 fn from(value: String) -> Self {
726 let vec = Vec::from(value);
727 let vec = TinyVec::<_, N>::from_vec(vec);
728 unsafe { Self::from_utf8_unchecked(vec) }
729 }
730}
731
732impl<const N: usize> From<TinyString<N>> for TinyVec<u8, N> {
733 fn from(value: TinyString<N>) -> Self {
734 value.buf
735 }
736}
737
738#[cfg(feature = "alloc")]
739impl<const N: usize> From<TinyString<N>> for Vec<u8> {
740 fn from(value: TinyString<N>) -> Self {
741 value.buf.into_vec()
742 }
743}
744
745#[cfg(feature = "alloc")]
746impl<const N: usize> From<TinyString<N>> for Box<str> {
747 fn from(value: TinyString<N>) -> Self {
748 value.into_boxed_str()
749 }
750}
751
752#[cfg(feature = "alloc")]
753impl<const N: usize> From<Box<str>> for TinyString<N> {
754 fn from(value: Box<str>) -> Self {
755 let vec = value.as_bytes();
756 let s = TinyVec::from(vec);
757 unsafe { Self::from_utf8_unchecked(s) }
758 }
759}
760
761
762impl<const N: usize> FromIterator<char> for TinyString<N> {
763 fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
764 let mut s = Self::new();
765 s.extend(iter);
766 s
767 }
768}
769
770impl<const N: usize> Extend<char> for TinyString<N> {
771 fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
772 let iter = iter.into_iter();
773 let (lower, _) = iter.size_hint();
774 self.reserve(lower);
775 for c in iter {
776 self.push(c);
777 }
778 }
779
780 #[cfg(feature = "use-nightly-features")]
781 #[inline]
782 fn extend_one(&mut self, item: char) {
783 self.push(item);
784 }
785
786 #[cfg(feature = "use-nightly-features")]
787 #[inline]
788 fn extend_reserve(&mut self, additional: usize) {
789 self.reserve(additional);
790 }
791}
792
793impl<const N: usize, S> PartialEq<S> for TinyString<N>
794where
795 S: AsRef<str>,
796{
797 fn eq(&self, other: &S) -> bool {
798 self.as_str() == other.as_ref()
799 }
800}
801
802impl<const N: usize> PartialEq<TinyString<N>> for &str {
803 fn eq(&self, other: &TinyString<N>) -> bool {
804 self.as_bytes() == other.as_bytes()
805 }
806}
807
808impl<const N: usize> Eq for TinyString<N> { }
809
810impl<const N: usize> AsRef<[u8]> for TinyString<N> {
811 fn as_ref(&self) -> &[u8] {
812 self.as_bytes()
813 }
814}
815
816impl<const N: usize> AsRef<str> for TinyString<N> {
817 fn as_ref(&self) -> &str {
818 self.as_str()
819 }
820}
821
822impl<const N: usize> AsMut<str> for TinyString<N> {
823 fn as_mut(&mut self) -> &mut str {
824 self.as_mut_str()
825 }
826}
827
828impl<const N: usize> fmt::Debug for TinyString<N> {
829 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
830 write!(f, "{:?}", self.bytes())
831 }
832}
833
834impl<const N: usize> Display for TinyString<N> {
835 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
836 write!(f, "{}", self.as_str())
837 }
838}
839
840impl<const N: usize> FromStr for TinyString<N> {
841 type Err = core::convert::Infallible;
842
843 fn from_str(s: &str) -> Result<Self, Self::Err> {
844 Ok(Self::from(s))
845 }
846}
847
848#[cfg(feature = "serde")]
849impl<const N: usize> serde::Serialize for TinyString<N> {
850 #[inline]
851 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
852 where
853 S: serde::Serializer
854 {
855 self.buf.serialize(serializer)
856 }
857}
858
859#[cfg(feature = "serde")]
860impl<'de, const N: usize> serde::Deserialize<'de> for TinyString<N> {
861 #[inline]
862 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
863 where
864 D: serde::Deserializer<'de>
865 {
866 let buf = TinyVec::<u8, N>::deserialize(deserializer)?;
867 Ok(Self { buf })
868 }
869}
870
871#[cfg(test)]
872mod test;