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