1use core::{
2 alloc::Layout,
3 borrow::{Borrow, BorrowMut},
4 fmt::{Debug, Display},
5 hash::Hash,
6 hint::assert_unchecked,
7 mem::{MaybeUninit, transmute},
8 num::{NonZeroU8, NonZeroUsize},
9 ops::{Deref, DerefMut},
10 ptr::{self, NonNull},
11};
12
13use alloc::alloc::{alloc, dealloc, handle_alloc_error, realloc};
14
15use crate::{
16 discriminant::{DiscriminantValues, NICHE_MAX_INT},
17 likely, unlikely,
18};
19
20#[repr(C)]
21struct HeapRepr(NonZeroUsize);
22
23impl HeapRepr {
24 #[inline(always)]
29 const fn is_valid_len(len: usize) -> bool {
30 len < isize::MAX as usize && NICHE_MAX_INT < len
31 }
32
33 #[inline(always)]
34 pub const fn as_ptr(&self) -> NonNull<NonZeroUsize> {
35 unsafe {
42 NonNull::new_unchecked(
43 core::ptr::with_exposed_provenance::<NonZeroUsize>(self.0.get()) as _,
44 )
45 }
46 }
47
48 #[inline(always)]
49 #[allow(unused)]
50 pub const fn as_str_ptr(&mut self) -> NonNull<u8> {
51 unsafe { self.as_ptr().add(1).cast() }
54 }
55
56 #[inline(always)]
57 pub const fn as_ptr_mut(&mut self) -> NonNull<NonZeroUsize> {
58 unsafe { NonNull::new_unchecked(core::ptr::with_exposed_provenance_mut(self.0.get())) }
59 }
60
61 #[inline(always)]
62 pub const fn as_str_ptr_mut(&mut self) -> NonNull<u8> {
63 unsafe { self.as_ptr_mut().add(1).cast() }
66 }
67
68 #[allow(clippy::len_without_is_empty)]
72 #[inline(always)]
73 pub const fn len(&self) -> NonZeroUsize {
74 unsafe { self.as_ptr().read() }
76 }
77
78 #[inline(always)]
79 pub const fn set_len(&mut self, len: NonZeroUsize) {
80 unsafe { self.as_ptr().write(len) }
82 }
83
84 #[inline(always)]
86 const fn as_bytes(&self) -> &[u8] {
87 let ptr = self.as_ptr();
88 let len = self.len();
89 unsafe { NonNull::slice_from_raw_parts(ptr.add(1).cast::<u8>(), len.get()).as_ref() }
91 }
92
93 #[inline(always)]
95 const unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
96 let ptr = self.as_ptr_mut();
97 let len = self.len();
98 unsafe { NonNull::slice_from_raw_parts(ptr.add(1).cast::<u8>(), len.get()).as_mut() }
99 }
100
101 #[inline(always)]
103 const fn as_str(&self) -> &str {
104 unsafe { str::from_utf8_unchecked(self.as_bytes()) }
107 }
108
109 #[inline(always)]
111 const fn as_str_mut(&mut self) -> &mut str {
112 unsafe { str::from_utf8_unchecked_mut(self.as_bytes_mut()) }
115 }
116
117 #[inline(always)]
119 const fn capacity(&self) -> NonZeroUsize {
120 unsafe {
122 NonZeroUsize::new_unchecked(next_step(
123 self.len().get().unchecked_add(size_of::<usize>()),
124 ))
125 }
126 }
127
128 #[inline(always)]
130 #[allow(unused)]
131 const fn str_capacity(&self) -> NonZeroUsize {
132 unsafe { NonZeroUsize::new_unchecked(next_step(self.len().get())) }
134 }
135}
136
137#[repr(C)]
138struct InlinedRepr {
139 _align: [usize; 0],
140 #[cfg(target_endian = "big")]
141 data: [MaybeUninit<u8>; size_of::<NonZeroUsize>() - 1],
142 len: NonZeroU8,
143 #[cfg(target_endian = "little")]
144 data: [MaybeUninit<u8>; size_of::<NonZeroUsize>() - 1],
145}
146
147impl InlinedRepr {
148 #[inline(always)]
150 const fn as_bytes(&self) -> &[u8] {
151 unsafe {
153 core::slice::from_raw_parts(&raw const self.data as *const u8, self.len.get() as usize)
154 }
155 }
156
157 #[inline(always)]
159 const unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
160 unsafe {
162 core::slice::from_raw_parts_mut(&raw mut self.data as *mut u8, self.len.get() as usize)
163 }
164 }
165
166 #[inline(always)]
168 const fn as_str(&self) -> &str {
169 unsafe { str::from_utf8_unchecked(self.as_bytes()) }
172 }
173
174 #[inline(always)]
176 const fn as_str_mut(&mut self) -> &mut str {
177 unsafe { str::from_utf8_unchecked_mut(self.as_bytes_mut()) }
180 }
181}
182
183#[repr(C)]
184pub struct NonEmptySinStr {
185 _align: [usize; 0], #[cfg(target_endian = "big")]
187 data_or_partial_ptr: [MaybeUninit<u8>; size_of::<NonZeroUsize>() - 1],
189 disc: DiscriminantValues,
194 #[cfg(target_endian = "little")]
195 data_or_partial_ptr: [MaybeUninit<u8>; size_of::<NonZeroUsize>() - 1],
197}
198
199impl Clone for NonEmptySinStr {
200 fn clone(&self) -> Self {
201 if self.is_inlined() {
202 Self {
204 _align: [],
205 disc: self.disc,
206 data_or_partial_ptr: self.data_or_partial_ptr,
207 }
208 } else {
209 unsafe { Self::new_heap(self.as_str()) }
212 }
213 }
214
215 }
217
218impl Debug for NonEmptySinStr {
219 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
220 f.debug_struct("NonEmptySinStr")
221 .field("data_or_partial_ptr", &self.data_or_partial_ptr)
222 .field("disc", &self.disc)
223 .finish()
224 }
225}
226
227impl Display for NonEmptySinStr {
228 #[inline(always)]
229 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
230 <str as Display>::fmt(self.as_str(), f)
231 }
232}
233
234impl Hash for NonEmptySinStr {
235 #[inline(always)]
236 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
237 self.as_str().hash(state);
238 }
239}
240
241impl PartialEq for NonEmptySinStr {
242 #[inline(always)]
243 fn eq(&self, other: &Self) -> bool {
244 self.is_inlined() == other.is_inlined() && self.as_str() == other.as_str()
249 }
250}
251impl Eq for NonEmptySinStr {}
252
253impl PartialOrd for NonEmptySinStr {
254 #[inline]
255 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
256 Some(self.cmp(other))
257 }
258}
259impl Ord for NonEmptySinStr {
260 #[inline]
261 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
262 self.as_str().cmp(other.as_str())
263 }
264}
265
266impl Deref for NonEmptySinStr {
267 type Target = str;
268 #[inline]
269 fn deref(&self) -> &Self::Target {
270 self.as_str()
271 }
272}
273impl DerefMut for NonEmptySinStr {
274 #[inline]
275 fn deref_mut(&mut self) -> &mut Self::Target {
276 self.as_str_mut()
277 }
278}
279
280impl AsRef<str> for NonEmptySinStr {
281 #[inline]
282 fn as_ref(&self) -> &str {
283 self.as_str()
284 }
285}
286
287impl AsRef<[u8]> for NonEmptySinStr {
288 #[inline]
289 fn as_ref(&self) -> &[u8] {
290 self.as_bytes()
291 }
292}
293
294impl Borrow<str> for NonEmptySinStr {
295 #[inline]
296 fn borrow(&self) -> &str {
297 self.as_str()
298 }
299}
300
301impl BorrowMut<str> for NonEmptySinStr {
302 #[inline]
303 fn borrow_mut(&mut self) -> &mut str {
304 self.as_str_mut()
305 }
306}
307
308impl NonEmptySinStr {
309 #[inline(always)]
313 pub fn new(s: &str) -> Option<Self> {
314 let len = s.len();
315 if len == 0 {
316 return None;
317 }
318
319 Some(if likely(NICHE_MAX_INT >= len) {
320 unsafe { Self::new_inline(s) }
322 } else {
323 unsafe { Self::new_heap(s) }
326 })
327 }
328
329 #[inline(always)]
335 pub const fn new_const(s: &str) -> Self {
336 if unlikely(s.is_empty()) {
337 panic!("Cannot construct empty NonEmptySinStr");
338 }
339
340 if unlikely(s.len() > NICHE_MAX_INT) {
341 panic!("Cannot construct string greater than inline capacity at compile time");
342 }
343
344 unsafe { Self::new_inline(s) }
346 }
347
348 #[inline(always)]
355 pub const unsafe fn new_inline(s: &str) -> Self {
356 let len = s.len();
357 debug_assert!(!HeapRepr::is_valid_len(len));
358 unsafe { assert_unchecked(!HeapRepr::is_valid_len(len)) };
359 let mut buf = [MaybeUninit::uninit(); size_of::<NonZeroUsize>() - 1];
360
361 unsafe {
363 ptr::copy_nonoverlapping(s.as_ptr(), buf.as_mut_ptr().cast::<u8>(), len);
364 }
365
366 unsafe {
368 NonEmptySinStr {
369 _align: [],
370 disc: transmute::<u8, DiscriminantValues>(len as u8),
371 data_or_partial_ptr: buf,
372 }
373 }
374 }
375
376 pub unsafe fn new_heap(s: &str) -> Self {
382 let len = s.len();
383 debug_assert!(HeapRepr::is_valid_len(len));
384 unsafe { assert_unchecked(HeapRepr::is_valid_len(len)) };
385 let total_size = size_of::<usize>() + len;
386 if unlikely(total_size > isize::MAX as usize) {
387 panic!("NonEmptySinStr::new_heap should never exceed max size");
388 }
389 let alloc_size = next_step(total_size);
390 let layout = unsafe { Layout::from_size_align_unchecked(alloc_size, align_of::<usize>()) };
392
393 let Some(ptr) = NonNull::new(unsafe { alloc(layout) }) else {
395 handle_alloc_error(layout)
396 };
397
398 unsafe {
400 ptr.cast::<usize>().write(len);
401 ptr.add(size_of::<usize>())
402 .copy_from_nonoverlapping(NonNull::new_unchecked(s.as_ptr() as *mut u8), len);
403 transmute::<usize, NonEmptySinStr>(ptr.expose_provenance().get())
405 }
406 }
407
408 #[inline(always)]
409 pub const fn is_inlined(&self) -> bool {
410 let len = self.disc as usize;
418 likely((len.wrapping_sub(1)) < NICHE_MAX_INT)
420 }
421
422 #[inline(always)]
423 pub const fn is_heap(&self) -> bool {
424 unlikely(!self.is_inlined())
425 }
426
427 #[inline(always)]
428 pub fn len(&self) -> NonZeroUsize {
429 if self.is_inlined() {
430 unsafe { NonZeroUsize::new_unchecked(self.disc as usize) }
433 } else {
434 unsafe { self.get_heap() }.len()
436 }
437 }
438
439 #[inline(always)]
445 const unsafe fn get_heap(&self) -> &HeapRepr {
446 const _: () = assert!(size_of::<NonEmptySinStr>() == size_of::<usize>());
447 const _: () = assert!(align_of::<NonEmptySinStr>() == align_of::<usize>());
448 unsafe {
453 (self as *const NonEmptySinStr as *const HeapRepr)
454 .as_ref()
455 .unwrap_unchecked()
456 }
457 }
458
459 #[inline(always)]
465 const unsafe fn get_heap_mut(&mut self) -> &mut HeapRepr {
466 const _: () = assert!(size_of::<NonEmptySinStr>() == size_of::<usize>());
467 unsafe {
472 (self as *mut NonEmptySinStr as *mut HeapRepr)
473 .as_mut()
474 .unwrap_unchecked()
475 }
476 }
477
478 #[inline(always)]
484 const unsafe fn get_inlined(&self) -> &InlinedRepr {
485 unsafe {
490 (self as *const NonEmptySinStr as *const InlinedRepr)
491 .as_ref()
492 .unwrap_unchecked()
493 }
494 }
495
496 #[inline(always)]
502 const unsafe fn get_inlined_mut(&mut self) -> &mut InlinedRepr {
503 unsafe {
508 (self as *mut NonEmptySinStr as *mut InlinedRepr)
509 .as_mut()
510 .unwrap_unchecked()
511 }
512 }
513
514 #[inline(always)]
516 pub const fn as_bytes(&self) -> &[u8] {
517 if likely(self.is_inlined()) {
519 unsafe { self.get_inlined() }.as_bytes()
520 } else {
521 unsafe { self.get_heap() }.as_bytes()
522 }
523 }
524
525 #[inline(always)]
531 pub const unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
532 if likely(self.is_inlined()) {
533 unsafe { self.get_inlined_mut().as_bytes_mut() }
535 } else {
536 unsafe { self.get_heap_mut().as_bytes_mut() }
538 }
539 }
540
541 #[inline(always)]
543 pub const fn as_str(&self) -> &str {
544 if likely(self.is_inlined()) {
546 unsafe { self.get_inlined() }.as_str()
547 } else {
548 unsafe { self.get_heap() }.as_str()
550 }
551 }
552
553 #[inline(always)]
555 pub const fn as_str_mut(&mut self) -> &mut str {
556 if likely(self.is_inlined()) {
558 unsafe { self.get_inlined_mut() }.as_str_mut()
559 } else {
560 unsafe { self.get_heap_mut() }.as_str_mut()
562 }
563 }
564
565 #[inline(always)]
572 pub fn set_str(&mut self, s: &str) {
573 if unlikely(s.is_empty()) {
574 panic!("NonEmptySinStr::set_str recieved empty string");
575 }
576
577 unsafe { self.set_str_unchecked(s) };
579 }
580
581 #[inline(always)]
595 pub unsafe fn set_str_unchecked(&mut self, s: &str) {
596 debug_assert!(!s.is_empty());
597 unsafe { assert_unchecked(!s.is_empty()) };
598 if likely(s.len() <= NICHE_MAX_INT) {
599 *self = unsafe { Self::new_inline(s) };
601 } else if self.is_heap() {
602 let hp = unsafe { self.get_heap_mut() };
604 let next = next_step(size_of::<usize>() + s.len());
605 let capacity = hp.capacity();
606 if capacity.get() == next {
607 unsafe {
608 hp.set_len(NonZeroUsize::new_unchecked(s.len()));
609 hp.as_str_ptr_mut()
610 .as_ptr()
611 .copy_from_nonoverlapping(s.as_ptr(), s.len());
612 };
613 } else {
614 let new_size = next_step(size_of::<usize>() + s.len());
615 debug_assert!(new_size <= isize::MAX as usize);
616 unsafe { assert_unchecked(new_size <= isize::MAX as usize) };
618
619 let layout = unsafe {
623 Layout::from_size_align_unchecked(hp.capacity().get(), align_of::<usize>())
624 };
625 let Some(ptr) = NonNull::new(
626 unsafe { realloc(hp.as_ptr_mut().as_ptr() as *mut u8, layout, new_size) },
631 ) else {
632 handle_alloc_error(layout);
633 };
634
635 hp.0 = ptr.expose_provenance();
636 unsafe {
641 hp.as_str_ptr_mut()
642 .as_ptr()
643 .copy_from_nonoverlapping(s.as_ptr(), s.len());
644 hp.set_len(NonZeroUsize::new_unchecked(s.len()));
645 };
646 }
647 } else {
648 *self = unsafe { Self::new_heap(s) };
653 }
654 }
655}
656
657impl Drop for NonEmptySinStr {
658 #[inline]
659 fn drop(&mut self) {
660 if unlikely(self.is_heap()) {
661 unsafe { self.drop_heap() };
663 }
664 }
665}
666
667impl NonEmptySinStr {
668 #[cold]
674 #[inline(always)]
675 unsafe fn drop_heap(&mut self) {
676 unsafe {
680 let heap = self.get_heap_mut();
681 let ptr = heap.as_ptr_mut();
682 let layout =
683 Layout::from_size_align_unchecked(heap.capacity().get(), align_of::<usize>());
684 dealloc(ptr.cast::<u8>().as_ptr(), layout)
685 }
686 }
687}
688
689const _: () = assert!(size_of::<NonEmptySinStr>() == size_of::<usize>());
691const _: () = assert!(size_of::<Option<NonEmptySinStr>>() == size_of::<usize>());
692const _: () = assert!(size_of::<Option<NonEmptySinStr>>() >= align_of::<usize>());
693
694const LEN_CAP_STEP: usize = align_of::<usize>();
695
696#[inline(always)]
697#[track_caller]
698const fn next_step(len: usize) -> usize {
699 const _: () = assert!(LEN_CAP_STEP.count_ones() == 1);
701 let n = if NICHE_MAX_INT <= len {
702 len
703 } else {
704 const { NICHE_MAX_INT }
705 };
706
707 (n + LEN_CAP_STEP - 1) & !(LEN_CAP_STEP - 1)
708}
709
710#[cfg(test)]
711mod tests {
712 use super::*;
713 use crate::discriminant::NICHE_MAX_INT;
714 use alloc::string::String;
715
716 fn max_inline_string() -> String {
717 "x".repeat(NICHE_MAX_INT)
718 }
719
720 fn first_heap_string() -> String {
721 "y".repeat(NICHE_MAX_INT + 1)
722 }
723
724 mod constructor {
725 use super::*;
726
727 #[test]
728 fn test_new_returns_none_for_empty() {
729 assert!(NonEmptySinStr::new("").is_none());
730 }
731
732 #[test]
733 fn test_new_inline_string() {
734 for len in 1..=NICHE_MAX_INT {
735 let s = "a".repeat(len);
736 let nes = NonEmptySinStr::new(&s).expect("should create");
737 assert!(nes.is_inlined());
738 assert!(!nes.is_heap());
739 assert_eq!(nes.len().get(), len);
740 assert_eq!(nes.as_str(), s);
741 }
742 }
743
744 #[test]
745 fn test_new_heap_string() {
746 let s = first_heap_string();
747 let nes = NonEmptySinStr::new(&s).expect("should create");
748 assert!(!nes.is_inlined());
749 assert!(nes.is_heap());
750 assert_eq!(nes.len().get(), NICHE_MAX_INT + 1);
751 assert_eq!(nes.as_str(), s);
752 }
753
754 #[test]
755 fn test_new_inline_boundary() {
756 let max_inline = max_inline_string();
757 let nes = NonEmptySinStr::new(&max_inline).expect("should create");
758 assert!(nes.is_inlined());
759
760 let first_heap = first_heap_string();
761 let nes = NonEmptySinStr::new(&first_heap).expect("should create");
762 assert!(nes.is_heap());
763 }
764 }
765
766 mod unsafe_constructors {
767 use super::*;
768
769 #[test]
770 fn test_new_inline_valid() {
771 for len in 1..=NICHE_MAX_INT {
772 let s = "x".repeat(len);
773 let nes = unsafe { NonEmptySinStr::new_inline(&s) };
774 assert!(nes.is_inlined());
775 assert_eq!(nes.as_str(), s);
776 }
777 }
778
779 #[test]
780 fn test_new_heap_valid() {
781 let s = "a".repeat(NICHE_MAX_INT + 5);
782 let nes = unsafe { NonEmptySinStr::new_heap(&s) };
783 assert!(nes.is_heap());
784 assert_eq!(nes.as_str(), s);
785 }
786 }
787
788 mod storage_mode {
789 use super::*;
790
791 #[test]
792 fn test_is_heap_inline_string() {
793 let s = "a".repeat(NICHE_MAX_INT);
794 let nes = NonEmptySinStr::new(&s).expect("should create");
795 assert!(!nes.is_heap());
796 }
797
798 #[test]
799 fn test_is_heap_heap_string() {
800 let s = first_heap_string();
801 let nes = NonEmptySinStr::new(&s).expect("should create");
802 assert!(nes.is_heap());
803 }
804
805 #[test]
806 fn test_const_is_inlined() {
807 const S: NonEmptySinStr = unsafe { NonEmptySinStr::new_inline("a") };
808 assert_eq!(S.as_str(), "a");
809 assert!(S.is_inlined());
810 }
811 }
812
813 mod content_access {
814 use super::*;
815
816 #[test]
817 fn test_len_inline() {
818 for len in 1..=NICHE_MAX_INT.min(5) {
819 let s = "x".repeat(len);
820 let nes = NonEmptySinStr::new(&s).expect("should create");
821 assert_eq!(nes.len().get(), len);
822 }
823 }
824
825 #[test]
826 fn test_len_heap() {
827 let s = "x".repeat(100);
828 let nes = NonEmptySinStr::new(&s).expect("should create");
829 assert_eq!(nes.len().get(), 100);
830 }
831
832 #[test]
833 fn test_as_str_inline() {
834 let s = "hello";
835 if NICHE_MAX_INT >= 5 {
836 let nes = NonEmptySinStr::new(s).expect("should create");
837 assert_eq!(nes.as_str(), s);
838 }
839 }
840
841 #[test]
842 fn test_as_str_heap() {
843 let s = "hello world, this is a long string";
844 let nes = NonEmptySinStr::new(s).expect("should create");
845 assert_eq!(nes.as_str(), s);
846 }
847
848 #[test]
849 fn test_as_bytes_inline() {
850 let s = "abc";
851 if NICHE_MAX_INT >= 3 {
852 let nes = NonEmptySinStr::new(s).expect("should create");
853 assert_eq!(nes.as_bytes(), s.as_bytes());
854 }
855 }
856
857 #[test]
858 fn test_as_bytes_heap() {
859 let s = "longer string on the heap";
860 let nes = NonEmptySinStr::new(s).expect("should create");
861 assert_eq!(nes.as_bytes(), s.as_bytes());
862 }
863
864 #[test]
865 fn test_as_str_mut_inline() {
866 if NICHE_MAX_INT >= 3 {
867 let mut nes = NonEmptySinStr::new("abc").expect("should create");
868 let s_mut = nes.as_str_mut();
869 assert_eq!(s_mut, "abc");
870 }
871 }
872
873 #[test]
874 fn test_as_str_mut_heap() {
875 let original = "hello world";
876 let mut nes = NonEmptySinStr::new(original).expect("should create");
877 let s_mut = nes.as_str_mut();
878 assert_eq!(s_mut, original);
879 }
880
881 #[test]
882 fn test_as_bytes_mut_inline() {
883 if NICHE_MAX_INT >= 3 {
884 let mut nes = NonEmptySinStr::new("abc").expect("should create");
885 let bytes = unsafe { nes.as_bytes_mut() };
886 assert_eq!(bytes, b"abc");
887 }
888 }
889
890 #[test]
891 fn test_as_bytes_mut_heap() {
892 let original = "hello world";
893 let mut nes = NonEmptySinStr::new(original).expect("should create");
894 let bytes = unsafe { nes.as_bytes_mut() };
895 assert_eq!(bytes, original.as_bytes());
896 }
897 }
898
899 mod unsafe_accessors {
900 use super::*;
901
902 #[test]
903 fn test_get_inlined() {
904 let s = "ab";
905 if NICHE_MAX_INT >= 2 {
906 let nes = NonEmptySinStr::new(s).expect("should create");
907 let inlined = unsafe { nes.get_inlined() };
908 assert_eq!(inlined.as_str(), s);
909 }
910 }
911
912 #[test]
913 fn test_get_inlined_mut() {
914 let s = "xy";
915 if NICHE_MAX_INT >= 2 {
916 let mut nes = NonEmptySinStr::new(s).expect("should create");
917 let inlined = unsafe { nes.get_inlined_mut() };
918 assert_eq!(inlined.as_str(), s);
919 }
920 }
921
922 #[test]
923 fn test_get_heap() {
924 let s = first_heap_string();
925 let nes = NonEmptySinStr::new(&s).expect("should create");
926 let heap = unsafe { nes.get_heap() };
927 assert_eq!(heap.as_str(), &s);
928 }
929
930 #[test]
931 fn test_get_heap_mut() {
932 let s = first_heap_string();
933 let mut nes = NonEmptySinStr::new(&s).expect("should create");
934 let heap = unsafe { nes.get_heap_mut() };
935 assert_eq!(heap.as_str(), &s);
936 }
937
938 #[test]
939 fn test_heap_repr_len() {
940 let len = NICHE_MAX_INT + 10;
941 let s = "x".repeat(len);
942 let nes = NonEmptySinStr::new(&s).expect("should create");
943 let heap = unsafe { nes.get_heap() };
944 assert_eq!(heap.len().get(), len);
945 }
946 }
947
948 mod edge_cases {
949 use super::*;
950
951 #[test]
952 fn test_unicode_inline() {
953 let unicode_chars = [("Γ©", 2), ("ζ₯", 3), ("π¦", 4)];
954 for (c, byte_len) in unicode_chars {
955 if NICHE_MAX_INT >= byte_len {
956 let nes = NonEmptySinStr::new(c).expect("should create");
957 assert!(nes.is_inlined());
958 assert_eq!(nes.as_str(), c);
959 }
960 }
961 }
962
963 #[test]
964 fn test_unicode_heap() {
965 let s = "ζ₯ζ¬θͺγγΉγ";
966 let nes = NonEmptySinStr::new(s).expect("should create");
967 assert_eq!(nes.as_str(), s);
968 assert!(nes.is_heap());
969 }
970
971 #[test]
972 fn test_unicode_max_inline() {
973 if NICHE_MAX_INT >= 4 {
974 let s = "π¦".repeat(NICHE_MAX_INT / 4);
975 let nes = NonEmptySinStr::new(&s).expect("should create");
976 assert!(nes.is_inlined());
977 assert_eq!(nes.as_str(), s);
978 }
979 }
980
981 #[test]
982 fn test_very_long_heap_string() {
983 let s = "x".repeat(10000);
984 let nes = NonEmptySinStr::new(&s).expect("should create");
985 assert!(nes.is_heap());
986 assert_eq!(nes.len().get(), 10000);
987 assert_eq!(nes.as_str(), s);
988 }
989 }
990
991 mod trait_impls {
992 use super::*;
993 use alloc::borrow::Borrow;
994 use core::hash::{Hash, Hasher};
995
996 mod clone_tests {
997 use super::*;
998
999 #[test]
1000 fn test_clone_inline() {
1001 let s = "abc";
1002 if NICHE_MAX_INT >= 3 {
1003 let nes = NonEmptySinStr::new(s).expect("should create");
1004 let cloned = nes.clone();
1005 assert_eq!(cloned.as_str(), s);
1006 assert!(cloned.is_inlined());
1007 }
1008 }
1009
1010 #[test]
1011 fn test_clone_heap() {
1012 let s = first_heap_string();
1013 let nes = NonEmptySinStr::new(&s).expect("should create");
1014 let cloned = nes.clone();
1015 assert_eq!(cloned.as_str(), &s);
1016 assert!(cloned.is_heap());
1017 }
1018
1019 #[test]
1020 fn test_clone_max_inline() {
1021 let s = max_inline_string();
1022 let nes = NonEmptySinStr::new(&s).expect("should create");
1023 let cloned = nes.clone();
1024 assert_eq!(cloned.as_str(), &s);
1025 assert!(cloned.is_inlined());
1026 }
1027
1028 #[test]
1029 fn test_clone_preserves_content() {
1030 let original = "hello";
1031 if NICHE_MAX_INT >= 5 {
1032 let nes = NonEmptySinStr::new(original).expect("should create");
1033 let cloned = nes.clone();
1034 assert_eq!(original, cloned.as_str());
1035 }
1036 }
1037 }
1038
1039 mod display_tests {
1040 use super::*;
1041
1042 #[test]
1043 fn test_display_inline() {
1044 let s = "hello";
1045 if NICHE_MAX_INT >= 5 {
1046 let nes = NonEmptySinStr::new(s).expect("should create");
1047 let displayed = alloc::format!("{}", nes);
1048 assert_eq!(displayed, s);
1049 }
1050 }
1051
1052 #[test]
1053 fn test_display_heap() {
1054 let s = first_heap_string();
1055 let nes = NonEmptySinStr::new(&s).expect("should create");
1056 let displayed = alloc::format!("{}", nes);
1057 assert_eq!(displayed, s);
1058 }
1059
1060 #[test]
1061 fn test_display_unicode() {
1062 let s = "ζ₯ζ¬θͺ";
1063 let nes = NonEmptySinStr::new(s).expect("should create");
1064 let displayed = alloc::format!("{}", nes);
1065 assert_eq!(displayed, s);
1066 }
1067 }
1068
1069 mod hash_tests {
1070 use super::*;
1071 extern crate std;
1072 use std::hash::DefaultHasher;
1073
1074 fn calculate_hash<T: Hash>(value: &T) -> u64 {
1075 let mut hasher = DefaultHasher::new();
1076 value.hash(&mut hasher);
1077 hasher.finish()
1078 }
1079
1080 #[test]
1081 fn test_hash_consistency() {
1082 let s = "abc";
1083 if NICHE_MAX_INT >= 3 {
1084 let nes1 = NonEmptySinStr::new(s).expect("should create");
1085 let nes2 = NonEmptySinStr::new(s).expect("should create");
1086 assert_eq!(calculate_hash(&nes1), calculate_hash(&nes2));
1087 }
1088 }
1089
1090 #[test]
1091 fn test_hash_different() {
1092 if NICHE_MAX_INT >= 3 {
1093 let nes1 = NonEmptySinStr::new("abc").expect("should create");
1094 let nes2 = NonEmptySinStr::new("xyz").expect("should create");
1095 assert_ne!(calculate_hash(&nes1), calculate_hash(&nes2));
1096 }
1097 }
1098
1099 #[test]
1100 fn test_hash_inline_vs_heap_different() {
1101 let short = "ab";
1102 let long_suffix = "x".repeat(NICHE_MAX_INT);
1103 let content = alloc::format!("{}{}", short, long_suffix);
1104
1105 let nes_heap = NonEmptySinStr::new(&content).expect("should create");
1106 let str_ref: &str = nes_heap.as_str();
1107 let nes_inline =
1108 NonEmptySinStr::new(str_ref.get(0..2).unwrap()).expect("should create");
1109
1110 assert_ne!(
1111 calculate_hash(&nes_heap),
1112 calculate_hash(&nes_inline),
1113 "Different content should have different hashes"
1114 );
1115 }
1116 }
1117
1118 mod eq_tests {
1119 use super::*;
1120
1121 #[test]
1122 fn test_eq_same_content_inline() {
1123 let s = "test";
1124 if NICHE_MAX_INT >= 4 {
1125 let a = NonEmptySinStr::new(s).expect("should create");
1126 let b = NonEmptySinStr::new(s).expect("should create");
1127 assert_eq!(a, b);
1128 }
1129 }
1130
1131 #[test]
1132 fn test_eq_same_content_heap() {
1133 let s = first_heap_string();
1134 let a = NonEmptySinStr::new(&s).expect("should create");
1135 let b = NonEmptySinStr::new(&s).expect("should create");
1136 assert_eq!(a, b);
1137 }
1138
1139 #[test]
1140 fn test_ne_different_content() {
1141 if NICHE_MAX_INT >= 3 {
1142 let a = NonEmptySinStr::new("abc").expect("should create");
1143 let b = NonEmptySinStr::new("xyz").expect("should create");
1144 assert_ne!(a, b);
1145 }
1146 }
1147
1148 #[test]
1149 fn test_eq_inline_and_heap_same_storage_mode_check() {
1150 let s1 = "a".repeat(NICHE_MAX_INT);
1151 let s2 = s1.clone();
1152 let a = NonEmptySinStr::new(&s1).expect("should create");
1153 let b = NonEmptySinStr::new(&s2).expect("should create");
1154 assert!(a.is_inlined());
1155 assert!(b.is_inlined());
1156 assert_eq!(a, b);
1157 }
1158 }
1159
1160 mod ord_tests {
1161 use super::*;
1162 use core::cmp::Ordering;
1163
1164 #[test]
1165 fn test_ord_less() {
1166 if NICHE_MAX_INT >= 2 {
1167 let a = NonEmptySinStr::new("ab").expect("should create");
1168 let b = NonEmptySinStr::new("cd").expect("should create");
1169 assert_eq!(a.cmp(&b), Ordering::Less);
1170 assert!(a < b);
1171 }
1172 }
1173
1174 #[test]
1175 fn test_ord_equal() {
1176 let s = "test";
1177 if NICHE_MAX_INT >= 4 {
1178 let a = NonEmptySinStr::new(s).expect("should create");
1179 let b = NonEmptySinStr::new(s).expect("should create");
1180 assert_eq!(a.cmp(&b), Ordering::Equal);
1181 assert!(a <= b);
1182 assert!(a >= b);
1183 }
1184 }
1185
1186 #[test]
1187 fn test_ord_greater() {
1188 if NICHE_MAX_INT >= 2 {
1189 let a = NonEmptySinStr::new("yz").expect("should create");
1190 let b = NonEmptySinStr::new("ab").expect("should create");
1191 assert_eq!(a.cmp(&b), Ordering::Greater);
1192 assert!(a > b);
1193 }
1194 }
1195
1196 #[test]
1197 fn test_ord_cross_storage() {
1198 let short = "ab";
1199 let long = "cd";
1200 if NICHE_MAX_INT >= 2 && long.len() <= NICHE_MAX_INT {
1201 let a = NonEmptySinStr::new(short).expect("should create");
1202 let b = NonEmptySinStr::new(long).expect("should create");
1203 assert!(a < b);
1204 }
1205 }
1206 }
1207
1208 mod deref_tests {
1209 use super::*;
1210
1211 #[test]
1212 fn test_deref_inline() {
1213 let s = "hello";
1214 if NICHE_MAX_INT >= 5 {
1215 let nes = NonEmptySinStr::new(s).expect("should create");
1216 assert_eq!(&*nes, s);
1217 }
1218 }
1219
1220 #[test]
1221 fn test_deref_heap() {
1222 let s = first_heap_string();
1223 let nes = NonEmptySinStr::new(&s).expect("should create");
1224 assert_eq!(&*nes, &s);
1225 }
1226
1227 #[test]
1228 fn test_deref_methods() {
1229 let s = "test";
1230 if NICHE_MAX_INT >= 4 {
1231 let nes = NonEmptySinStr::new(s).expect("should create");
1232 assert_eq!(nes.len().get(), s.len());
1233 assert!(nes.starts_with("te"));
1234 assert!(nes.ends_with("st"));
1235 }
1236 }
1237
1238 #[test]
1239 fn test_deref_mut_inline() {
1240 if NICHE_MAX_INT >= 3 {
1241 let nes = NonEmptySinStr::new("abc").expect("should create");
1242 assert_eq!(&*nes, "abc");
1243 }
1244 }
1245
1246 #[test]
1247 fn test_deref_mut_heap() {
1248 let s = first_heap_string();
1249 let nes = NonEmptySinStr::new(&s).expect("should create");
1250 assert_eq!(&*nes, &s);
1251 }
1252 }
1253
1254 mod as_ref_tests {
1255 use super::*;
1256
1257 #[test]
1258 fn test_as_ref_str_inline() {
1259 let s = "test";
1260 if NICHE_MAX_INT >= 4 {
1261 let nes = NonEmptySinStr::new(s).expect("should create");
1262 let as_str: &str = nes.as_ref();
1263 assert_eq!(as_str, s);
1264 }
1265 }
1266
1267 #[test]
1268 fn test_as_ref_str_heap() {
1269 let s = first_heap_string();
1270 let nes = NonEmptySinStr::new(&s).expect("should create");
1271 let as_str: &str = nes.as_ref();
1272 assert_eq!(as_str, &s);
1273 }
1274
1275 #[test]
1276 fn test_as_ref_bytes_inline() {
1277 let s = "abc";
1278 if NICHE_MAX_INT >= 3 {
1279 let nes = NonEmptySinStr::new(s).expect("should create");
1280 let as_bytes: &[u8] = nes.as_ref();
1281 assert_eq!(as_bytes, s.as_bytes());
1282 }
1283 }
1284
1285 #[test]
1286 fn test_as_ref_bytes_heap() {
1287 let s = first_heap_string();
1288 let nes = NonEmptySinStr::new(&s).expect("should create");
1289 let as_bytes: &[u8] = nes.as_ref();
1290 assert_eq!(as_bytes, s.as_bytes());
1291 }
1292 }
1293
1294 mod borrow_tests {
1295 use super::*;
1296
1297 #[test]
1298 fn test_borrow_str_inline() {
1299 let s = "test";
1300 if NICHE_MAX_INT >= 4 {
1301 let nes = NonEmptySinStr::new(s).expect("should create");
1302 let borrowed: &str = nes.borrow();
1303 assert_eq!(borrowed, s);
1304 }
1305 }
1306
1307 #[test]
1308 fn test_borrow_str_heap() {
1309 let s = first_heap_string();
1310 let nes = NonEmptySinStr::new(&s).expect("should create");
1311 let borrowed: &str = nes.borrow();
1312 assert_eq!(borrowed, &s);
1313 }
1314
1315 #[test]
1316 fn test_borrow_mut_str_inline() {
1317 let s = "abc";
1318 if NICHE_MAX_INT >= 3 {
1319 let mut nes = NonEmptySinStr::new(s).expect("should create");
1320 let borrowed: &mut str = nes.borrow_mut();
1321 assert_eq!(borrowed, s);
1322 }
1323 }
1324
1325 #[test]
1326 fn test_borrow_mut_str_heap() {
1327 let s = first_heap_string();
1328 let mut nes = NonEmptySinStr::new(&s).expect("should create");
1329 let borrowed: &mut str = nes.borrow_mut();
1330 assert_eq!(borrowed, &s);
1331 }
1332 }
1333 }
1334
1335 mod len_capacity {
1336 use crate::non_empty::{LEN_CAP_STEP, next_step};
1337
1338 #[test]
1339 fn forces_next_step() {
1340 assert_eq!(next_step(0), LEN_CAP_STEP);
1342 assert_eq!(next_step(1), LEN_CAP_STEP);
1343 assert_eq!(next_step(2), LEN_CAP_STEP);
1344 assert_eq!(next_step(3), LEN_CAP_STEP);
1345 assert_eq!(next_step(4), LEN_CAP_STEP);
1346 assert_eq!(next_step(5), LEN_CAP_STEP);
1347 assert_eq!(next_step(6), LEN_CAP_STEP);
1348 assert_eq!(next_step(7), LEN_CAP_STEP);
1349 assert_eq!(next_step(8), LEN_CAP_STEP);
1350
1351 assert_eq!(next_step(9), LEN_CAP_STEP * 2);
1353 assert_eq!(next_step(10), LEN_CAP_STEP * 2);
1354 assert_eq!(next_step(11), LEN_CAP_STEP * 2);
1355 assert_eq!(next_step(12), LEN_CAP_STEP * 2);
1356 assert_eq!(next_step(13), LEN_CAP_STEP * 2);
1357 assert_eq!(next_step(14), LEN_CAP_STEP * 2);
1358 assert_eq!(next_step(15), LEN_CAP_STEP * 2);
1359 assert_eq!(next_step(16), LEN_CAP_STEP * 2);
1360
1361 assert_eq!(next_step(17), LEN_CAP_STEP * 3);
1363 assert_eq!(next_step(18), LEN_CAP_STEP * 3);
1364 assert_eq!(next_step(19), LEN_CAP_STEP * 3);
1365 assert_eq!(next_step(20), LEN_CAP_STEP * 3);
1366 assert_eq!(next_step(21), LEN_CAP_STEP * 3);
1367 assert_eq!(next_step(22), LEN_CAP_STEP * 3);
1368 assert_eq!(next_step(23), LEN_CAP_STEP * 3);
1369 assert_eq!(next_step(24), LEN_CAP_STEP * 3);
1370 }
1371 }
1372
1373 mod set_str {
1374 use super::*;
1375
1376 #[test]
1377 fn test_set_str_inline_to_inline() {
1378 let mut nes = NonEmptySinStr::new("abc").expect("should create");
1379 nes.set_str("xyz");
1380 assert!(nes.is_inlined());
1381 assert_eq!(nes.as_str(), "xyz");
1382 }
1383
1384 #[test]
1385 fn test_set_str_inline_to_heap() {
1386 let mut nes = NonEmptySinStr::new("abc").expect("should create");
1387 let heap_str = "x".repeat(NICHE_MAX_INT + 10);
1388 nes.set_str(&heap_str);
1389 assert!(nes.is_heap());
1390 assert_eq!(nes.as_str(), heap_str);
1391 }
1392
1393 #[test]
1394 fn test_set_str_heap_to_inline() {
1395 let heap_str = "y".repeat(NICHE_MAX_INT + 10);
1396 let mut nes = NonEmptySinStr::new(&heap_str).expect("should create");
1397 nes.set_str("abc");
1398 assert!(nes.is_inlined());
1399 assert_eq!(nes.as_str(), "abc");
1400 }
1401
1402 #[test]
1403 fn test_set_str_heap_to_heap_smaller() {
1404 let heap_str = "x".repeat(NICHE_MAX_INT + 100);
1405 let mut nes = NonEmptySinStr::new(&heap_str).expect("should create");
1406 let smaller_heap = "y".repeat(NICHE_MAX_INT + 10);
1407 nes.set_str(&smaller_heap);
1408 assert!(nes.is_heap());
1409 assert_eq!(nes.as_str(), smaller_heap);
1410 }
1411
1412 #[test]
1413 fn test_set_str_heap_to_heap_larger() {
1414 let heap_str = "x".repeat(NICHE_MAX_INT + 10);
1415 let mut nes = NonEmptySinStr::new(&heap_str).expect("should create");
1416 let larger_heap = "z".repeat(NICHE_MAX_INT + 100);
1417 nes.set_str(&larger_heap);
1418 assert!(nes.is_heap());
1419 assert_eq!(nes.as_str(), larger_heap);
1420 }
1421
1422 #[test]
1423 fn test_set_str_preserves_content_max_inline() {
1424 let max_inline = "a".repeat(NICHE_MAX_INT);
1425 let mut nes = NonEmptySinStr::new(&max_inline).expect("should create");
1426 nes.set_str(&max_inline);
1427 assert!(nes.is_inlined());
1428 assert_eq!(nes.as_str(), max_inline);
1429 }
1430
1431 #[test]
1432 fn test_set_str_unicode() {
1433 let mut nes = NonEmptySinStr::new("ζ₯ζ¬θͺ").expect("should create");
1434 nes.set_str("δΈζζ΅θ―");
1435 assert_eq!(nes.as_str(), "δΈζζ΅θ―");
1436
1437 let unicode_heap = "π¦".repeat(NICHE_MAX_INT / 4 + 10);
1438 nes.set_str(&unicode_heap);
1439 assert!(nes.is_heap());
1440 assert_eq!(nes.as_str(), unicode_heap);
1441 }
1442
1443 #[test]
1444 #[should_panic(expected = "NonEmptySinStr::set_str recieved empty string")]
1445 fn test_set_str_panic_on_empty() {
1446 let mut nes = NonEmptySinStr::new("abc").expect("should create");
1447 nes.set_str("");
1448 }
1449 }
1450}