1extern crate alloc;
11
12use alloc::string::ToString as _;
13use alloc::vec;
14use core::{borrow, cmp, fmt, hash, mem, ops, panic, slice, str};
15
16use crate::{StringPtr, TkStrError};
17
18pub const PREFIX_LENGTH: usize = mem::size_of::<u64>() - mem::size_of::<u16>();
21
22const PREFIX_LENGTH_ADD1: usize = PREFIX_LENGTH + 1;
24
25pub const SMALL_DATA_LENGTH: usize = mem::size_of::<u64>();
28
29pub const MAX_LENGTH_SMALL: usize = PREFIX_LENGTH + SMALL_DATA_LENGTH;
32
33pub const MAX_LENGTH_SMALL_ADD1: usize = MAX_LENGTH_SMALL + 1;
35
36pub const MAX_LENGTH: usize = u16::MAX as usize;
39
40#[repr(C)]
54pub struct TokenString {
55 pub(crate) len: u16,
59 pub(crate) prefix: [u8; PREFIX_LENGTH],
61 pub(crate) u: Data,
66}
67
68
69const _: () = assert!(
74 mem::align_of::<TokenString>() == mem::size_of::<u64>(),
75 "struct TokenString is not aligned to 64 bits!"
76);
77const _: () = assert!(
78 mem::size_of::<TokenString>() == 2 * mem::size_of::<u64>(),
79 "struct TokenString has size != 128 bits"
80);
81const _: () = assert!(
82 mem::align_of::<Data>() == mem::size_of::<u64>(),
83 "struct Data is not aligned to 64 bits!"
84);
85const _: () = assert!(
86 mem::size_of::<Data>() == mem::size_of::<u64>(),
87 "union Data has size != 64 bits"
88);
89
90#[repr(C)]
98pub union Data {
99 pub(crate) small: [u8; SMALL_DATA_LENGTH],
102 pub(crate) ptr: mem::ManuallyDrop<StringPtr>,
105}
106
107pub const EMPTY: TokenString = TokenString {
114 len: 0,
115 prefix: [0_u8; PREFIX_LENGTH],
116 u: Data {
117 small: [0_u8; SMALL_DATA_LENGTH],
118 },
119};
120
121impl TryFrom<&str> for TokenString {
125 type Error = TkStrError;
126
127 fn try_from(value: &str) -> Result<Self, Self::Error> {
137 let bytes = value.as_bytes();
138 match value.len() {
139 | 0 => Ok(Self {
140 len: 0,
141 prefix: [0_u8; PREFIX_LENGTH],
142 u: Data {
143 small: [0_u8; SMALL_DATA_LENGTH],
144 },
145 }),
146 | 1 ..= PREFIX_LENGTH => {
147 let s = value.len();
148 let mut prefix = [0_u8; PREFIX_LENGTH];
149 prefix[.. s].copy_from_slice(&bytes[.. s]);
150 Ok(Self {
151 #[expect(
152 clippy::cast_possible_truncation,
153 reason = "Length has been checked above"
154 )]
155 len: s as u16,
156 prefix,
157 u: Data {
158 small: [0_u8; SMALL_DATA_LENGTH],
159 },
160 })
161 }
162 | PREFIX_LENGTH_ADD1 ..= MAX_LENGTH_SMALL => {
163 let s = value.len();
164 let mut prefix = [0_u8; PREFIX_LENGTH];
165 prefix.copy_from_slice(&bytes[.. PREFIX_LENGTH]);
166 let mut small = [0_u8; SMALL_DATA_LENGTH];
167 small[.. s - PREFIX_LENGTH]
168 .copy_from_slice(&bytes[PREFIX_LENGTH .. s]);
169 Ok(Self {
170 #[expect(
171 clippy::cast_possible_truncation,
172 reason = "Length has been checked above"
173 )]
174 len: s as u16,
175 prefix,
176 u: Data { small },
177 })
178 }
179 | MAX_LENGTH_SMALL_ADD1 ..= MAX_LENGTH => {
180 let ptr = StringPtr::from(bytes);
181 let u = Data {
182 ptr: mem::ManuallyDrop::new(ptr),
183 };
184 let mut prefix = [0_u8; PREFIX_LENGTH];
185 prefix.copy_from_slice(&bytes[.. PREFIX_LENGTH]);
186 Ok(Self {
187 #[expect(
188 clippy::cast_possible_truncation,
189 reason = "Length has been checked above"
190 )]
191 len: value.len() as u16,
192 prefix,
193 u,
194 })
195 }
196 | _ => Err(TkStrError::TooBig(value.len())),
197 }
198 }
199}
200
201impl TryFrom<&[u8]> for TokenString {
202 type Error = TkStrError;
203
204 #[inline]
215 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
216 match str::from_utf8(value) {
217 | Ok(str) => Self::try_from(str),
218 | Err(utf_err) => Err(TkStrError::UnicodeError(utf_err)),
219 }
220 }
221}
222
223impl TryFrom<&[char]> for TokenString {
224 type Error = TkStrError;
225
226 #[inline]
236 fn try_from(value: &[char]) -> Result<Self, Self::Error> {
237 let i = value.iter();
238 Self::try_from(i.collect::<alloc::string::String>())
239 }
240}
241
242impl TryFrom<&alloc::string::String> for TokenString {
243 type Error = TkStrError;
244
245 #[inline]
255 fn try_from(value: &alloc::string::String) -> Result<Self, Self::Error> {
256 let str = value.as_str();
257 Self::try_from(str)
258 }
259}
260
261impl TryFrom<alloc::string::String> for TokenString {
262 type Error = TkStrError;
263
264 #[inline]
274 fn try_from(value: alloc::string::String) -> Result<Self, Self::Error> {
275 let str = value.as_str();
279 Self::try_from(str)
280 }
281}
282
283impl Drop for TokenString {
284 #[cfg_attr(test, mutants::skip)]
285 #[inline]
286 fn drop(&mut self) {
287 if usize::from(self.len) > MAX_LENGTH_SMALL {
288 let mut m_ptr = unsafe { mem::ManuallyDrop::take(&mut self.u.ptr) };
293 m_ptr.drop_manually(self.len.into());
294 }
295 }
296}
297
298impl Clone for TokenString {
299 #[inline]
306 fn clone(&self) -> Self {
307 let u = if self.len as usize > MAX_LENGTH_SMALL {
308 Data {
309 ptr: mem::ManuallyDrop::new(unsafe {
313 self.u.ptr.clone_manually(self.len.into())
314 }),
315 }
316 } else {
317 Data {
318 small: unsafe { self.u.small },
321 }
322 };
323 Self {
324 len: self.len,
325 prefix: self.prefix,
326 u,
327 }
328 }
329}
330
331impl Default for TokenString {
332 #[inline]
334 fn default() -> Self {
335 EMPTY
336 }
337}
338
339impl Eq for TokenString {}
340
341impl PartialEq for TokenString {
342 #[inline]
343 fn eq(&self, other: &Self) -> bool {
344 if self.len != other.len || self.prefix != other.prefix {
345 return false;
346 }
347
348 if self.len as usize <= MAX_LENGTH_SMALL {
349 unsafe { self.u.small == other.u.small }
352 } else {
353 unsafe { self.u.ptr.eq_manually(&other.u.ptr, self.len.into()) }
356 }
357 }
358}
359
360impl PartialEq<[u8]> for TokenString {
361 fn eq(&self, other: &[u8]) -> bool {
362 if self.len as usize != other.len() {
363 return false;
364 }
365 let len = self.len as usize;
366 match len {
367 | 0 => true,
368 | 1 ..= PREFIX_LENGTH => self.prefix[.. len] == other[.. len],
369 | PREFIX_LENGTH_ADD1 ..= MAX_LENGTH_SMALL => {
370 let bytes =
379 unsafe { slice::from_raw_parts(self.prefix.as_ptr(), len) };
380 bytes == other
381 }
382 | MAX_LENGTH_SMALL_ADD1 ..= MAX_LENGTH => unsafe {
385 self.u.ptr.as_slice_manually(len) == other
386 },
387 | _ => panic!("The TokenString is bigger than MAX_LENGTH!"),
388 }
389 }
390}
391
392impl PartialEq<str> for TokenString {
393 #[inline]
394 fn eq(&self, other: &str) -> bool {
395 self == other.as_bytes()
396 }
397}
398
399impl PartialEq<alloc::string::String> for TokenString {
400 #[inline]
401 fn eq(&self, other: &alloc::string::String) -> bool {
402 self == other.as_bytes()
403 }
404}
405
406
407impl Ord for TokenString {
408 #[inline]
413 fn cmp(&self, other: &Self) -> cmp::Ordering {
414 let pref_ord = self.prefix.cmp(&other.prefix);
415 if pref_ord != cmp::Ordering::Equal {
416 return pref_ord;
417 }
418
419 self.suffix().cmp(other.suffix())
420 }
421}
422
423impl PartialOrd for TokenString {
424 #[inline]
429 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
430 Some(self.cmp(other))
431 }
432}
433
434impl fmt::Display for TokenString {
435 #[inline]
436 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
437 write!(f, "{}", self.as_str())
438 }
439}
440
441impl fmt::Debug for TokenString {
442 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
443 if self.len as usize > MAX_LENGTH_SMALL {
444 let string =
445 unsafe { self.u.ptr.as_string_manually(self.len.into()) };
448 let ptr = unsafe { &self.u.ptr };
451 f.debug_struct("TokenString")
452 .field("len", &self.len)
453 .field("prefix", &self.prefix_str())
454 .field("ptr", ptr)
455 .field("string", &string)
456 .finish()
457 } else {
458 unsafe {
461 f.debug_struct("TokenString")
462 .field("len", &self.len)
463 .field("prefix", &self.prefix_str())
464 .field("small", &self.small_str())
465 .field("string", &self.as_str())
466 .finish()
467 }
468 }
469 }
470}
471
472impl<Idx> ops::Index<Idx> for TokenString
473where
474 Idx: slice::SliceIndex<str>,
475{
476 type Output = Idx::Output;
477
478 #[inline]
479 fn index(&self, index: Idx) -> &Self::Output {
480 self.as_str().index(index)
481 }
482}
483
484impl borrow::Borrow<str> for TokenString {
485 #[inline]
486 fn borrow(&self) -> &str {
487 self.as_str()
488 }
489}
490
491impl AsRef<str> for TokenString {
492 #[inline]
493 fn as_ref(&self) -> &str {
494 self.as_str()
495 }
496}
497
498impl hash::Hash for TokenString {
499 #[inline]
500 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
501 self.as_str().hash(state);
502 }
503}
504
505unsafe impl Send for TokenString {}
508
509unsafe impl Sync for TokenString {}
512
513impl TokenString {
517 fn prefix_str(&self) -> &[u8] {
519 let l = cmp::min(self.len as usize, PREFIX_LENGTH);
520 &self.prefix[.. l]
521 }
522
523 unsafe fn small_str(&self) -> &[u8] {
529 let l = (self.len as usize).saturating_sub(PREFIX_LENGTH);
530 unsafe { &self.u.small[.. l] }
533 }
534
535 #[must_use]
540 #[inline]
541 pub const fn len(&self) -> usize {
542 self.len as usize
543 }
544
545 #[must_use]
550 #[inline]
551 pub const fn is_small(&self) -> bool {
552 self.len as usize <= MAX_LENGTH_SMALL
553 }
554
555 #[must_use]
559 #[inline]
560 pub const fn is_empty(&self) -> bool {
561 self.len == 0
562 }
563
564 #[must_use]
583 pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> Self {
584 match bytes.len() {
585 | 0 => Self {
586 len: 0,
587 prefix: [0_u8; PREFIX_LENGTH],
588 u: Data {
589 small: [0_u8; SMALL_DATA_LENGTH],
590 },
591 },
592 | 1 ..= PREFIX_LENGTH => {
593 let s = bytes.len();
594 let mut prefix = [0_u8; PREFIX_LENGTH];
595 prefix[.. s].copy_from_slice(&bytes[.. s]);
596 Self {
597 #[expect(
598 clippy::cast_possible_truncation,
599 reason = "Length has been checked above"
600 )]
601 len: s as u16,
602 prefix,
603 u: Data {
604 small: [0_u8; SMALL_DATA_LENGTH],
605 },
606 }
607 }
608 | PREFIX_LENGTH_ADD1 ..= MAX_LENGTH_SMALL => {
609 let s = bytes.len();
610 let mut prefix = [0_u8; PREFIX_LENGTH];
611 prefix.copy_from_slice(&bytes[.. PREFIX_LENGTH]);
612 let mut small = [0_u8; SMALL_DATA_LENGTH];
613 small[.. s - PREFIX_LENGTH]
614 .copy_from_slice(&bytes[PREFIX_LENGTH .. s]);
615 Self {
616 #[expect(
617 clippy::cast_possible_truncation,
618 reason = "Length has been checked above"
619 )]
620 len: s as u16,
621 prefix,
622 u: Data { small },
623 }
624 }
625 | MAX_LENGTH_SMALL_ADD1 ..= MAX_LENGTH => {
626 let ptr = StringPtr::from(bytes);
627 let u = Data {
628 ptr: mem::ManuallyDrop::new(ptr),
629 };
630 let mut prefix = [0_u8; PREFIX_LENGTH];
631 prefix.copy_from_slice(&bytes[.. PREFIX_LENGTH]);
632 Self {
633 #[expect(
634 clippy::cast_possible_truncation,
635 reason = "Length has been checked above"
636 )]
637 len: bytes.len() as u16,
638 prefix,
639 u,
640 }
641 }
642 | _ => panic!(
643 "This byte slice is too big for a TokenString, {} > \
644 {MAX_LENGTH}",
645 bytes.len()
646 ),
647 }
648 }
649
650 #[must_use]
665 #[inline]
666 pub fn from_str_unchecked(s: &str) -> Self {
667 unsafe { Self::from_bytes_unchecked(s.as_bytes()) }
672 }
673
674 #[must_use]
689 #[inline]
690 pub fn from_string_unchecked(s: &alloc::string::String) -> Self {
691 unsafe { Self::from_bytes_unchecked(s.as_bytes()) }
696 }
697
698 #[must_use]
700 #[inline]
701 pub fn as_str(&self) -> &str {
702 if self.len == 0 {
703 ""
704 } else if self.len as usize > MAX_LENGTH_SMALL {
705 unsafe { self.u.ptr.as_string_manually(self.len.into()) }
708 } else {
709 let bytes = unsafe {
717 slice::from_raw_parts(self.prefix.as_ptr(), self.len.into())
718 };
719 unsafe { str::from_utf8_unchecked(bytes) }
723 }
724 }
725
726 #[must_use]
728 #[inline]
729 pub fn as_bytes(&self) -> &[u8] {
730 if self.len == 0 {
731 Default::default()
732 } else if self.len as usize > MAX_LENGTH_SMALL {
733 unsafe { self.u.ptr.as_slice_manually(self.len.into()) }
736 } else {
737 unsafe {
745 slice::from_raw_parts(self.prefix.as_ptr(), self.len.into())
746 }
747 }
748 }
749
750 #[must_use]
756 #[inline]
757 pub fn as_string(&self) -> alloc::string::String {
758 self.to_string()
759 }
760
761 #[must_use]
767 #[inline]
768 pub fn as_chars(&self) -> vec::Vec<char> {
769 self.as_str().chars().collect()
770 }
771
772 fn suffix(&self) -> &[u8] {
776 match self.len as usize {
777 | 0 ..= PREFIX_LENGTH => Default::default(),
778 | PREFIX_LENGTH_ADD1 ..= MAX_LENGTH_SMALL =>
779 unsafe { &self.u.small },
782 | MAX_LENGTH_SMALL_ADD1 ..= MAX_LENGTH =>
783 unsafe {
786 &self.u.ptr.as_slice_manually(self.len.into())[PREFIX_LENGTH ..]
787 },
788 | _ => panic!(
789 "Error: this TokenString is bigger than \
790 TokenString::MAX_LENGTH!"
791 ),
792 }
793 }
794
795 #[inline]
803 pub fn get(&self, idx: u16) -> Result<u8, TkStrError> {
804 if idx >= self.len {
805 return Err(TkStrError::OutOfBounds(idx as usize));
806 }
807 unsafe { Ok(*self.as_bytes().get_unchecked(idx as usize)) }
810 }
811
812 #[must_use]
821 #[inline]
822 pub fn get_unchecked(&self, idx: u16) -> u8 {
823 assert!((idx < self.len), "index {idx} out of bounds");
824 unsafe { *self.as_bytes().get_unchecked(idx as usize) }
827 }
828
829 #[inline]
834 pub fn chars(&'_ self) -> str::Chars<'_> {
835 self.as_str().chars()
836 }
837
838 #[must_use]
840 #[inline]
841 pub fn iter(&self) -> TokenStringIter<'_> {
842 <&Self as IntoIterator>::into_iter(self)
843 }
844
845 #[must_use]
847 #[inline]
848 pub const fn starts_ascii_uppercase(&self) -> bool {
849 self.prefix[0].is_ascii_uppercase()
850 }
851
852 #[must_use]
854 #[inline]
855 pub const fn starts_ascii_lowercase(&self) -> bool {
856 self.prefix[0].is_ascii_lowercase()
857 }
858
859 #[must_use]
861 #[inline]
862 pub fn is_ascii(&self) -> bool {
863 self.as_bytes().is_ascii()
864 }
865
866 #[must_use]
870 #[inline]
871 pub fn starts_with(&self, needle: &Self) -> bool {
872 self.as_bytes().starts_with(needle.as_bytes())
873 }
874
875 #[must_use]
879 #[inline]
880 pub fn starts_with_bytes(&self, needle: &[u8]) -> bool {
881 self.as_bytes().starts_with(needle)
882 }
883
884 #[must_use]
888 #[inline]
889 pub fn starts_with_str(&self, needle: &str) -> bool {
890 self.as_str().starts_with(needle)
891 }
892
893 #[must_use]
897 #[inline]
898 pub fn ends_with(&self, needle: &Self) -> bool {
899 self.as_bytes().ends_with(needle.as_bytes())
900 }
901
902 #[must_use]
906 #[inline]
907 pub fn ends_with_bytes(&self, needle: &[u8]) -> bool {
908 self.as_bytes().ends_with(needle)
909 }
910
911 #[must_use]
915 #[inline]
916 pub fn ends_with_str(&self, needle: &str) -> bool {
917 self.as_str().ends_with(needle)
918 }
919
920 fn map_bytes_mut(&mut self, f: fn(&mut [u8]) -> ()) {
922 if self.len as usize > MAX_LENGTH_SMALL {
923 unsafe {
926 f((*self.u.ptr).as_slice_manually_mut(self.len as usize));
927 }
928 } else {
929 unsafe {
933 f(slice::from_raw_parts_mut(
934 self.prefix.as_mut_ptr(),
935 self.len as usize,
936 ));
937 }
938 }
939 }
940
941 #[must_use]
944 #[inline]
945 pub fn to_ascii_lowercase(&self) -> Self {
946 let mut ret_val = self.clone();
947 ret_val.map_bytes_mut(<[u8]>::make_ascii_lowercase);
948 ret_val
949 }
950
951 #[must_use]
954 #[inline]
955 pub fn to_ascii_uppercase(&self) -> Self {
956 let mut ret_val = self.clone();
957 ret_val.map_bytes_mut(<[u8]>::make_ascii_uppercase);
958 ret_val
959 }
960
961 #[must_use]
964 #[inline]
965 pub fn trim_ascii(&self) -> Self {
966 unsafe { Self::from_bytes_unchecked(self.as_bytes().trim_ascii()) }
971 }
972
973 #[must_use]
975 #[inline]
976 pub fn trim_ascii_start(&self) -> Self {
977 unsafe {
985 Self::from_bytes_unchecked(self.as_bytes().trim_ascii_start())
986 }
987 }
988
989 #[must_use]
991 #[inline]
992 pub fn trim_ascii_end(&self) -> Self {
993 unsafe { Self::from_bytes_unchecked(self.as_bytes().trim_ascii_end()) }
1001 }
1002
1003 #[cfg(feature = "pattern")]
1005 #[doc(cfg(pattern))]
1006 #[inline]
1007 pub fn strip_prefix<P: str::pattern::Pattern>(
1008 &self,
1009 prefix: P,
1010 ) -> Option<Self> {
1011 self.as_str()
1012 .strip_prefix(prefix)
1013 .map(Self::from_str_unchecked)
1016 }
1017
1018 #[cfg(feature = "pattern")]
1020 #[doc(cfg(pattern))]
1021 #[inline]
1022 pub fn strip_suffix<P>(&self, suffix: P) -> Option<Self>
1023 where
1024 P: str::pattern::Pattern,
1025 for<'a> P::Searcher<'a>: str::pattern::ReverseSearcher<'a>,
1026 {
1027 self.as_str()
1028 .strip_suffix(suffix)
1029 .map(Self::from_str_unchecked)
1032 }
1033
1034 #[cfg(feature = "pattern")]
1040 #[doc(cfg(pattern))]
1041 #[inline]
1042 pub fn contains<P: str::pattern::Pattern>(&self, pat: P) -> bool {
1043 self.as_str().contains(pat)
1044 }
1045}
1046
1047
1048pub struct TokenStringIter<'a> {
1055 string: &'a TokenString,
1057 idx: usize,
1059}
1060
1061impl<'a> TokenStringIter<'a> {
1062 #[must_use]
1064 #[inline]
1065 pub const fn new(s: &'a TokenString) -> Self {
1066 TokenStringIter { string: s, idx: 0 }
1067 }
1068}
1069
1070impl Iterator for TokenStringIter<'_> {
1071 type Item = u8;
1072
1073 fn next(&mut self) -> Option<Self::Item> {
1076 debug_assert!(
1077 self.idx <= self.string.len.into(),
1078 "The iterator index '{0}' is greater than the string length '{1}'!",
1079 self.idx,
1080 self.string.len
1081 );
1082 if self.idx == self.string.len.into() {
1083 None
1084 } else if self.string.len as usize > MAX_LENGTH_SMALL {
1085 self.idx += 1;
1086 Some(self.string.as_bytes()[self.idx - 1])
1087 } else {
1088 self.idx += 1;
1089 Some(
1090 unsafe {
1094 slice::from_raw_parts(
1095 self.string.prefix.as_ptr(),
1096 self.string.len as usize,
1097 )
1098 }[self.idx - 1],
1099 )
1100 }
1101 }
1102}
1103
1104impl<'a> IntoIterator for &'a TokenString {
1105 type IntoIter = TokenStringIter<'a>;
1106 type Item = u8;
1107
1108 #[inline]
1109 fn into_iter(self) -> Self::IntoIter {
1110 Self::IntoIter::new(self)
1111 }
1112}
1113
1114pub struct TokenStringIterOwn {
1121 string: TokenString,
1123 idx: usize,
1125}
1126
1127impl TokenStringIterOwn {
1128 #[must_use]
1130 #[inline]
1131 pub const fn new(s: TokenString) -> Self {
1132 Self { string: s, idx: 0 }
1133 }
1134}
1135
1136impl Iterator for TokenStringIterOwn {
1137 type Item = u8;
1138
1139 fn next(&mut self) -> Option<Self::Item> {
1142 debug_assert!(
1143 self.idx <= self.string.len.into(),
1144 "The iterator index '{0}' is greater than the string length '{1}'!",
1145 self.idx,
1146 self.string.len
1147 );
1148 if self.idx == self.string.len.into() {
1149 None
1150 } else if self.string.len as usize > MAX_LENGTH_SMALL {
1151 self.idx += 1;
1152 Some(self.string.as_bytes()[self.idx - 1])
1153 } else {
1154 self.idx += 1;
1155 Some(
1156 unsafe {
1160 slice::from_raw_parts(
1161 self.string.prefix.as_ptr(),
1162 self.string.len as usize,
1163 )
1164 }[self.idx - 1],
1165 )
1166 }
1167 }
1168}
1169
1170impl IntoIterator for TokenString {
1171 type IntoIter = TokenStringIterOwn;
1172 type Item = u8;
1173
1174 #[inline]
1175 fn into_iter(self) -> Self::IntoIter {
1176 Self::IntoIter::new(self)
1177 }
1178}
1179
1180
1181#[cfg(test)]
1186mod prefix {
1187 extern crate std;
1188 use assert2::{check, let_assert};
1189
1190 use crate::TokenString;
1191
1192
1193 #[test]
1194 fn empty_is_empty() {
1195 let_assert!(Ok(res) = TokenString::try_from(""));
1196 check!(res.prefix[0] == 0);
1197 check!(res.len == 0);
1198 check!(res.is_small() == true);
1199 }
1200
1201 #[test]
1202 fn clone_empty() {
1203 let_assert!(Ok(s1) = TokenString::try_from(""));
1204 let res = s1.clone();
1205 check!(res.prefix[0] == s1.prefix[0]);
1206 check!(res.len == s1.len);
1207 check!(res.is_small() == true);
1208 }
1209
1210 #[test]
1211 fn try_from_str() {
1212 let_assert!(Ok(res) = TokenString::try_from("123456"));
1213 check!(&res.prefix[0 .. 6] == b"123456");
1214 check!(res.len == 6);
1215 check!(res.is_small() == true);
1216 }
1217
1218 #[test]
1219 fn clone() {
1220 let_assert!(Ok(s1) = TokenString::try_from("123456"));
1221 let res = s1.clone();
1222 check!(&res.prefix[0 .. 6] == &s1.prefix[0 .. 6]);
1223 check!(res.len == s1.len);
1224 check!(res.is_small() == true);
1225 }
1226
1227 #[test]
1228 fn try_from_bytes() {
1229 let s1: &[u8] = b"123456";
1230 let_assert!(Ok(res) = TokenString::try_from(s1));
1231 check!(&res.prefix[0 .. 6] == b"123456");
1232 check!(res.len == 6);
1233 check!(res.is_small() == true);
1234 }
1235
1236 #[test]
1237 fn try_from_chars() {
1238 #[expect(
1239 clippy::std_instead_of_alloc,
1240 reason = "We are testing, this needs std"
1241 )]
1242 let s1: std::vec::Vec<char> = "123456".chars().collect();
1243 let_assert!(Ok(res) = TokenString::try_from(s1.as_slice()));
1244 check!(&res.prefix[0 .. 6] == b"123456");
1245 check!(res.len == 6);
1246 check!(res.is_small() == true);
1247 }
1248
1249 #[test]
1250 fn try_from_string() {
1251 #[expect(
1252 clippy::std_instead_of_alloc,
1253 reason = "We are testing, this needs std"
1254 )]
1255 let s1: std::string::String = "123456".into();
1256 let_assert!(Ok(res) = TokenString::try_from(s1));
1257 check!(&res.prefix[0 .. 6] == b"123456");
1258 check!(res.len == 6);
1259 check!(res.is_small() == true);
1260 }
1261
1262 #[test]
1263 fn try_from_stringref() {
1264 #[expect(
1265 clippy::std_instead_of_alloc,
1266 reason = "We are testing, this needs std"
1267 )]
1268 let s1: std::string::String = "123456".into();
1269 let_assert!(Ok(res) = TokenString::try_from(&s1));
1270 check!(&res.prefix[0 .. 6] == b"123456");
1271 check!(res.len == 6);
1272 check!(res.is_small() == true);
1273 }
1274
1275 #[test]
1276 fn from_str_unchecked() {
1277 let res = TokenString::from_str_unchecked("123456");
1278 check!(&res.prefix[0 .. 6] == b"123456");
1279 check!(res.len == 6);
1280 }
1281
1282 #[test]
1283 fn from_bytes_unchecked() {
1284 let s1: &[u8] = b"123456";
1285 let res = unsafe { TokenString::from_bytes_unchecked(s1) };
1288 check!(&res.prefix[0 .. 6] == b"123456");
1289 check!(res.len == 6);
1290 check!(res.is_small() == true);
1291 }
1292
1293 #[test]
1294 fn from_stringref_unchecked() {
1295 #[expect(
1296 clippy::std_instead_of_alloc,
1297 reason = "We are testing, this needs std"
1298 )]
1299 let s1: std::string::String = "123456".into();
1300 let res = TokenString::from_string_unchecked(&s1);
1301 check!(&res.prefix[0 .. 6] == b"123456");
1302 check!(res.len == 6);
1303 check!(res.is_small() == true);
1304 }
1305}
1306
1307#[cfg(test)]
1308mod small {
1309 extern crate std;
1310 use assert2::{check, let_assert};
1311
1312 use crate::TokenString;
1313
1314
1315 #[test]
1316 fn try_from_str() {
1317 let_assert!(Ok(res) = TokenString::try_from("1234567"));
1318 check!(&res.prefix[0 .. 6] == b"123456");
1319 check!(unsafe { res.u.small[0] } == b'7');
1322 check!(res.len == 7);
1323 check!(res.is_small() == true);
1324 }
1325
1326 #[test]
1327 fn clone() {
1328 let_assert!(Ok(s1) = TokenString::try_from("1234567"));
1329 let res = s1.clone();
1330 check!(&res.prefix[0 .. 6] == &s1.prefix[0 .. 6]);
1331 check!(unsafe { res.u.small[0] == s1.u.small[0] });
1334 check!(res.len == s1.len);
1335 check!(res.is_small() == true);
1336 }
1337
1338 #[test]
1339 fn try_from_bytes() {
1340 let s1: &[u8] = b"1234567";
1341 let_assert!(Ok(res) = TokenString::try_from(s1));
1342 check!(&res.prefix[0 .. 6] == b"123456");
1343 check!(unsafe { res.u.small[0] } == b'7');
1346 check!(res.len == 7);
1347 check!(res.is_small() == true);
1348 }
1349
1350 #[test]
1351 fn try_from_chars() {
1352 #[expect(
1353 clippy::std_instead_of_alloc,
1354 reason = "We are testing, this needs std"
1355 )]
1356 let s1: std::vec::Vec<char> = "1234567".chars().collect();
1357 let_assert!(Ok(res) = TokenString::try_from(s1.as_slice()));
1358 check!(&res.prefix[0 .. 6] == b"123456");
1359 check!(unsafe { res.u.small[0] } == b'7');
1362 check!(res.len == 7);
1363 check!(res.is_small() == true);
1364 }
1365
1366 #[test]
1367 fn try_from_string() {
1368 #[expect(
1369 clippy::std_instead_of_alloc,
1370 reason = "We are testing, this needs std"
1371 )]
1372 let s1: std::string::String = "1234567".into();
1373 let_assert!(Ok(res) = TokenString::try_from(s1));
1374 check!(&res.prefix[0 .. 6] == b"123456");
1375 check!(unsafe { res.u.small[0] } == b'7');
1378 check!(res.len == 7);
1379 check!(res.is_small() == true);
1380 }
1381
1382 #[test]
1383 fn try_from_stringref() {
1384 #[expect(
1385 clippy::std_instead_of_alloc,
1386 reason = "We are testing, this needs std"
1387 )]
1388 let s1: std::string::String = "1234567".into();
1389 let_assert!(Ok(res) = TokenString::try_from(&s1));
1390 check!(&res.prefix[0 .. 6] == b"123456");
1391 check!(unsafe { res.u.small[0] } == b'7');
1394 check!(res.len == 7);
1395 check!(res.is_small() == true);
1396 }
1397
1398 #[test]
1399 fn from_str_unchecked() {
1400 let res = TokenString::from_str_unchecked("1234567");
1401 check!(&res.prefix[0 .. 6] == b"123456");
1402 check!(unsafe { res.u.small[0] } == b'7');
1405 check!(res.len == 7);
1406 check!(res.is_small() == true);
1407 }
1408
1409 #[test]
1410 fn from_bytes_unchecked() {
1411 let s1: &[u8] = b"1234567";
1412 let res = unsafe { TokenString::from_bytes_unchecked(s1) };
1415 check!(&res.prefix[0 .. 6] == b"123456");
1416 check!(unsafe { res.u.small[0] } == b'7');
1419 check!(res.len == 7);
1420 check!(res.is_small() == true);
1421 }
1422
1423 #[test]
1424 fn from_stringref_unchecked() {
1425 #[expect(
1426 clippy::std_instead_of_alloc,
1427 reason = "We are testing, this needs std"
1428 )]
1429 let s1: std::string::String = "1234567".into();
1430 let res = TokenString::from_string_unchecked(&s1);
1431 check!(&res.prefix[0 .. 6] == b"123456");
1432 check!(unsafe { res.u.small[0] } == b'7');
1435 check!(res.len == 7);
1436 check!(res.is_small() == true);
1437 }
1438}
1439
1440#[cfg(test)]
1441mod heap {
1442 extern crate std;
1443 use assert2::{check, let_assert};
1444
1445 use crate::TokenString;
1446
1447
1448 #[test]
1449 fn try_from_str() {
1450 let_assert!(Ok(res) = TokenString::try_from("1234567890ABCDE"));
1451 check!(&res.prefix[0 .. 6] == b"123456");
1452 check!(
1453 unsafe { &res.u.ptr.as_slice_manually(res.len as usize)[.. 15] }
1456 == b"1234567890ABCDE"
1457 );
1458 check!(res.len == 15);
1459 check!(res.is_small() == false);
1460 }
1461
1462
1463 #[test]
1464 fn clone() {
1465 let_assert!(Ok(s1) = TokenString::try_from("1234567890ABCDE"));
1466 let res = s1.clone();
1467 check!(&res.prefix[0 .. 6] == &s1.prefix[0 .. 6]);
1468 check!(
1469 unsafe {
1472 res.u.ptr.as_slice_manually(res.len as usize)[.. 15]
1473 == s1.u.ptr.as_slice_manually(res.len as usize)[.. 15]
1474 }
1475 );
1476 check!(res.len == s1.len);
1477 check!(res.is_small() == false);
1478 }
1479
1480 #[test]
1481 fn try_from_bytes() {
1482 let s1: &[u8] = b"1234567890ABCDE";
1483 let_assert!(Ok(res) = TokenString::try_from(s1));
1484 check!(&res.prefix[0 .. 6] == b"123456");
1485 check!(
1486 unsafe { &res.u.ptr.as_slice_manually(res.len as usize)[.. 15] }
1489 == b"1234567890ABCDE"
1490 );
1491 check!(res.len == 15);
1492 check!(res.is_small() == false);
1493 }
1494
1495 #[test]
1496 fn try_from_chars() {
1497 #[expect(
1498 clippy::std_instead_of_alloc,
1499 reason = "We are testing, this needs std"
1500 )]
1501 let s1: std::vec::Vec<char> = "1234567890ABCDE".chars().collect();
1502 let_assert!(Ok(res) = TokenString::try_from(s1.as_slice()));
1503 check!(&res.prefix[0 .. 6] == b"123456");
1504 check!(
1505 unsafe { &res.u.ptr.as_slice_manually(res.len as usize)[.. 15] }
1508 == b"1234567890ABCDE"
1509 );
1510 check!(res.len == 15);
1511 check!(res.is_small() == false);
1512 }
1513
1514 #[test]
1515 fn try_from_string() {
1516 #[expect(
1517 clippy::std_instead_of_alloc,
1518 reason = "We are testing, this needs std"
1519 )]
1520 let s1: std::string::String = "1234567890ABCDE".into();
1521 let_assert!(Ok(res) = TokenString::try_from(s1));
1522 check!(&res.prefix[0 .. 6] == b"123456");
1523 check!(
1524 unsafe { &res.u.ptr.as_slice_manually(res.len as usize)[.. 15] }
1527 == b"1234567890ABCDE"
1528 );
1529 check!(res.len == 15);
1530 check!(res.is_small() == false);
1531 }
1532
1533 #[test]
1534 fn try_from_stringref() {
1535 #[expect(
1536 clippy::std_instead_of_alloc,
1537 reason = "We are testing, this needs std"
1538 )]
1539 let s1: std::string::String = "1234567890ABCDE".into();
1540 let_assert!(Ok(res) = TokenString::try_from(&s1));
1541 check!(&res.prefix[0 .. 6] == b"123456");
1542 check!(
1543 unsafe { &res.u.ptr.as_slice_manually(res.len as usize)[.. 15] }
1546 == b"1234567890ABCDE"
1547 );
1548 check!(res.len == 15);
1549 check!(res.is_small() == false);
1550 }
1551
1552 #[test]
1553 fn from_str_unchecked() {
1554 let res = TokenString::from_str_unchecked("1234567890ABCDE");
1555 check!(&res.prefix[0 .. 6] == b"123456");
1556 check!(
1557 unsafe { &res.u.ptr.as_slice_manually(res.len as usize)[.. 15] }
1560 == b"1234567890ABCDE"
1561 );
1562 check!(res.len == 15);
1563 check!(res.is_small() == false);
1564 }
1565
1566 #[test]
1567 fn from_bytes_unchecked() {
1568 let s1: &[u8] = b"1234567890ABCDE";
1569 let res = unsafe { TokenString::from_bytes_unchecked(s1) };
1572 check!(&res.prefix[0 .. 6] == b"123456");
1573 check!(
1574 unsafe { &res.u.ptr.as_slice_manually(res.len as usize)[.. 15] }
1577 == b"1234567890ABCDE"
1578 );
1579 check!(res.len == 15);
1580 check!(res.is_small() == false);
1581 }
1582
1583 #[test]
1584 fn from_stringref_unchecked() {
1585 #[expect(
1586 clippy::std_instead_of_alloc,
1587 reason = "We are testing, this needs std"
1588 )]
1589 let s1: std::string::String = "1234567890ABCDE".into();
1590 let res = TokenString::from_string_unchecked(&s1);
1591 check!(&res.prefix[0 .. 6] == b"123456");
1592 check!(
1593 unsafe { &res.u.ptr.as_slice_manually(res.len as usize)[.. 15] }
1596 == b"1234567890ABCDE"
1597 );
1598 check!(res.len == 15);
1599 check!(res.is_small() == false);
1600 }
1601}