1#![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#![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
115pub 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 #[inline]
150 pub const fn new() -> Self {
151 Self { buf: TinyVec::new() }
152 }
153
154 pub fn with_capacity(cap: usize) -> Self {
156 Self { buf: TinyVec::with_capacity(cap) }
157 }
158
159 pub fn try_with_capacity(cap: usize) -> Result<Self,ResizeError> {
165 Ok(Self { buf: TinyVec::try_with_capacity(cap)? })
166 }
167
168 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 #[inline(always)]
182 pub const unsafe fn from_utf8_unchecked(utf8: TinyVec<u8, N>) -> Self {
183 Self { buf: utf8 }
184 }
185
186 pub fn repeat(slice: &str, n: usize) -> Self {
198 Self {
199 buf: TinyVec::repeat(slice.as_bytes(), n)
200 }
201 }
202
203 #[inline]
205 pub const fn len(&self) -> usize { self.buf.len() }
206
207 #[inline]
209 pub const fn is_empty(&self) -> bool { self.buf.is_empty() }
210
211 #[inline]
213 pub const fn capacity(&self) -> usize { self.buf.capacity() }
214
215 #[inline]
234 pub const fn lives_on_stack(&self) -> bool { self.buf.lives_on_stack() }
235
236 #[inline]
238 pub const fn as_str(&self) -> &str {
239 unsafe { str::from_utf8_unchecked(self.buf.as_slice()) }
240 }
241
242 #[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 #[inline]
252 pub const fn as_ptr(&self) -> *const u8 {
253 self.buf.as_ptr()
254 }
255
256 #[inline]
260 pub const fn as_mut_ptr(&mut self) -> *mut u8 {
261 self.buf.as_mut_ptr()
262 }
263
264 #[inline]
266 pub const fn as_bytes(&self) -> &[u8] {
267 self.buf.as_slice()
268 }
269
270 #[inline]
293 pub const unsafe fn as_mut_bytes(&mut self) -> &mut [u8] {
294 self.buf.as_mut_slice()
295 }
296
297 #[inline]
318 pub const unsafe fn as_mut_vec(&mut self) -> &mut TinyVec<u8, N> {
319 &mut self.buf
320 }
321
322 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 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 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 #[inline]
379 pub fn push_str(&mut self, s: &str) {
380 self.buf.copy_from_slice(s.as_bytes());
381 }
382
383 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 #[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 #[inline]
438 #[cfg(feature = "alloc")]
439 pub fn shrink_to_fit(&mut self) {
440 self.buf.shrink_to_fit();
441 }
442
443 #[inline]
456 pub fn clear(&mut self) {
457 self.buf.clear();
458 }
459
460 #[inline]
462 pub fn reserve(&mut self, n: usize) {
463 self.buf.reserve(n);
464 }
465
466 #[inline]
469 pub fn try_reserve(&mut self, n: usize) -> Result<(), ResizeError> {
470 self.buf.try_reserve(n)
471 }
472
473 #[inline]
475 pub fn reserve_exact(&mut self, n: usize) {
476 self.buf.reserve_exact(n);
477 }
478
479 #[inline]
482 pub fn try_reserve_exact(&mut self, n: usize) -> Result<(), ResizeError> {
483 self.buf.try_reserve_exact(n)
484 }
485
486 #[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 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 #[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 #[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 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 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 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;