1#![deny(warnings)]
8#![deny(missing_docs)]
9#![allow(clippy::enum_clike_unportable_variant)]
12
13use std::{
14 borrow::Borrow, fmt, hash, mem::ManuallyDrop, ops::Deref, ptr::NonNull, slice::from_raw_parts,
15 str::from_utf8_unchecked, sync::Arc,
16};
17
18use serde::{Deserialize, Serialize};
19
20mod clone;
21
22pub use self::clone::CheapMetaString;
23
24pub mod interning;
25use self::interning::{InternedString, InternedStringState, Interner};
26
27const ZERO_VALUE: usize = 0;
28const TOP_MOST_BIT: usize = usize::MAX & !(isize::MAX as usize);
29const INLINED_STR_DATA_BUF_LEN: usize = std::mem::size_of::<usize>() * 3;
30const TOP_BYTE_INDEX: usize = if cfg!(target_endian = "little") {
33 std::mem::size_of::<usize>() - 1
34} else {
35 0
36};
37const INLINED_STR_TAG_INDEX: usize = std::mem::size_of::<usize>() * 2 + TOP_BYTE_INDEX;
38const INLINED_STR_MAX_LEN: usize = INLINED_STR_TAG_INDEX;
39const INLINED_STR_MAX_LEN_U8: u8 = INLINED_STR_MAX_LEN as u8;
40
41const UNION_TYPE_TAG_VALUE_STATIC: u8 = get_offset_tag_value(0);
42const UNION_TYPE_TAG_VALUE_INTERNED_FIXED_SIZE: u8 = get_offset_tag_value(1);
43const UNION_TYPE_TAG_VALUE_INTERNED_GENERIC_MAP: u8 = get_offset_tag_value(2);
44const UNION_TYPE_TAG_VALUE_SHARED: u8 = get_offset_tag_value(3);
45
46const fn get_offset_tag_value(tag: u8) -> u8 {
47 const UNION_TYPE_TAG_VALUE_BASE: u8 = INLINED_STR_MAX_LEN as u8 + 1;
48
49 if tag > (u8::MAX - INLINED_STR_MAX_LEN as u8) {
50 panic!("Union type tag value must fit in the discriminant byte.");
51 }
52
53 tag + UNION_TYPE_TAG_VALUE_BASE
54}
55
56const fn get_scaled_union_tag(tag: u8) -> usize {
57 const UNION_TYPE_TAG_VALUE_SHIFT: u32 = (std::mem::size_of::<usize>() as u32 - 1) * 8;
59
60 (tag as usize) << UNION_TYPE_TAG_VALUE_SHIFT
61}
62
63#[cfg(not(target_pointer_width = "64"))]
65const _INVARIANTS_CHECK: () = {
66 compile_error!("`stringtheory` is only supported on 64-bit platforms.");
67};
68
69const fn is_tagged(cap: u8) -> bool {
70 cap & 0b10000000 != 0
71}
72
73const fn tag_cap(cap: usize) -> usize {
74 cap | TOP_MOST_BIT
75}
76
77const fn untag_cap(cap: usize) -> usize {
78 cap & !(TOP_MOST_BIT)
79}
80
81#[derive(Clone, Copy, Eq, PartialEq)]
82#[repr(usize)]
83enum Zero {
84 Zero = ZERO_VALUE,
85}
86
87#[derive(Clone, Copy, Debug, Eq, PartialEq)]
88#[repr(usize)]
89enum Tag {
90 Static = get_scaled_union_tag(UNION_TYPE_TAG_VALUE_STATIC),
91 InternedFixedSize = get_scaled_union_tag(UNION_TYPE_TAG_VALUE_INTERNED_FIXED_SIZE),
92 InternedGenericMap = get_scaled_union_tag(UNION_TYPE_TAG_VALUE_INTERNED_GENERIC_MAP),
93 Shared = get_scaled_union_tag(UNION_TYPE_TAG_VALUE_SHARED),
94}
95
96#[repr(C)]
97#[derive(Clone, Copy)]
98struct EmptyUnion {
99 ptr: Zero, len: Zero, cap: Zero, }
103
104impl EmptyUnion {
105 const fn new() -> Self {
106 Self {
107 ptr: Zero::Zero,
108 len: Zero::Zero,
109 cap: Zero::Zero,
110 }
111 }
112}
113
114#[repr(C)]
115#[derive(Clone, Copy)]
116struct OwnedUnion {
117 ptr: *mut u8, len: usize, cap: usize, }
121
122impl OwnedUnion {
123 #[inline]
124 fn as_str(&self) -> &str {
125 unsafe { from_utf8_unchecked(from_raw_parts(self.ptr, self.len)) }
128 }
129
130 fn into_owned(self) -> String {
131 unsafe { String::from_raw_parts(self.ptr, self.len, untag_cap(self.cap)) }
134 }
135}
136
137#[repr(C)]
138#[derive(Clone, Copy)]
139struct StaticUnion {
140 value: &'static str, _cap: Tag, }
143
144impl StaticUnion {
145 #[inline]
146 const fn as_str(&self) -> &str {
147 self.value
148 }
149}
150
151#[repr(C)]
152struct InternedUnion {
153 state: InternedStateUnion, _cap: Tag, }
156
157impl Drop for InternedUnion {
158 fn drop(&mut self) {
159 match self._cap {
160 Tag::InternedFixedSize => {
161 let state = unsafe { ManuallyDrop::take(&mut self.state.fixed_size) };
162 drop(state);
163 }
164 Tag::InternedGenericMap => {
165 let state = unsafe { ManuallyDrop::take(&mut self.state.generic_map) };
166 drop(state);
167 }
168 _ => unreachable!(),
169 }
170 }
171}
172
173impl Clone for InternedUnion {
174 fn clone(&self) -> Self {
175 let state = unsafe {
178 match self._cap {
179 Tag::InternedFixedSize => InternedStateUnion {
180 fixed_size: self.state.fixed_size.clone(),
181 },
182 Tag::InternedGenericMap => InternedStateUnion {
183 generic_map: self.state.generic_map.clone(),
184 },
185 _ => unreachable!(),
186 }
187 };
188
189 Self { state, _cap: self._cap }
190 }
191}
192
193#[repr(C)]
194union InternedStateUnion {
195 fixed_size: ManuallyDrop<self::interning::fixed_size::StringState>,
196 generic_map: ManuallyDrop<self::interning::map::StringState>,
197}
198
199#[repr(C)]
200struct SharedUnion {
201 ptr: NonNull<str>, _cap: Tag, }
204
205impl SharedUnion {
206 #[inline]
207 const fn as_str(&self) -> &str {
208 unsafe { self.ptr.as_ref() }
210 }
211}
212
213#[repr(C)]
214#[derive(Clone, Copy)]
215struct InlinedUnion {
216 data: [u8; INLINED_STR_DATA_BUF_LEN], }
219
220impl InlinedUnion {
221 #[inline]
222 fn as_str(&self) -> &str {
223 let len = self.data[INLINED_STR_MAX_LEN] as usize;
224
225 unsafe { from_utf8_unchecked(&self.data[0..len]) }
228 }
229}
230
231#[repr(C)]
232#[derive(Clone, Copy)]
233struct DiscriminantUnion {
234 data: [u8; INLINED_STR_DATA_BUF_LEN],
236}
237
238#[derive(Debug, Eq, PartialEq)]
239enum UnionType {
240 Empty,
241 Owned,
242 Static,
243 InternedFixedSize,
244 InternedGenericMap,
245 Inlined,
246 Shared,
247}
248
249impl UnionType {
250 #[inline]
251 const fn is_owned(&self) -> bool {
252 matches!(self, UnionType::Owned)
253 }
254}
255
256impl DiscriminantUnion {
257 #[inline]
258 const fn get_union_type(&self) -> UnionType {
259 let tag_byte = self.data[INLINED_STR_TAG_INDEX];
324
325 if is_tagged(tag_byte) {
326 return UnionType::Owned;
327 }
328
329 match tag_byte {
332 0 => UnionType::Empty,
334
335 1..=INLINED_STR_MAX_LEN_U8 => UnionType::Inlined,
337
338 UNION_TYPE_TAG_VALUE_STATIC => UnionType::Static,
341 UNION_TYPE_TAG_VALUE_INTERNED_FIXED_SIZE => UnionType::InternedFixedSize,
342 UNION_TYPE_TAG_VALUE_INTERNED_GENERIC_MAP => UnionType::InternedGenericMap,
343 UNION_TYPE_TAG_VALUE_SHARED => UnionType::Shared,
344
345 _ => UnionType::Empty,
348 }
349 }
350}
351
352union Inner {
374 empty: EmptyUnion,
375 owned: OwnedUnion,
376 static_: StaticUnion,
377 interned: ManuallyDrop<InternedUnion>,
378 shared: ManuallyDrop<SharedUnion>,
379 inlined: InlinedUnion,
380 discriminant: DiscriminantUnion,
381}
382
383impl Inner {
384 const fn empty() -> Self {
385 Self {
386 empty: EmptyUnion::new(),
387 }
388 }
389
390 fn owned(value: String) -> Self {
391 match value.capacity() {
392 0 => Self::empty(),
394 cap => {
395 let mut value = value.into_bytes();
396
397 let ptr = value.as_mut_ptr();
398 let len = value.len();
399
400 std::mem::forget(value);
402
403 Self {
404 owned: OwnedUnion {
405 ptr,
406 len,
407 cap: tag_cap(cap),
408 },
409 }
410 }
411 }
412 }
413
414 const fn static_str(value: &'static str) -> Self {
415 match value.len() {
416 0 => Self::empty(),
417 _ => Self {
418 static_: StaticUnion {
419 value,
420 _cap: Tag::Static,
421 },
422 },
423 }
424 }
425
426 fn interned(value: InternedString) -> Self {
427 match value.len() {
428 0 => Self::empty(),
429 _len => {
430 let (state, tag) = match value.into_state() {
431 InternedStringState::FixedSize(fixed_size) => (
432 InternedStateUnion {
433 fixed_size: ManuallyDrop::new(fixed_size),
434 },
435 Tag::InternedFixedSize,
436 ),
437 InternedStringState::GenericMap(generic_map) => (
438 InternedStateUnion {
439 generic_map: ManuallyDrop::new(generic_map),
440 },
441 Tag::InternedGenericMap,
442 ),
443 };
444
445 Self {
446 interned: ManuallyDrop::new(InternedUnion { state, _cap: tag }),
447 }
448 }
449 }
450 }
451
452 fn shared(value: Arc<str>) -> Self {
453 match value.len() {
454 0 => Self::empty(),
455 _len => Self {
456 shared: ManuallyDrop::new(SharedUnion {
457 ptr: unsafe { NonNull::new_unchecked(Arc::into_raw(value).cast_mut()) },
459 _cap: Tag::Shared,
460 }),
461 },
462 }
463 }
464
465 fn try_inlined(value: &str) -> Option<Self> {
466 match value.len() {
467 0 => Some(Self::empty()),
468 len => {
469 if len > INLINED_STR_MAX_LEN {
470 return None;
471 }
472
473 let mut data = [0; INLINED_STR_DATA_BUF_LEN];
474
475 data[INLINED_STR_MAX_LEN] = len as u8;
477
478 let buf = value.as_bytes();
479 data[0..len].copy_from_slice(buf);
480
481 Some(Self {
482 inlined: InlinedUnion { data },
483 })
484 }
485 }
486 }
487
488 #[inline]
489 fn as_str(&self) -> &str {
490 let union_type = unsafe { self.discriminant.get_union_type() };
491 match union_type {
492 UnionType::Empty => "",
493 UnionType::Owned => {
494 let owned = unsafe { &self.owned };
495 owned.as_str()
496 }
497 UnionType::Static => {
498 let static_ = unsafe { &self.static_ };
499 static_.as_str()
500 }
501 UnionType::InternedFixedSize => {
502 let interned = unsafe { &self.interned.state.fixed_size };
503 interned.as_str()
504 }
505 UnionType::InternedGenericMap => {
506 let interned = unsafe { &self.interned.state.generic_map };
507 interned.as_str()
508 }
509 UnionType::Shared => {
510 let shared = unsafe { &self.shared };
511 shared.as_str()
512 }
513 UnionType::Inlined => {
514 let inlined = unsafe { &self.inlined };
515 inlined.as_str()
516 }
517 }
518 }
519
520 #[inline]
521 const fn get_union_type(&self) -> UnionType {
522 unsafe { self.discriminant.get_union_type() }
523 }
524
525 fn into_owned(mut self) -> String {
526 let union_type = unsafe { self.discriminant.get_union_type() };
527 match union_type {
528 UnionType::Empty => String::new(),
529 UnionType::Owned => {
530 let owned = unsafe { self.owned };
533 self.empty = EmptyUnion::new();
534
535 owned.into_owned()
536 }
537 UnionType::Static => {
538 let static_ = unsafe { self.static_ };
539 static_.as_str().to_owned()
540 }
541 UnionType::InternedFixedSize => {
542 let interned = unsafe { &self.interned.state.fixed_size };
543 interned.as_str().to_owned()
544 }
545 UnionType::InternedGenericMap => {
546 let interned = unsafe { &self.interned.state.generic_map };
547 interned.as_str().to_owned()
548 }
549 UnionType::Shared => {
550 let shared = unsafe { &self.shared };
551 shared.as_str().to_owned()
552 }
553 UnionType::Inlined => {
554 let inlined = unsafe { self.inlined };
555 inlined.as_str().to_owned()
556 }
557 }
558 }
559}
560
561impl Drop for Inner {
562 fn drop(&mut self) {
563 let union_type = unsafe { self.discriminant.get_union_type() };
564 match union_type {
565 UnionType::Owned => {
566 let owned = unsafe { &mut self.owned };
567
568 let ptr = owned.ptr;
569 let len = owned.len;
570 let cap = untag_cap(owned.cap);
571
572 let data = unsafe { Vec::<u8>::from_raw_parts(ptr, len, cap) };
576 drop(data);
577 }
578 UnionType::InternedFixedSize | UnionType::InternedGenericMap => {
579 let interned = unsafe { &mut self.interned };
580
581 let data = unsafe { ManuallyDrop::take(interned) };
584 drop(data);
585 }
586 UnionType::Shared => {
587 let shared = unsafe { &mut self.shared };
588
589 unsafe {
595 Arc::decrement_strong_count(shared.ptr.as_ptr().cast_const());
596 }
597 }
598 _ => {}
599 }
600 }
601}
602
603impl Clone for Inner {
604 fn clone(&self) -> Self {
605 let union_type = unsafe { self.discriminant.get_union_type() };
606 match union_type {
607 UnionType::Empty => Self::empty(),
608 UnionType::Owned => {
609 let owned = unsafe { self.owned };
610 let s = owned.as_str();
611
612 Self::try_inlined(s).unwrap_or_else(|| Self::owned(s.to_owned()))
618 }
619 UnionType::Static => Self {
620 static_: unsafe { self.static_ },
621 },
622 UnionType::InternedFixedSize | UnionType::InternedGenericMap => Self {
623 interned: unsafe { self.interned.clone() },
624 },
625 UnionType::Shared => {
626 let shared = unsafe { &self.shared };
627
628 unsafe {
633 Arc::increment_strong_count(shared.ptr.as_ptr().cast_const());
634 }
635
636 Self {
637 shared: ManuallyDrop::new(SharedUnion {
638 ptr: shared.ptr,
639 _cap: Tag::Shared,
640 }),
641 }
642 }
643 UnionType::Inlined => Self {
644 inlined: unsafe { self.inlined },
645 },
646 }
647 }
648}
649
650unsafe impl Send for Inner {}
652
653unsafe impl Sync for Inner {}
656
657#[derive(Clone)]
721pub struct MetaString {
722 inner: Inner,
723}
724
725impl MetaString {
726 pub const fn empty() -> Self {
730 Self { inner: Inner::empty() }
731 }
732
733 pub const fn from_static(s: &'static str) -> Self {
737 Self {
738 inner: Inner::static_str(s),
739 }
740 }
741
742 pub fn try_inline(s: &str) -> Option<Self> {
744 Inner::try_inlined(s).map(|inner| Self { inner })
745 }
746
747 pub fn from_interner<I>(s: &str, interner: &I) -> Self
752 where
753 I: Interner,
754 {
755 if let Some(inlined) = Self::try_inline(s) {
756 return inlined;
757 }
758 if let Some(interned) = interner.try_intern(s) {
759 return Self::from(interned);
760 }
761 Self::from(s.to_owned())
762 }
763
764 pub fn is_empty(&self) -> bool {
766 self.deref().is_empty()
767 }
768
769 pub const fn is_cheaply_cloneable(&self) -> bool {
771 !self.inner.get_union_type().is_owned()
773 }
774
775 pub fn into_owned(self) -> String {
780 self.inner.into_owned()
781 }
782}
783
784impl Default for MetaString {
785 fn default() -> Self {
786 Self::empty()
787 }
788}
789
790impl hash::Hash for MetaString {
791 fn hash<H: hash::Hasher>(&self, state: &mut H) {
792 self.deref().hash(state)
793 }
794}
795
796impl PartialEq<str> for MetaString {
797 fn eq(&self, other: &str) -> bool {
798 self.deref() == other
799 }
800}
801
802impl PartialEq<&str> for MetaString {
803 fn eq(&self, other: &&str) -> bool {
804 self.deref() == *other
805 }
806}
807
808impl PartialEq<MetaString> for MetaString {
809 fn eq(&self, other: &MetaString) -> bool {
810 self.deref() == other.deref()
811 }
812}
813
814impl Eq for MetaString {}
815
816impl PartialOrd for MetaString {
817 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
818 Some(self.cmp(other))
819 }
820}
821
822impl Ord for MetaString {
823 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
824 self.deref().cmp(other.deref())
825 }
826}
827
828impl Borrow<str> for MetaString {
829 fn borrow(&self) -> &str {
830 self.deref()
831 }
832}
833
834impl Serialize for MetaString {
835 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
836 where
837 S: serde::Serializer,
838 {
839 serializer.serialize_str(self.deref())
840 }
841}
842
843impl<'de> Deserialize<'de> for MetaString {
844 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
845 where
846 D: serde::Deserializer<'de>,
847 {
848 struct MetaStringVisitor;
849
850 impl<'de> serde::de::Visitor<'de> for MetaStringVisitor {
851 type Value = MetaString;
852
853 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
854 formatter.write_str("a string")
855 }
856
857 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
858 where
859 E: serde::de::Error,
860 {
861 Ok(MetaString::from(v))
862 }
863 }
864
865 deserializer.deserialize_str(MetaStringVisitor)
866 }
867}
868
869impl From<String> for MetaString {
870 fn from(s: String) -> Self {
871 Self { inner: Inner::owned(s) }
872 }
873}
874
875impl From<&str> for MetaString {
876 fn from(s: &str) -> Self {
877 Self::try_inline(s).unwrap_or_else(|| Self::from(s.to_owned()))
878 }
879}
880
881impl From<InternedString> for MetaString {
882 fn from(s: InternedString) -> Self {
883 Self {
884 inner: Inner::interned(s),
885 }
886 }
887}
888
889impl From<Arc<str>> for MetaString {
890 fn from(s: Arc<str>) -> Self {
891 Self {
892 inner: Inner::shared(s),
893 }
894 }
895}
896
897impl From<MetaString> for protobuf::Chars {
898 fn from(value: MetaString) -> Self {
899 value.into_owned().into()
900 }
901}
902
903impl<'a> From<&'a MetaString> for protobuf::Chars {
904 fn from(value: &'a MetaString) -> Self {
905 value.deref().into()
911 }
912}
913
914impl Deref for MetaString {
915 type Target = str;
916
917 fn deref(&self) -> &str {
918 self.inner.as_str()
919 }
920}
921
922impl AsRef<str> for MetaString {
923 fn as_ref(&self) -> &str {
924 self.deref()
925 }
926}
927
928impl fmt::Debug for MetaString {
929 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
930 self.deref().fmt(f)
931 }
932}
933
934impl fmt::Display for MetaString {
935 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
936 self.deref().fmt(f)
937 }
938}
939
940#[cfg(test)]
941mod tests {
942 use std::{num::NonZeroUsize, sync::Arc};
943
944 use proptest::{prelude::*, proptest};
945
946 use super::{
947 interning::GenericMapInterner, CheapMetaString, InlinedUnion, Inner, MetaString, UnionType,
948 INLINED_STR_MAX_LEN, INLINED_STR_TAG_INDEX, UNION_TYPE_TAG_VALUE_INTERNED_FIXED_SIZE,
949 UNION_TYPE_TAG_VALUE_INTERNED_GENERIC_MAP, UNION_TYPE_TAG_VALUE_SHARED, UNION_TYPE_TAG_VALUE_STATIC,
950 };
951 use crate::interning::{FixedSizeInterner, Interner as _};
952
953 fn discriminant_byte(inner: &Inner) -> u8 {
954 unsafe { inner.discriminant.data[INLINED_STR_TAG_INDEX] }
955 }
956
957 #[test]
958 fn struct_sizes() {
959 assert_eq!(std::mem::size_of::<MetaString>(), std::mem::size_of::<usize>() * 3);
961 assert_eq!(std::mem::size_of::<Inner>(), std::mem::size_of::<usize>() * 3);
962
963 assert_eq!(std::mem::size_of::<InlinedUnion>(), std::mem::size_of::<Inner>());
966 }
967
968 #[test]
969 fn inline_capacity_matches_target_endianness() {
970 let expected_inline_capacity = if cfg!(target_endian = "little") { 23 } else { 16 };
971
972 assert_eq!(INLINED_STR_MAX_LEN, expected_inline_capacity);
973 }
974
975 #[test]
976 fn discriminant_byte_tracks_endian_specific_variant_tags() {
977 let owned = MetaString::from(String::from("owned-value"));
978 assert_eq!(owned.inner.get_union_type(), UnionType::Owned);
979 assert!(discriminant_byte(&owned.inner) & 0b1000_0000 != 0);
980
981 let static_value = MetaString::from_static("static-value");
982 assert_eq!(static_value.inner.get_union_type(), UnionType::Static);
983 assert_eq!(discriminant_byte(&static_value.inner), UNION_TYPE_TAG_VALUE_STATIC);
984
985 let shared = MetaString::from(Arc::<str>::from("shared-value"));
986 assert_eq!(shared.inner.get_union_type(), UnionType::Shared);
987 assert_eq!(discriminant_byte(&shared.inner), UNION_TYPE_TAG_VALUE_SHARED);
988
989 let fixed_size_interner = FixedSizeInterner::<1>::new(NonZeroUsize::new(1024).unwrap());
990 let fixed_size = MetaString::from(fixed_size_interner.try_intern("fixed-size-value").unwrap());
991 assert_eq!(fixed_size.inner.get_union_type(), UnionType::InternedFixedSize);
992 assert_eq!(
993 discriminant_byte(&fixed_size.inner),
994 UNION_TYPE_TAG_VALUE_INTERNED_FIXED_SIZE
995 );
996
997 let generic_map_interner = GenericMapInterner::new(NonZeroUsize::new(1024).unwrap());
998 let generic_map = MetaString::from(generic_map_interner.try_intern("generic-map-value").unwrap());
999 assert_eq!(generic_map.inner.get_union_type(), UnionType::InternedGenericMap);
1000 assert_eq!(
1001 discriminant_byte(&generic_map.inner),
1002 UNION_TYPE_TAG_VALUE_INTERNED_GENERIC_MAP
1003 );
1004 }
1005
1006 #[test]
1007 fn inlined_string_uses_every_byte_before_tag_byte() {
1008 let input = "a".repeat(INLINED_STR_MAX_LEN);
1009 let meta = MetaString::try_inline(&input).expect("input should fit exactly in inline storage");
1010
1011 assert_eq!(meta.inner.get_union_type(), UnionType::Inlined);
1012 assert_eq!(discriminant_byte(&meta.inner), INLINED_STR_MAX_LEN as u8);
1013 let inlined = unsafe { meta.inner.inlined };
1014 assert_eq!(&inlined.data[..INLINED_STR_MAX_LEN], input.as_bytes());
1015 }
1016
1017 #[test]
1018 fn inlined_string_rejects_one_byte_past_tag_byte() {
1019 let input = "a".repeat(INLINED_STR_MAX_LEN + 1);
1020
1021 assert!(MetaString::try_inline(&input).is_none());
1022 }
1023
1024 #[test]
1025 fn static_str_inlineable() {
1026 let s = "hello";
1029 let meta = MetaString::from_static(s);
1030
1031 assert_eq!(meta.inner.get_union_type(), UnionType::Static);
1032 assert_eq!(s, &*meta);
1033 assert_eq!(s, meta.into_owned());
1034 }
1035
1036 #[test]
1037 fn static_str_not_inlineable() {
1038 let s = "hello there, world! it's me, margaret!";
1039 let meta = MetaString::from_static(s);
1040
1041 assert_eq!(s, &*meta);
1042 assert_eq!(meta.inner.get_union_type(), UnionType::Static);
1043 assert_eq!(s, meta.into_owned());
1044 }
1045
1046 #[test]
1047 fn owned_string() {
1048 let s_orig = "hello";
1049 let s = String::from(s_orig);
1050 let meta = MetaString::from(s);
1051
1052 assert_eq!(s_orig, &*meta);
1053 assert_eq!(meta.inner.get_union_type(), UnionType::Owned);
1054 assert_eq!(s_orig, meta.into_owned());
1055 }
1056
1057 #[test]
1058 fn inlined_string() {
1059 let s = "hello";
1060 let meta = MetaString::from(s);
1061
1062 assert_eq!(s, &*meta);
1063 assert_eq!(meta.inner.get_union_type(), UnionType::Inlined);
1064 assert_eq!(s, meta.into_owned());
1065 }
1066
1067 #[test]
1068 fn interned_string() {
1069 let intern_str = "hello interned str!";
1070
1071 let fs_interner = FixedSizeInterner::<1>::new(NonZeroUsize::new(1024).unwrap());
1072 let s = fs_interner.try_intern(intern_str).unwrap();
1073 assert_eq!(intern_str, &*s);
1074
1075 let meta = MetaString::from(s);
1076 assert_eq!(intern_str, &*meta);
1077 assert_eq!(meta.inner.get_union_type(), UnionType::InternedFixedSize);
1078 assert_eq!(intern_str, meta.into_owned());
1079
1080 let gm_interner = GenericMapInterner::new(NonZeroUsize::new(1024).unwrap());
1081 let s = gm_interner.try_intern(intern_str).unwrap();
1082 assert_eq!(intern_str, &*s);
1083
1084 let meta = MetaString::from(s);
1085 assert_eq!(intern_str, &*meta);
1086 assert_eq!(meta.inner.get_union_type(), UnionType::InternedGenericMap);
1087 assert_eq!(intern_str, meta.into_owned());
1088 }
1089
1090 #[test]
1091 fn shared_string() {
1092 let shared_str = "hello shared str!";
1093
1094 let s = Arc::<str>::from(shared_str);
1095 assert_eq!(shared_str, &*s);
1096
1097 let meta = MetaString::from(s);
1098 assert_eq!(shared_str, &*meta);
1099 assert_eq!(meta.inner.get_union_type(), UnionType::Shared);
1100 assert_eq!(shared_str, meta.into_owned());
1101 }
1102
1103 #[test]
1104 fn meta_string_sorts_by_string_value() {
1105 let mut values = [
1106 MetaString::from("gamma"),
1107 MetaString::from("alpha"),
1108 MetaString::from("beta"),
1109 ];
1110 values.sort();
1111
1112 assert_eq!(
1113 values.iter().map(|value| &**value).collect::<Vec<_>>(),
1114 ["alpha", "beta", "gamma"]
1115 );
1116 }
1117
1118 #[test]
1119 fn protobuf_chars_conversions_preserve_value() {
1120 let chars: protobuf::Chars = MetaString::from("protobuf-value").into();
1121 let chars_ref: &str = chars.as_ref();
1122 assert_eq!(chars_ref, "protobuf-value");
1123
1124 let meta = MetaString::from("protobuf-ref-value");
1125 let chars: protobuf::Chars = (&meta).into();
1126 let chars_ref: &str = chars.as_ref();
1127 assert_eq!(chars_ref, "protobuf-ref-value");
1128 }
1129
1130 #[test]
1131 fn cheap_meta_string_trait_reports_cheap_clone_availability() {
1132 let shared = MetaString::from(Arc::<str>::from("shared-value"));
1133 assert_eq!(shared.try_cheap_clone().as_deref(), Some("shared-value"));
1134
1135 let owned = MetaString::from(String::from("owned-value"));
1136 assert!(owned.try_cheap_clone().is_none());
1137 }
1138
1139 #[test]
1140 fn shared_string_clone() {
1141 let shared_str = "hello shared str!";
1142 let s = Arc::<str>::from(shared_str);
1143 let meta = MetaString::from(s);
1144
1145 let meta2 = meta.clone();
1147 assert_eq!(shared_str, &*meta2);
1148
1149 drop(meta);
1152 assert_eq!(shared_str, &*meta2);
1153 }
1154
1155 #[test]
1156 fn empty_string_shared() {
1157 let shared_str = "";
1158
1159 let s = Arc::<str>::from(shared_str);
1160 assert_eq!(shared_str, &*s);
1161
1162 let meta = MetaString::from(s);
1163 assert_eq!(shared_str, &*meta);
1164 assert_eq!(meta.inner.get_union_type(), UnionType::Empty);
1165 assert_eq!(shared_str, meta.into_owned());
1166 }
1167
1168 #[test]
1169 fn empty_string_interned() {
1170 let intern_str = "";
1171
1172 let interner = GenericMapInterner::new(NonZeroUsize::new(1024).unwrap());
1173 let s = interner.try_intern(intern_str).unwrap();
1174 assert_eq!(intern_str, &*s);
1175
1176 let meta = MetaString::from(s);
1177 assert_eq!(intern_str, &*meta);
1178 assert_eq!(meta.inner.get_union_type(), UnionType::Empty);
1179 assert_eq!(intern_str, meta.into_owned());
1180 }
1181
1182 #[test]
1183 fn empty_string_static() {
1184 let s = "";
1185
1186 let meta = MetaString::from_static(s);
1187 assert_eq!(s, &*meta);
1188 assert_eq!(meta.inner.get_union_type(), UnionType::Empty);
1189 assert_eq!(s, meta.into_owned());
1190 }
1191
1192 #[test]
1193 fn empty_string_inlined() {
1194 let s = "";
1195
1196 let meta = MetaString::try_inline(s).expect("empty string definitely 'fits'");
1197 assert_eq!(s, &*meta);
1198 assert_eq!(meta.inner.get_union_type(), UnionType::Empty);
1199 assert_eq!(s, meta.into_owned());
1200 }
1201
1202 #[test]
1203 fn empty_string_owned() {
1204 let s = String::with_capacity(32);
1207 let actual_cap = s.capacity();
1208
1209 let meta = MetaString::from(s);
1210 assert_eq!("", &*meta);
1211 assert_eq!(meta.inner.get_union_type(), UnionType::Owned);
1212
1213 let owned = meta.into_owned();
1214 assert_eq!(owned.capacity(), actual_cap);
1215 assert_eq!("", owned);
1216 }
1217
1218 #[test]
1219 fn empty_string_owned_zero_capacity() {
1220 let s = String::new();
1222
1223 let meta = MetaString::from(s);
1224 assert_eq!("", &*meta);
1225 assert_eq!(meta.inner.get_union_type(), UnionType::Empty);
1226 assert_eq!("", meta.into_owned());
1227 }
1228
1229 #[test]
1230 fn is_cheaply_cloneable() {
1231 let s =
1233 String::from("big ol' stringy string that can't be inlined and lives out its bleak existence in the heap");
1234 let ms = MetaString::from(s);
1235 assert!(!ms.is_cheaply_cloneable());
1236
1237 let ms = MetaString::empty();
1239 assert!(ms.is_cheaply_cloneable());
1240
1241 let s = "hello";
1243 let ms = MetaString::try_inline(s).expect("inlined string should fit");
1244 assert!(ms.is_cheaply_cloneable());
1245
1246 let s = "hello there, world! it's me, margaret!";
1248 let ms = MetaString::from_static(s);
1249 assert!(ms.is_cheaply_cloneable());
1250
1251 let interner = GenericMapInterner::new(NonZeroUsize::new(1024).unwrap());
1253 let s = interner.try_intern("hello interned str!").unwrap();
1254 let ms = MetaString::from(s);
1255 assert!(ms.is_cheaply_cloneable());
1256
1257 let s = Arc::from("hello shared str!");
1259 let ms = MetaString::from(s);
1260 assert!(ms.is_cheaply_cloneable());
1261 }
1262
1263 const LONG: &str = "this string is definitely longer than twenty-three bytes";
1266
1267 fn hash_of(ms: &MetaString) -> u64 {
1268 use std::hash::{Hash as _, Hasher as _};
1269
1270 let mut hasher = std::collections::hash_map::DefaultHasher::new();
1271 ms.hash(&mut hasher);
1272 hasher.finish()
1273 }
1274
1275 #[test]
1276 fn serde_round_trips_every_variant() {
1277 let interner = GenericMapInterner::new(NonZeroUsize::new(1024).unwrap());
1281 let cases: Vec<(MetaString, &str)> = vec![
1282 (MetaString::empty(), ""),
1283 (MetaString::try_inline("hi").expect("fits inline"), "hi"),
1284 (MetaString::from_static(LONG), LONG),
1285 (MetaString::from(String::from(LONG)), LONG),
1286 (MetaString::from(interner.try_intern(LONG).expect("interns")), LONG),
1287 (MetaString::from(Arc::<str>::from(LONG)), LONG),
1288 ];
1289
1290 for (ms, expected) in cases {
1291 let json = serde_json::to_string(&ms).expect("serialization should succeed");
1292
1293 let as_plain: String = serde_json::from_str(&json).expect("serialized form should be a JSON string");
1295 assert_eq!(as_plain, expected, "serialized form should carry the string content");
1296
1297 let back: MetaString = serde_json::from_str(&json).expect("deserialization should succeed");
1298 assert_eq!(back, expected, "deserialized value should match the original content");
1299 assert_eq!(ms, back, "round-trip should preserve equality");
1300 }
1301 }
1302
1303 #[test]
1304 fn equal_content_hashes_equally_across_variants() {
1305 let interner = GenericMapInterner::new(NonZeroUsize::new(1024).unwrap());
1308 let variants = [
1309 MetaString::from_static(LONG),
1310 MetaString::from(String::from(LONG)),
1311 MetaString::from(interner.try_intern(LONG).expect("interns")),
1312 MetaString::from(Arc::<str>::from(LONG)),
1313 ];
1314
1315 assert_eq!(variants[0].inner.get_union_type(), UnionType::Static);
1317 assert_eq!(variants[1].inner.get_union_type(), UnionType::Owned);
1318 assert_eq!(variants[2].inner.get_union_type(), UnionType::InternedGenericMap);
1319 assert_eq!(variants[3].inner.get_union_type(), UnionType::Shared);
1320
1321 let expected_hash = hash_of(&variants[0]);
1322 for variant in &variants {
1323 assert_eq!(
1324 hash_of(variant),
1325 expected_hash,
1326 "hash must depend only on content, not the backing variant"
1327 );
1328 assert_eq!(
1329 *variant, variants[0],
1330 "equal content must compare equal across variants"
1331 );
1332 }
1333
1334 let inlined = MetaString::from("short");
1336 let static_ = MetaString::from_static("short");
1337 assert_eq!(inlined.inner.get_union_type(), UnionType::Inlined);
1338 assert_eq!(static_.inner.get_union_type(), UnionType::Static);
1339 assert_eq!(hash_of(&inlined), hash_of(&static_));
1340 }
1341
1342 #[test]
1343 fn from_interner_inlines_short_strings() {
1344 let interner = GenericMapInterner::new(NonZeroUsize::new(1024).unwrap());
1346 let ms = MetaString::from_interner("short", &interner);
1347
1348 assert_eq!(ms.inner.get_union_type(), UnionType::Inlined);
1349 assert_eq!(ms, "short");
1350 assert_eq!(interner.len(), 0, "inlining must not add an interner entry");
1351 }
1352
1353 #[test]
1354 fn from_interner_interns_non_inlineable_strings() {
1355 let interner = GenericMapInterner::new(NonZeroUsize::new(1024).unwrap());
1357 assert!(LONG.len() > INLINED_STR_MAX_LEN);
1358
1359 let ms = MetaString::from_interner(LONG, &interner);
1360
1361 assert_eq!(ms.inner.get_union_type(), UnionType::InternedGenericMap);
1362 assert_eq!(ms, LONG);
1363 assert_eq!(interner.len(), 1, "the string should have been interned");
1364 }
1365
1366 #[test]
1367 fn from_interner_falls_back_to_owned_when_interner_is_full() {
1368 let interner = GenericMapInterner::new(NonZeroUsize::new(16).unwrap());
1371 assert!(LONG.len() > INLINED_STR_MAX_LEN);
1372
1373 let ms = MetaString::from_interner(LONG, &interner);
1374
1375 assert_eq!(ms.inner.get_union_type(), UnionType::Owned);
1376 assert_eq!(ms, LONG);
1377 assert_eq!(
1378 interner.len(),
1379 0,
1380 "the interner had no room, so nothing should be interned"
1381 );
1382 }
1383
1384 fn arb_unicode_str_max_len(max_len: usize) -> impl Strategy<Value = String> {
1385 ".{0,23}".prop_filter("resulting string is too long", move |s| s.len() <= max_len)
1386 }
1387
1388 proptest! {
1389 #![proptest_config(ProptestConfig::with_cases(10000))]
1390
1391 #[test]
1392 #[cfg_attr(miri, ignore)]
1393 fn property_test_inlined_string(
1394 input in arb_unicode_str_max_len(INLINED_STR_MAX_LEN),
1395 ) {
1396 assert!(input.len() <= INLINED_STR_MAX_LEN, "input should fit in the inline buffer");
1397 let meta = MetaString::try_inline(&input).expect("input should fit");
1398
1399 if input.is_empty() {
1400 assert_eq!(meta.inner.get_union_type(), UnionType::Empty);
1401 } else {
1402 assert_eq!(input, &*meta);
1403 assert_eq!(meta.inner.get_union_type(), UnionType::Inlined);
1404 }
1405 }
1406
1407 #[test]
1408 #[cfg_attr(miri, ignore)]
1409 fn property_test_owned_string(
1410 input in ".*",
1411 ) {
1412 let is_empty = input.is_empty();
1413 let meta = MetaString::from(input);
1414
1415 if is_empty {
1416 assert_eq!(meta.inner.get_union_type(), UnionType::Empty);
1417 } else {
1418 assert_eq!(meta.inner.get_union_type(), UnionType::Owned);
1419 }
1420 }
1421 }
1422}