1use std::borrow::Borrow;
37use std::cmp::Ordering;
38use std::fmt::{Debug, Display};
39use std::hash::{Hash, Hasher};
40use std::mem::ManuallyDrop;
41use std::ops::Deref;
42
43use revision::{DeserializeRevisioned, Error, Revisioned, SerializeRevisioned};
44use serde::de::{self, Visitor};
45use serde::{Deserialize, Deserializer, Serialize, Serializer};
46
47pub const INLINE_CAP: usize = 23;
49
50const TAG_STATIC: u8 = 254;
52const TAG_BOXED: u8 = 255;
54
55const HEAP_PAD_LEN: usize = 23 - 2 * std::mem::size_of::<usize>();
57
58#[derive(Clone, Copy)]
60#[repr(C)]
61struct HeapData {
62 ptr: *const u8,
63 len: usize,
64 _pad: [u8; HEAP_PAD_LEN],
65 tag: u8,
66}
67
68#[repr(C)]
70union StrandData {
71 inline: [u8; 24],
72 heap: ManuallyDrop<HeapData>,
73}
74
75#[repr(transparent)]
79pub struct Strand {
80 data: StrandData,
81}
82
83unsafe impl Send for Strand {}
84unsafe impl Sync for Strand {}
85
86impl Strand {
87 #[inline]
89 pub fn new(s: impl AsRef<str>) -> Self {
90 let s = s.as_ref();
91 if s.len() <= INLINE_CAP {
92 Self::new_inline(s)
93 } else {
94 Self::from(Box::from(s))
95 }
96 }
97
98 #[inline]
99 fn new_inline(s: &str) -> Self {
100 debug_assert!(s.len() <= INLINE_CAP);
101 let mut inline = [0u8; 24];
102 unsafe {
104 std::ptr::copy_nonoverlapping(s.as_ptr(), inline.as_mut_ptr(), s.len());
105 }
106 inline[23] = s.len() as u8;
107 Self {
108 data: StrandData {
109 inline,
110 },
111 }
112 }
113
114 #[inline(always)]
126 pub const fn new_static(text: &'static str) -> Self {
127 Self {
128 data: StrandData {
129 heap: ManuallyDrop::new(HeapData {
130 ptr: text.as_ptr(),
131 len: text.len(),
132 _pad: [0; HEAP_PAD_LEN],
133 tag: TAG_STATIC,
134 }),
135 },
136 }
137 }
138
139 pub fn from_display(d: impl Display) -> Self {
145 use std::fmt::Write;
146 struct StrandWriter {
148 inline: [u8; 24],
149 len: usize,
150 overflow: Option<String>,
151 }
152 impl Write for StrandWriter {
154 fn write_str(&mut self, s: &str) -> std::fmt::Result {
155 if let Some(overflow) = &mut self.overflow {
157 overflow.push_str(s);
158 return Ok(());
159 }
160 let bytes = s.as_bytes();
162 let end = self.len + bytes.len();
164 if end <= INLINE_CAP {
166 unsafe {
168 std::ptr::copy_nonoverlapping(
169 bytes.as_ptr(),
170 self.inline.as_mut_ptr().add(self.len),
171 bytes.len(),
172 );
173 }
174 self.len = end;
175 } else {
176 let valid_utf8 =
179 unsafe { std::str::from_utf8_unchecked(&self.inline[..self.len]) };
180 let mut overflow = String::with_capacity(end);
181 overflow.push_str(valid_utf8);
182 overflow.push_str(s);
183 self.overflow = Some(overflow);
184 }
185 Ok(())
186 }
187 }
188 let mut writer = StrandWriter {
190 inline: [0u8; 24],
191 len: 0,
192 overflow: None,
193 };
194 write!(&mut writer, "{}", d).expect("writing to StrandWriter should never fail");
196 if let Some(overflow) = writer.overflow {
198 Self::from(overflow)
200 } else {
201 writer.inline[23] = writer.len as u8;
203 Self {
204 data: StrandData {
205 inline: writer.inline,
206 },
207 }
208 }
209 }
210
211 #[inline]
213 pub fn is_inline(&self) -> bool {
214 unsafe { self.data.inline[23] <= INLINE_CAP as u8 }
215 }
216
217 #[inline]
219 pub fn is_static(&self) -> bool {
220 unsafe { self.data.inline[23] == TAG_STATIC }
221 }
222
223 #[inline]
225 pub fn is_boxed(&self) -> bool {
226 unsafe { self.data.inline[23] == TAG_BOXED }
227 }
228
229 #[inline(always)]
231 pub fn as_str(&self) -> &str {
232 unsafe {
236 let tag = self.data.inline[23];
238 if tag > INLINE_CAP as u8 && tag != TAG_STATIC && tag != TAG_BOXED {
240 std::hint::unreachable_unchecked();
241 }
242 let is_inline = tag <= INLINE_CAP as u8;
244 let len = if is_inline {
246 tag as usize
247 } else {
248 self.data.heap.len
249 };
250 let ptr = if is_inline {
252 self.data.inline.as_ptr()
253 } else {
254 self.data.heap.ptr
255 };
256 std::str::from_utf8_unchecked(std::slice::from_raw_parts(ptr, len))
258 }
259 }
260
261 #[inline]
263 pub fn len(&self) -> usize {
264 self.as_str().len()
265 }
266
267 #[inline]
269 pub fn is_empty(&self) -> bool {
270 self.as_str().is_empty()
271 }
272
273 #[inline]
275 pub fn into_string(self) -> String {
276 self.as_str().to_owned()
277 }
278}
279
280impl Drop for Strand {
285 #[inline]
286 fn drop(&mut self) {
287 unsafe {
291 if self.data.inline[23] == TAG_BOXED {
292 let ptr = self.data.heap.ptr as *mut u8;
293 let len = self.data.heap.len;
294 let slice = std::ptr::slice_from_raw_parts_mut(ptr, len);
295 let _ = Box::from_raw(slice as *mut str);
296 }
297 }
298 }
299}
300
301impl Clone for Strand {
306 #[inline]
307 fn clone(&self) -> Self {
308 unsafe {
313 let tag = self.data.inline[23];
314 if tag == TAG_BOXED {
315 #[cold]
316 #[inline(never)]
317 fn cold_clone(s: &str) -> Strand {
318 Strand::from(Box::from(s))
319 }
320 cold_clone(self.as_str())
321 } else {
322 std::ptr::read(self as *const Strand)
324 }
325 }
326 }
327}
328
329impl Default for Strand {
334 #[inline]
335 fn default() -> Self {
336 Self {
337 data: StrandData {
338 inline: [0u8; 24],
339 },
340 }
341 }
342}
343
344impl Deref for Strand {
345 type Target = str;
346 #[inline]
347 fn deref(&self) -> &str {
348 self.as_str()
349 }
350}
351
352impl AsRef<str> for Strand {
353 #[inline]
354 fn as_ref(&self) -> &str {
355 self.as_str()
356 }
357}
358
359impl Borrow<str> for Strand {
360 #[inline]
361 fn borrow(&self) -> &str {
362 self.as_str()
363 }
364}
365
366impl From<&str> for Strand {
371 #[inline]
372 fn from(s: &str) -> Self {
373 Self::new(s)
374 }
375}
376
377impl From<String> for Strand {
378 #[inline]
379 fn from(s: String) -> Self {
380 if s.len() <= INLINE_CAP {
381 Self::new_inline(&s)
382 } else {
383 Self::from(s.into_boxed_str())
384 }
385 }
386}
387
388impl From<&String> for Strand {
389 #[inline]
390 fn from(s: &String) -> Self {
391 Self::new(s.as_str())
392 }
393}
394
395impl From<Box<str>> for Strand {
396 #[inline]
397 fn from(s: Box<str>) -> Self {
398 if s.len() <= INLINE_CAP {
399 Self::new_inline(&s)
400 } else {
401 let ptr = s.as_ptr();
402 let len = s.len();
403 std::mem::forget(s);
404 Self {
405 data: StrandData {
406 heap: ManuallyDrop::new(HeapData {
407 ptr,
408 len,
409 _pad: [0; HEAP_PAD_LEN],
410 tag: TAG_BOXED,
411 }),
412 },
413 }
414 }
415 }
416}
417
418impl From<Strand> for String {
419 #[inline]
420 fn from(s: Strand) -> String {
421 s.as_str().to_owned()
422 }
423}
424
425impl From<&Strand> for String {
426 #[inline]
427 fn from(s: &Strand) -> String {
428 s.as_str().to_owned()
429 }
430}
431
432impl Eq for Strand {}
437
438impl PartialEq for Strand {
439 #[inline]
440 fn eq(&self, other: &Self) -> bool {
441 unsafe {
447 let tag_a = self.data.inline[23];
449 let tag_b = other.data.inline[23];
450 if tag_a <= INLINE_CAP as u8 && tag_b <= INLINE_CAP as u8 {
452 return self.data.inline == other.data.inline;
454 }
455 }
456 self.as_str() == other.as_str()
458 }
459}
460
461impl PartialEq<str> for Strand {
462 #[inline]
463 fn eq(&self, other: &str) -> bool {
464 self.as_str() == other
465 }
466}
467
468impl PartialEq<&str> for Strand {
469 #[inline]
470 fn eq(&self, other: &&str) -> bool {
471 self.as_str() == *other
472 }
473}
474
475impl PartialEq<String> for Strand {
476 #[inline]
477 fn eq(&self, other: &String) -> bool {
478 self.as_str() == other.as_str()
479 }
480}
481
482impl Ord for Strand {
483 #[inline]
484 fn cmp(&self, other: &Self) -> Ordering {
485 unsafe {
489 let tag_a = self.data.inline[23];
491 let tag_b = other.data.inline[23];
492 if tag_a <= INLINE_CAP as u8 && tag_b <= INLINE_CAP as u8 {
494 let len_a = tag_a as usize;
496 let len_b = tag_b as usize;
497 let slice_a = std::slice::from_raw_parts(self.data.inline.as_ptr(), len_a);
500 let slice_b = std::slice::from_raw_parts(other.data.inline.as_ptr(), len_b);
501 return slice_a.cmp(slice_b);
503 }
504 }
505 self.as_str().cmp(other.as_str())
507 }
508}
509
510impl PartialOrd for Strand {
511 #[inline]
512 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
513 Some(self.cmp(other))
514 }
515}
516
517impl PartialOrd<str> for Strand {
518 #[inline]
519 fn partial_cmp(&self, other: &str) -> Option<Ordering> {
520 self.as_str().partial_cmp(other)
521 }
522}
523
524impl PartialOrd<String> for Strand {
525 #[inline]
526 fn partial_cmp(&self, other: &String) -> Option<Ordering> {
527 self.as_str().partial_cmp(other.as_str())
528 }
529}
530
531impl Hash for Strand {
532 #[inline]
533 fn hash<H: Hasher>(&self, state: &mut H) {
534 self.as_str().hash(state)
535 }
536}
537
538impl Debug for Strand {
539 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
540 Debug::fmt(self.as_str(), f)
541 }
542}
543
544impl Display for Strand {
545 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
546 Display::fmt(self.as_str(), f)
547 }
548}
549
550impl Serialize for Strand {
555 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
556 where
557 S: Serializer,
558 {
559 serializer.serialize_str(self.as_str())
560 }
561}
562
563impl<'de> Deserialize<'de> for Strand {
564 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
565 where
566 D: Deserializer<'de>,
567 {
568 struct StrandVisitor;
569
570 impl<'de> Visitor<'de> for StrandVisitor {
571 type Value = Strand;
572
573 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
574 f.write_str("a string")
575 }
576
577 fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
578 Ok(Strand::from(v))
579 }
580
581 fn visit_string<E: de::Error>(self, v: String) -> Result<Self::Value, E> {
582 Ok(Strand::from(v))
583 }
584 }
585
586 deserializer.deserialize_str(StrandVisitor)
587 }
588}
589
590impl Revisioned for Strand {
595 #[inline]
596 fn revision() -> u16 {
597 1
598 }
599}
600
601impl SerializeRevisioned for Strand {
602 #[inline]
603 fn serialize_revisioned<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
604 self.as_str().serialize_revisioned(writer)
605 }
606}
607
608impl DeserializeRevisioned for Strand {
609 #[inline]
610 fn deserialize_revisioned<R: std::io::Read>(reader: &mut R) -> Result<Self, Error> {
611 let len = usize::deserialize_revisioned(reader)?;
612 if len == 0 {
613 return Ok(Self::default());
614 }
615 if len <= INLINE_CAP {
616 let mut inline = [0u8; 24];
617 reader.read_exact(&mut inline[..len]).map_err(Error::Io)?;
618 std::str::from_utf8(&inline[..len]).map_err(Error::Utf8Error)?;
619 inline[23] = len as u8;
620 return Ok(Strand {
621 data: StrandData {
622 inline,
623 },
624 });
625 }
626 let mut buf = vec![0u8; len];
627 reader.read_exact(&mut buf).map_err(Error::Io)?;
628 let s = String::from_utf8(buf).map_err(|e| Error::Utf8Error(e.utf8_error()))?;
629 Ok(Strand::from(s.into_boxed_str()))
630 }
631}
632
633impl revision::SkipRevisioned for Strand {
634 #[inline]
635 fn skip_revisioned<R: std::io::Read>(reader: &mut R) -> Result<(), Error> {
636 <String as revision::SkipRevisioned>::skip_revisioned(reader)
637 }
638
639 #[inline]
640 fn skip_revisioned_slice(reader: &mut revision::SliceReader<'_>) -> Result<(), Error> {
641 <String as revision::SkipRevisioned>::skip_revisioned_slice(reader)
642 }
643}
644
645impl revision::WalkRevisioned for Strand {
646 type Walker<'r, R: revision::BorrowedReader + 'r> = revision::LeafWalker<'r, Strand, R>;
647
648 #[inline]
649 fn walk_revisioned<'r, R: revision::BorrowedReader>(
650 reader: &'r mut R,
651 ) -> Result<Self::Walker<'r, R>, Error> {
652 Ok(revision::LeafWalker::new(reader))
653 }
654}
655
656impl revision::LengthPrefixedBytes for Strand {}
657
658impl<F> storekey::Encode<F> for Strand {
663 #[inline]
664 fn encode<W: std::io::Write>(
665 &self,
666 writer: &mut storekey::Writer<W>,
667 ) -> Result<(), storekey::EncodeError> {
668 <str as storekey::Encode<F>>::encode(self.as_str(), writer)
669 }
670}
671
672impl<'de, F> storekey::BorrowDecode<'de, F> for Strand {
673 #[inline]
674 fn borrow_decode(
675 reader: &mut storekey::BorrowReader<'de>,
676 ) -> Result<Self, storekey::DecodeError> {
677 let cow = reader.read_str_cow()?;
678 let s: &str = &cow;
679 Ok(if s.len() <= INLINE_CAP {
680 Self::new_inline(s)
681 } else {
682 Self::from(Box::from(s))
683 })
684 }
685}
686
687impl<F> storekey::Decode<F> for Strand {
688 #[inline]
689 fn decode<R: std::io::BufRead>(
690 reader: &mut storekey::Reader<R>,
691 ) -> Result<Self, storekey::DecodeError> {
692 let bytes = reader.read_vec()?;
693 let s = std::str::from_utf8(&bytes).map_err(|_| storekey::DecodeError::Utf8)?;
694 Ok(if s.len() <= INLINE_CAP {
695 Self::new_inline(s)
696 } else {
697 Self::from(Box::from(s))
698 })
699 }
700}
701
702#[cfg(feature = "arbitrary")]
707impl<'a> arbitrary::Arbitrary<'a> for Strand {
708 #[inline]
709 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
710 let s = <&str as arbitrary::Arbitrary<'a>>::arbitrary(u)?;
711 Ok(Strand::from(s))
712 }
713
714 #[inline]
715 fn size_hint(depth: usize) -> (usize, Option<usize>) {
716 <&str as arbitrary::Arbitrary<'a>>::size_hint(depth)
717 }
718}
719
720mod table;
721
722pub use table::TableName;
723
724#[cfg(test)]
725mod tests {
726 use super::*;
727
728 const SHORT: &str = "hello";
729 const AT_CAP: &str = "abcdefghijklmnopqrstuvw";
731 const LONG: &str = "this string is intentionally much longer than twenty three bytes so it must live on the heap";
732
733 #[test]
736 fn stack_size_is_24_bytes() {
737 use std::mem::size_of;
738 assert_eq!(size_of::<Strand>(), 24);
742 }
743
744 #[test]
747 fn short_strings_are_inline() {
748 let s = Strand::from(SHORT);
749 assert!(s.is_inline());
750 assert_eq!(s.as_str(), SHORT);
751 }
752
753 #[test]
754 fn long_strings_are_boxed() {
755 let s = Strand::from(LONG);
756 assert!(!s.is_inline());
757 assert!(!s.is_static());
758 assert!(s.is_boxed());
759 assert_eq!(s.as_str(), LONG);
760 }
761
762 #[test]
763 fn inline_boundary() {
764 assert_eq!(AT_CAP.len(), INLINE_CAP);
765 assert!(Strand::from(AT_CAP).is_inline());
766
767 let over: String = "a".repeat(INLINE_CAP + 1);
768 assert!(!Strand::from(over.as_str()).is_inline());
769 }
770
771 #[test]
772 fn empty_is_inline() {
773 let s = Strand::from("");
774 assert!(s.is_inline());
775 assert!(s.is_empty());
776 assert_eq!(s.as_str(), "");
777 }
778
779 #[test]
780 fn default_is_empty_inline() {
781 let s = Strand::default();
782 assert!(s.is_inline());
783 assert!(s.is_empty());
784 }
785
786 #[test]
789 fn inline_clone_is_independent_copy() {
790 let s = Strand::from(SHORT);
791 let t = s.clone();
792 assert_eq!(s.as_str(), t.as_str());
793 drop(s);
795 assert_eq!(t.as_str(), SHORT);
796 }
797
798 #[test]
799 fn boxed_clone_is_deep_copy() {
800 let s = Strand::from(LONG);
801 assert!(s.is_boxed());
802 let t = s.clone();
803 assert_eq!(s.as_str(), t.as_str());
804 assert_ne!(s.as_str().as_ptr(), t.as_str().as_ptr());
807 drop(s);
809 assert_eq!(t.as_str(), LONG);
810 }
811
812 #[test]
815 fn new_static_is_static() {
816 let s = Strand::new_static("foo");
817 assert!(s.is_static());
818 assert!(!s.is_inline());
819 assert!(!s.is_boxed());
820 assert_eq!(s.as_str(), "foo");
821 }
822
823 #[test]
824 fn new_static_long_is_still_static() {
825 let s = Strand::new_static(LONG);
828 assert!(s.len() > INLINE_CAP);
829 assert!(s.is_static());
830 assert!(!s.is_boxed());
831 assert_eq!(s.as_str(), LONG);
832 }
833
834 #[test]
835 fn new_static_is_const() {
836 const SHORT_STATIC: Strand = Strand::new_static("foo");
839 const LONG_STATIC: Strand =
840 Strand::new_static("this literal is longer than INLINE_CAP but costs nothing");
841 assert_eq!(SHORT_STATIC.as_str(), "foo");
842 assert!(LONG_STATIC.as_str().len() > INLINE_CAP);
843 }
844
845 #[test]
846 fn static_clone_is_pointer_copy() {
847 let original = "some compile-time string";
848 let s = Strand::new_static(original);
849 let t = s.clone();
850 assert_eq!(s.as_str().as_ptr(), original.as_ptr());
853 assert_eq!(t.as_str().as_ptr(), original.as_ptr());
854 }
855
856 #[test]
859 fn cross_repr_equality() {
860 let inline = Strand::from("abc");
863 let stat = Strand::new_static("abc");
864 let boxed = Strand::from("a".repeat(INLINE_CAP + 1));
865 let boxed2 = Strand::from(boxed.as_str());
866 assert!(inline.is_inline());
867 assert!(stat.is_static());
868 assert!(boxed.is_boxed());
869 assert_eq!(inline, stat);
870 assert_eq!(stat, inline);
871 assert_eq!(boxed, boxed2);
872 }
873
874 #[test]
875 fn ord_is_lexicographic() {
876 let a = Strand::from("apple");
877 let b = Strand::from("banana");
878 assert!(a < b);
879 }
880
881 #[test]
882 fn hashing_works_as_map_key() {
883 use std::collections::HashMap;
884 let mut m = HashMap::new();
885 m.insert(Strand::from("k"), 1);
886 assert_eq!(m.get("k"), Some(&1));
887 }
888
889 #[test]
890 fn roundtrip_revisioned() {
891 let s = Strand::from("round trip");
892 let mut bytes = Vec::new();
893 s.serialize_revisioned(&mut bytes).unwrap();
894 let back = Strand::deserialize_revisioned(&mut bytes.as_slice()).unwrap();
895 assert_eq!(s, back);
896 }
897
898 #[test]
899 fn roundtrip_long_heap() {
900 let s = Strand::from(LONG);
901 let mut bytes = Vec::new();
902 s.serialize_revisioned(&mut bytes).unwrap();
903 let back = Strand::deserialize_revisioned(&mut bytes.as_slice()).unwrap();
904 assert_eq!(s, back);
905 assert!(!back.is_inline());
906 }
907
908 fn roundtrip_revisioned_for(s: &str, expect_inline: bool) {
916 let strand = Strand::from(s);
917 let mut bytes = Vec::new();
918 strand.serialize_revisioned(&mut bytes).unwrap();
919 let back = Strand::deserialize_revisioned(&mut bytes.as_slice()).unwrap();
920 assert_eq!(back.as_str(), s);
921 assert_eq!(back.is_inline(), expect_inline);
922 }
923
924 #[test]
925 fn roundtrip_revisioned_empty() {
926 roundtrip_revisioned_for("", true);
927 }
928
929 #[test]
930 fn roundtrip_revisioned_at_inline_cap() {
931 roundtrip_revisioned_for(AT_CAP, true);
932 assert_eq!(AT_CAP.len(), INLINE_CAP);
933 }
934
935 #[test]
936 fn roundtrip_revisioned_one_over_inline_cap() {
937 let over: String = "a".repeat(INLINE_CAP + 1);
938 roundtrip_revisioned_for(&over, false);
939 }
940
941 #[test]
946 fn roundtrip_revisioned_utf8_heap() {
947 let s = "δοκιμή αξιολόγησης κειμένου με πολυβυτικούς χαρακτήρες";
948 assert!(s.len() > INLINE_CAP);
949 roundtrip_revisioned_for(s, false);
950 }
951
952 #[test]
953 fn deserialize_revisioned_rejects_invalid_utf8() {
954 let mut bytes = Vec::new();
957 2usize.serialize_revisioned(&mut bytes).unwrap();
958 bytes.push(0xFF);
959 bytes.push(0xFE);
960 assert!(Strand::deserialize_revisioned(&mut bytes.as_slice()).is_err());
961 }
962
963 fn roundtrip_storekey_for(s: &str, expect_inline: bool) {
966 use storekey::{BorrowDecode, Decode, Encode};
967
968 let strand = Strand::from(s);
969 let mut buf = Vec::new();
971 let mut w = storekey::Writer::new(&mut buf);
972 <Strand as Encode<()>>::encode(&strand, &mut w).unwrap();
973
974 {
976 let mut r = storekey::BorrowReader::new(&buf);
977 let back = <Strand as BorrowDecode<'_, ()>>::borrow_decode(&mut r).unwrap();
978 assert_eq!(back.as_str(), s);
979 assert_eq!(back.is_inline(), expect_inline);
980 }
981
982 {
984 let mut r = storekey::Reader::new(buf.as_slice());
985 let back = <Strand as Decode<()>>::decode(&mut r).unwrap();
986 assert_eq!(back.as_str(), s);
987 assert_eq!(back.is_inline(), expect_inline);
988 }
989 }
990
991 #[test]
992 fn roundtrip_storekey_empty() {
993 roundtrip_storekey_for("", true);
994 }
995
996 #[test]
997 fn roundtrip_storekey_short() {
998 roundtrip_storekey_for(SHORT, true);
999 }
1000
1001 #[test]
1002 fn roundtrip_storekey_at_inline_cap() {
1003 roundtrip_storekey_for(AT_CAP, true);
1004 }
1005
1006 #[test]
1007 fn roundtrip_storekey_long() {
1008 roundtrip_storekey_for(LONG, false);
1009 }
1010
1011 #[test]
1015 fn roundtrip_storekey_with_escape_bytes() {
1016 roundtrip_storekey_for("abc\0def\x01ghi", true);
1017 let long_with_escapes: String = format!("{}\0{}", "x".repeat(30), "y".repeat(30));
1018 roundtrip_storekey_for(&long_with_escapes, false);
1019 }
1020
1021 fn wire_format_cases() -> Vec<String> {
1043 let at_cap_minus_one: String = "a".repeat(INLINE_CAP - 1);
1044 let at_cap: String = "a".repeat(INLINE_CAP);
1045 let at_cap_plus_one: String = "a".repeat(INLINE_CAP + 1);
1046 let utf8_heap = "δοκιμή αξιολόγησης κειμένου με πολυβυτικούς χαρακτήρες".to_owned();
1047 let escape_short = "abc\0def\x01ghi".to_owned();
1048 let escape_long = format!("{}\0{}", "x".repeat(30), "y".repeat(30));
1049 vec![
1050 String::new(),
1051 SHORT.to_owned(),
1052 at_cap_minus_one,
1053 at_cap,
1054 at_cap_plus_one,
1055 LONG.to_owned(),
1056 utf8_heap,
1057 escape_short,
1058 escape_long,
1059 ]
1060 }
1061
1062 #[test]
1066 fn revisioned_wire_matches_string() {
1067 for input in wire_format_cases() {
1068 let strand = Strand::from(input.as_str());
1069
1070 let mut strand_bytes = Vec::new();
1071 strand.serialize_revisioned(&mut strand_bytes).unwrap();
1072
1073 let mut string_bytes = Vec::new();
1074 input.serialize_revisioned(&mut string_bytes).unwrap();
1075
1076 assert_eq!(
1078 strand_bytes, string_bytes,
1079 "Strand and String must produce identical revisioned bytes for {:?}",
1080 input
1081 );
1082
1083 let from_string_bytes =
1085 Strand::deserialize_revisioned(&mut string_bytes.as_slice()).unwrap();
1086 assert_eq!(from_string_bytes.as_str(), input);
1087
1088 let from_strand_bytes =
1090 String::deserialize_revisioned(&mut strand_bytes.as_slice()).unwrap();
1091 assert_eq!(from_strand_bytes, input);
1092 }
1093 }
1094
1095 #[test]
1098 fn revision_leaf_walker_with_bytes_matches_strand_utf8() {
1099 use revision::{SerializeRevisioned, WalkRevisioned};
1100 let s = Strand::from("hello ρ");
1101 let mut buf = Vec::new();
1102 s.serialize_revisioned(&mut buf).unwrap();
1103 let mut r = buf.as_slice();
1104 let w = Strand::walk_revisioned(&mut r).unwrap();
1105 w.with_bytes(|bytes| assert_eq!(bytes, s.as_str().as_bytes())).unwrap();
1106 }
1107
1108 #[test]
1111 fn storekey_wire_matches_string() {
1112 use storekey::{BorrowDecode, Decode, Encode};
1113
1114 for input in wire_format_cases() {
1115 let strand = Strand::from(input.as_str());
1116
1117 let mut strand_bytes = Vec::new();
1118 {
1119 let mut w = storekey::Writer::new(&mut strand_bytes);
1120 <Strand as Encode<()>>::encode(&strand, &mut w).unwrap();
1121 }
1122
1123 let mut string_bytes = Vec::new();
1124 {
1125 let mut w = storekey::Writer::new(&mut string_bytes);
1126 <String as Encode<()>>::encode(&input, &mut w).unwrap();
1127 }
1128
1129 assert_eq!(
1133 strand_bytes, string_bytes,
1134 "Strand and String must produce identical storekey bytes for {:?}",
1135 input
1136 );
1137
1138 {
1141 let mut r = storekey::BorrowReader::new(&string_bytes);
1142 let back = <Strand as BorrowDecode<'_, ()>>::borrow_decode(&mut r).unwrap();
1143 assert_eq!(back.as_str(), input);
1144 }
1145 {
1146 let mut r = storekey::Reader::new(string_bytes.as_slice());
1147 let back = <Strand as Decode<()>>::decode(&mut r).unwrap();
1148 assert_eq!(back.as_str(), input);
1149 }
1150
1151 {
1153 let mut r = storekey::BorrowReader::new(&strand_bytes);
1154 let back = <String as BorrowDecode<'_, ()>>::borrow_decode(&mut r).unwrap();
1155 assert_eq!(back, input);
1156 }
1157 {
1158 let mut r = storekey::Reader::new(strand_bytes.as_slice());
1159 let back = <String as Decode<()>>::decode(&mut r).unwrap();
1160 assert_eq!(back, input);
1161 }
1162 }
1163 }
1164}