1#![cfg_attr(not(doctest), doc = include_str!("../README.md"))]
2
3use bytes::{Bytes, BytesMut};
4use simdutf8::basic::{from_utf8, Utf8Error};
5use std::{
6 borrow::{Borrow, Cow},
7 cmp::Ordering,
8 convert::Infallible,
9 fmt, hash,
10 hint::unreachable_unchecked,
11 iter,
12 mem::MaybeUninit,
13 ops::Deref,
14 str::FromStr,
15 sync::Arc,
16};
17
18#[derive(Clone)]
20pub struct FastStr(Repr);
21
22#[cfg(all(test, target_pointer_width = "64"))]
23mod size_asserts {
24 static_assertions::assert_eq_size!(super::FastStr, [u8; 40]); }
26
27impl FastStr {
28 #[inline]
33 pub fn new<T>(text: T) -> Self
34 where
35 T: AsRef<str>,
36 {
37 Self(Repr::new(text))
38 }
39
40 #[inline]
46 #[doc(hidden)]
47 #[deprecated(
48 since = "0.2.13",
49 note = "The inline threshold is not stable. Please use `FastStr::new()` instead."
50 )]
51 pub fn new_inline(s: &str) -> Self {
52 Self(Repr::new_inline(s))
53 }
54
55 #[inline]
58 pub fn new_u8_slice(v: &[u8]) -> Result<Self, Utf8Error> {
59 let s = from_utf8(v)?;
60 Ok(Self::new(s))
61 }
62
63 #[inline]
70 pub unsafe fn new_u8_slice_unchecked(v: &[u8]) -> Self {
71 let s = unsafe { std::str::from_utf8_unchecked(v) };
72 Self::new(s)
73 }
74
75 #[inline]
77 pub const fn empty() -> Self {
78 Self(Repr::empty())
79 }
80
81 #[inline]
83 pub fn from_arc_str(s: Arc<str>) -> Self {
84 Self(Repr::from_arc_str(s))
85 }
86
87 #[inline]
89 pub fn from_string(s: String) -> Self {
90 Self(Repr::from_string(s))
91 }
92
93 #[inline]
95 pub fn from_arc_string(s: Arc<String>) -> Self {
96 Self(Repr::from_arc_string(s))
97 }
98
99 #[inline]
102 pub fn from_bytes(b: Bytes) -> Result<Self, Utf8Error> {
103 from_utf8(&b)?;
104 Ok(unsafe { Self::from_bytes_unchecked(b) })
106 }
107
108 #[inline]
115 pub unsafe fn from_bytes_unchecked(b: Bytes) -> Self {
116 Self(Repr::from_bytes_unchecked(b))
117 }
118
119 #[inline]
122 pub fn from_bytes_mut(b: BytesMut) -> Result<Self, Utf8Error> {
123 from_utf8(&b)?;
124 Ok(unsafe { Self::from_bytes_mut_unchecked(b) })
126 }
127
128 #[inline]
135 pub unsafe fn from_bytes_mut_unchecked(b: BytesMut) -> Self {
136 let v = b.freeze();
137 Self::from_bytes_unchecked(v)
138 }
139
140 #[inline]
142 pub const fn from_static_str(s: &'static str) -> Self {
143 Self(Repr::StaticStr(s))
144 }
145
146 #[inline]
149 pub fn from_vec_u8(v: Vec<u8>) -> Result<Self, Utf8Error> {
150 from_utf8(&v)?;
151 Ok(unsafe { Self::from_vec_u8_unchecked(v) })
153 }
154
155 #[inline]
162 pub unsafe fn from_vec_u8_unchecked(v: Vec<u8>) -> Self {
163 Self::from_bytes_unchecked(v.into())
164 }
165
166 #[deprecated(
169 since = "0.2.13",
170 note = "This method is not really zero-cost. Use `new_u8_slice` instead."
171 )]
172 #[inline]
173 pub fn from_u8_slice(v: &[u8]) -> Result<Self, Utf8Error> {
174 Self::new_u8_slice(v)
175 }
176
177 #[deprecated(
184 since = "0.2.13",
185 note = "This method is not really zero-cost. Use `new_u8_slice_unchecked` instead."
186 )]
187 #[inline]
188 pub unsafe fn from_u8_slice_unchecked(v: &[u8]) -> Self {
189 Self::new_u8_slice_unchecked(v)
190 }
191}
192
193impl FastStr {
194 #[inline(always)]
196 pub fn as_str(&self) -> &str {
197 self.0.as_str()
198 }
199
200 #[inline(always)]
202 pub fn into_bytes(self) -> Bytes {
203 self.0.into_bytes()
204 }
205
206 #[inline(always)]
208 pub fn len(&self) -> usize {
209 self.0.len()
210 }
211
212 #[inline(always)]
214 pub fn is_empty(&self) -> bool {
215 self.0.is_empty()
216 }
217
218 #[inline(always)]
220 pub fn slice_ref(&self, subset: &str) -> Self {
221 Self(self.0.slice_ref(subset.as_bytes()))
222 }
223
224 #[inline(always)]
230 pub unsafe fn index(&self, start: usize, end: usize) -> Self {
231 Self(self.0.slice_ref(&self.as_bytes()[start..end]))
232 }
233
234 #[deprecated(
236 since = "0.2.13",
237 note = "This method does not really express the `into` semantic. Use `to_string` instead."
238 )]
239 #[inline(always)]
240 pub fn into_string(self) -> String {
241 #[allow(deprecated)]
242 self.0.into_string()
243 }
244
245 #[inline]
252 #[doc(hidden)]
253 pub fn deep_clone_bytes(&self) -> Self {
254 Self(self.0.deep_clone_bytes())
255 }
256
257 fn from_char_iter<I: iter::Iterator<Item = char>>(mut iter: I) -> Self {
258 let (min_size, _) = iter.size_hint();
259 if min_size > INLINE_CAP {
260 let s: String = iter.collect();
261 return Self(Repr::Bytes(Bytes::from(s)));
262 }
263 let mut len = 0;
264 let mut buf = [0u8; INLINE_CAP];
265 while let Some(ch) = iter.next() {
266 let size = ch.len_utf8();
267 if size + len > INLINE_CAP {
268 let (min_remaining, _) = iter.size_hint();
269 let mut s = String::with_capacity(size + len + min_remaining);
270 s.push_str(unsafe { core::str::from_utf8_unchecked(&buf[..len]) });
271 s.push(ch);
272 s.extend(iter);
273 return Self(Repr::Bytes(Bytes::from(s)));
274 }
275 ch.encode_utf8(&mut buf[len..]);
276 len += size;
277 }
278 Self(Repr::Inline { len: len as u8, buf })
279 }
280}
281
282impl Default for FastStr {
283 #[inline]
284 fn default() -> Self {
285 Self::empty()
286 }
287}
288
289impl AsRef<[u8]> for FastStr {
290 #[inline]
291 fn as_ref(&self) -> &[u8] {
292 self.0.as_ref()
293 }
294}
295
296impl AsRef<str> for FastStr {
297 #[inline(always)]
298 fn as_ref(&self) -> &str {
299 self.as_str()
300 }
301}
302impl Deref for FastStr {
303 type Target = str;
304
305 #[inline]
306 fn deref(&self) -> &str {
307 self.as_str()
308 }
309}
310
311impl From<FastStr> for String {
312 #[inline]
313 fn from(val: FastStr) -> Self {
314 #[allow(deprecated)]
315 val.into_string()
316 }
317}
318
319impl From<FastStr> for Bytes {
320 #[inline]
321 fn from(val: FastStr) -> Self {
322 val.into_bytes()
323 }
324}
325
326impl PartialEq<FastStr> for FastStr {
327 #[inline]
328 fn eq(&self, other: &FastStr) -> bool {
329 self.as_str() == other.as_str()
330 }
331}
332
333impl Eq for FastStr {}
334
335impl PartialEq<str> for FastStr {
336 #[inline]
337 fn eq(&self, other: &str) -> bool {
338 self.as_str() == other
339 }
340}
341
342impl PartialEq<FastStr> for str {
343 #[inline]
344 fn eq(&self, other: &FastStr) -> bool {
345 other == self
346 }
347}
348
349impl<'a> PartialEq<&'a str> for FastStr {
350 #[inline]
351 fn eq(&self, other: &&'a str) -> bool {
352 self == *other
353 }
354}
355
356impl<'a> PartialEq<FastStr> for &'a str {
357 #[inline]
358 fn eq(&self, other: &FastStr) -> bool {
359 *self == other
360 }
361}
362
363impl PartialEq<String> for FastStr {
364 #[inline]
365 fn eq(&self, other: &String) -> bool {
366 self.as_str() == other
367 }
368}
369
370impl PartialEq<FastStr> for String {
371 #[inline]
372 fn eq(&self, other: &FastStr) -> bool {
373 other == self
374 }
375}
376
377impl<'a> PartialEq<&'a String> for FastStr {
378 #[inline]
379 fn eq(&self, other: &&'a String) -> bool {
380 self == *other
381 }
382}
383
384impl<'a> PartialEq<FastStr> for &'a String {
385 #[inline]
386 fn eq(&self, other: &FastStr) -> bool {
387 *self == other
388 }
389}
390
391impl Ord for FastStr {
392 #[inline]
393 fn cmp(&self, other: &FastStr) -> Ordering {
394 self.as_str().cmp(other.as_str())
395 }
396}
397
398impl PartialOrd for FastStr {
399 #[inline]
400 fn partial_cmp(&self, other: &FastStr) -> Option<Ordering> {
401 Some(self.cmp(other))
402 }
403}
404
405impl hash::Hash for FastStr {
406 #[inline]
407 fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
408 self.as_str().hash(hasher)
409 }
410}
411
412impl fmt::Debug for FastStr {
413 #[inline]
414 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
415 fmt::Debug::fmt(self.as_str(), f)
416 }
417}
418
419impl fmt::Display for FastStr {
420 #[inline]
421 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
422 fmt::Display::fmt(self.as_str(), f)
423 }
424}
425
426impl iter::FromIterator<char> for FastStr {
427 #[inline]
428 fn from_iter<I: iter::IntoIterator<Item = char>>(iter: I) -> FastStr {
429 let iter = iter.into_iter();
430 Self::from_char_iter(iter)
431 }
432}
433
434fn build_from_str_iter<T>(mut iter: impl Iterator<Item = T>) -> FastStr
435where
436 T: AsRef<str>,
437 String: iter::Extend<T>,
438{
439 let mut len = 0;
440 let mut buf = [0u8; INLINE_CAP];
441 while let Some(slice) = iter.next() {
442 let slice = slice.as_ref();
443 let size = slice.len();
444 if size + len > INLINE_CAP {
445 let mut s = String::with_capacity(size + len);
446 s.push_str(unsafe { core::str::from_utf8_unchecked(&buf[..len]) });
447 s.push_str(slice);
448 s.extend(iter);
449 return FastStr(Repr::Bytes(Bytes::from(s)));
450 }
451 buf[len..][..size].copy_from_slice(slice.as_bytes());
452 len += size;
453 }
454 FastStr(Repr::Inline { len: len as u8, buf })
455}
456
457impl iter::FromIterator<String> for FastStr {
458 #[inline]
459 fn from_iter<I: iter::IntoIterator<Item = String>>(iter: I) -> FastStr {
460 build_from_str_iter(iter.into_iter())
461 }
462}
463
464impl<'a> iter::FromIterator<&'a String> for FastStr {
465 #[inline]
466 fn from_iter<I: iter::IntoIterator<Item = &'a String>>(iter: I) -> FastStr {
467 FastStr::from_iter(iter.into_iter().map(|x| x.as_str()))
468 }
469}
470
471impl<'a> iter::FromIterator<&'a str> for FastStr {
472 #[inline]
473 fn from_iter<I: iter::IntoIterator<Item = &'a str>>(iter: I) -> FastStr {
474 build_from_str_iter(iter.into_iter())
475 }
476}
477
478impl Borrow<str> for FastStr {
479 #[inline]
480 fn borrow(&self) -> &str {
481 self.as_str()
482 }
483}
484
485impl FromStr for FastStr {
486 type Err = Infallible;
487
488 #[inline]
489 fn from_str(s: &str) -> Result<FastStr, Self::Err> {
490 Ok(FastStr::new(s))
491 }
492}
493
494impl From<Arc<str>> for FastStr {
506 #[inline]
507 fn from(val: Arc<str>) -> Self {
508 Self::from_arc_str(val)
509 }
510}
511
512impl From<String> for FastStr {
513 #[inline]
514 fn from(val: String) -> Self {
515 Self::new(val)
516 }
517}
518
519impl From<Arc<String>> for FastStr {
520 #[inline]
521 fn from(val: Arc<String>) -> Self {
522 Self::from_arc_string(val)
523 }
524}
525
526impl From<&'static str> for FastStr {
527 #[inline]
528 fn from(val: &'static str) -> Self {
529 Self::from_static_str(val)
530 }
531}
532
533impl From<Cow<'static, str>> for FastStr {
534 #[inline]
535 fn from(val: Cow<'static, str>) -> Self {
536 match val {
537 Cow::Borrowed(s) => Self::from_static_str(s),
538 Cow::Owned(s) => Self::from_string(s),
539 }
540 }
541}
542
543#[derive(Debug, Clone, PartialEq, Eq, Hash)]
544pub struct FastStrOption(Option<FastStr>);
545
546impl FastStrOption {
547 pub fn new(inner: Option<FastStr>) -> Self {
548 Self(inner)
549 }
550}
551
552impl Deref for FastStrOption {
553 type Target = Option<FastStr>;
554
555 #[inline]
556 fn deref(&self) -> &Self::Target {
557 &self.0
558 }
559}
560
561impl From<Option<String>> for FastStrOption {
562 #[inline]
563 fn from(val: Option<String>) -> Self {
564 Self(val.map(FastStr::from))
565 }
566}
567
568const INLINE_CAP: usize = 38;
569
570#[derive(Clone)]
571enum Repr {
572 Empty,
573 Bytes(Bytes),
574 ArcStr(Arc<str>),
575 ArcString(Arc<String>),
576 StaticStr(&'static str),
577 Inline { len: u8, buf: [u8; INLINE_CAP] },
578}
579
580impl Repr {
581 #[inline]
582 fn new<T>(text: T) -> Self
583 where
584 T: AsRef<str>,
585 {
586 let text = text.as_ref();
587 if text.is_empty() {
588 return Self::Empty;
589 }
590 {
591 let len = text.len();
592 if len <= INLINE_CAP {
593 return unsafe { Self::new_inline_impl(text) };
595 }
596 }
597 Self::ArcString(Arc::new(text.to_string()))
598 }
600
601 fn new_inline(s: &str) -> Self {
602 if s.len() > INLINE_CAP {
603 panic!("[FastStr] string is too long to inline");
604 }
605 unsafe { Self::new_inline_impl(s) }
607 }
608
609 unsafe fn new_inline_impl(s: &str) -> Self {
613 #[allow(invalid_value, clippy::uninit_assumed_init)]
614 let mut inl = Self::Inline {
615 len: s.len() as u8,
616 buf: MaybeUninit::uninit().assume_init(),
617 };
618 match inl {
619 Self::Inline {
620 ref mut len,
621 ref mut buf,
622 } => {
623 std::ptr::copy(s.as_ptr(), buf.as_mut_ptr(), s.len());
625 *len = s.len() as u8;
626 }
627 _ => unreachable_unchecked(),
628 }
629 inl
630 }
631
632 #[inline]
633 const fn empty() -> Self {
634 Self::Empty
635 }
636
637 #[inline]
638 fn from_arc_str(s: Arc<str>) -> Self {
639 Self::ArcStr(s)
640 }
641
642 #[inline]
643 fn from_string(s: String) -> Self {
644 Self::new(s)
645 }
646
647 #[inline]
648 fn from_arc_string(s: Arc<String>) -> Self {
649 match Arc::try_unwrap(s) {
650 Ok(s) => Self::from_string(s),
651 Err(s) => Self::ArcString(s),
652 }
653 }
654
655 #[inline]
657 unsafe fn from_bytes_unchecked(bytes: Bytes) -> Self {
658 if bytes.is_empty() {
659 return Self::Empty;
660 }
661 if bytes.len() <= INLINE_CAP {
662 let mut buf = [0; INLINE_CAP];
663 buf[..bytes.len()].copy_from_slice(bytes.as_ref());
664 return Self::Inline {
665 len: bytes.len() as u8,
666 buf,
667 };
668 }
669 Self::Bytes(bytes)
670 }
671
672 #[inline]
673 fn len(&self) -> usize {
674 match self {
675 Self::Empty => 0,
676 Self::Bytes(bytes) => bytes.len(),
677 Self::ArcStr(arc_str) => arc_str.len(),
678 Self::ArcString(arc_string) => arc_string.len(),
679 Self::StaticStr(s) => s.len(),
680 Self::Inline { len, .. } => *len as usize,
681 }
682 }
683
684 #[inline]
685 fn is_empty(&self) -> bool {
686 match self {
687 Self::Empty => true,
688 Self::Bytes(bytes) => bytes.is_empty(),
689 Self::ArcStr(arc_str) => arc_str.is_empty(),
690 Self::ArcString(arc_string) => arc_string.is_empty(),
691 Self::StaticStr(s) => s.is_empty(),
692 Self::Inline { len, .. } => *len == 0,
693 }
694 }
695
696 #[inline]
697 fn as_str(&self) -> &str {
698 match self {
699 Self::Empty => "",
700 Self::Bytes(bytes) => unsafe { std::str::from_utf8_unchecked(bytes) },
702 Self::ArcStr(arc_str) => arc_str,
703 Self::ArcString(arc_string) => arc_string,
704 Self::StaticStr(s) => s,
705 Self::Inline { len, buf } => unsafe { std::str::from_utf8_unchecked(&buf[..*len as usize]) },
706 }
707 }
708
709 #[inline]
710 #[deprecated]
711 fn into_string(self) -> String {
712 match self {
713 Self::Empty => String::new(),
714 Self::Bytes(bytes) => unsafe { String::from_utf8_unchecked(bytes.into()) },
715 Self::ArcStr(arc_str) => arc_str.to_string(),
716 Self::ArcString(arc_string) => {
717 Arc::try_unwrap(arc_string).unwrap_or_else(|arc| (*arc).clone())
718 }
719 Self::StaticStr(s) => s.to_string(),
720 Self::Inline { len, buf } => unsafe {
721 String::from_utf8_unchecked(buf[..len as usize].to_vec())
722 },
723 }
724 }
725
726 #[inline]
727 fn into_bytes(self) -> Bytes {
728 match self {
729 Self::Empty => Bytes::new(),
730 Self::Bytes(bytes) => bytes,
731 Self::ArcStr(arc_str) => Bytes::from(arc_str.as_bytes().to_vec()),
732 Self::ArcString(arc_string) => {
733 Bytes::from(Arc::try_unwrap(arc_string).unwrap_or_else(|arc| (*arc).clone()))
734 }
735 Self::StaticStr(s) => Bytes::from_static(s.as_bytes()),
736 Self::Inline { len, buf } => Bytes::from(buf[..len as usize].to_vec()),
737 }
738 }
739
740 #[inline]
741 fn deep_clone_bytes(&self) -> Self {
742 match self {
743 Self::Empty => Self::Empty,
744 Self::Bytes(bytes) => unsafe { Self::new(std::str::from_utf8_unchecked(bytes)) },
746 Self::ArcStr(arc_str) => Self::ArcStr(Arc::clone(arc_str)),
747 Self::ArcString(arc_string) => Self::ArcString(Arc::clone(arc_string)),
748 Self::StaticStr(s) => Self::StaticStr(s),
749 Self::Inline { len, buf } => Self::Inline {
750 len: *len,
751 buf: *buf,
752 },
753 }
754 }
755
756 #[inline]
757 fn slice_ref(&self, subset: &[u8]) -> Self {
758 if subset.is_empty() {
759 return Self::Empty;
760 }
761 let bytes_p = self.as_ref().as_ptr() as usize;
762 let bytes_len = self.len();
763
764 let sub_p = subset.as_ptr() as usize;
765 let sub_len = subset.len();
766
767 assert!(
768 sub_p >= bytes_p,
769 "subset pointer ({:p}) is smaller than self pointer ({:p})",
770 subset.as_ptr(),
771 self.as_ref().as_ptr(),
772 );
773 assert!(
774 sub_p + sub_len <= bytes_p + bytes_len,
775 "subset is out of bounds: self = ({:p}, {}), subset = ({:p}, {})",
776 self.as_ref().as_ptr(),
777 bytes_len,
778 subset.as_ptr(),
779 sub_len,
780 );
781
782 let sub_offset = sub_p - bytes_p;
783 match self {
784 Repr::Empty => panic!("invalid slice ref, self is empty but subset is not"),
785 Repr::Bytes(b) => Self::Bytes(b.slice_ref(subset)),
786 Repr::ArcStr(s) => Self::Bytes(Bytes::copy_from_slice(
787 s[sub_offset..sub_offset + sub_len].as_bytes(),
788 )),
789 Repr::ArcString(s) => Self::Bytes(Bytes::copy_from_slice(
790 s[sub_offset..sub_offset + sub_len].as_bytes(),
791 )),
792 Repr::StaticStr(s) => Self::StaticStr(unsafe {
793 std::str::from_utf8_unchecked(&s.as_bytes()[sub_offset..sub_offset + sub_len])
794 }),
795 Repr::Inline { len: _, buf } => Self::Inline {
796 len: sub_len as u8,
797 buf: {
798 let mut new_buf = [0; INLINE_CAP];
799 new_buf[..sub_len].copy_from_slice(&buf[sub_offset..sub_offset + sub_len]);
800 new_buf
801 },
802 },
803 }
804 }
805}
806
807impl AsRef<[u8]> for Repr {
808 #[inline]
809 fn as_ref(&self) -> &[u8] {
810 match self {
811 Self::Empty => &[],
812 Self::Bytes(bytes) => bytes.as_ref(),
813 Self::ArcStr(arc_str) => arc_str.as_bytes(),
814 Self::ArcString(arc_string) => arc_string.as_bytes(),
815 Self::StaticStr(s) => s.as_bytes(),
816 Self::Inline { len, buf } => &buf[..*len as usize],
817 }
818 }
819}
820#[cfg(feature = "redis")]
821mod redis;
822
823#[cfg(feature = "serde")]
824mod serde;
825
826#[cfg(feature = "sqlx")]
827mod sqlx;