Skip to main content

waterui_str/
lib.rs

1#![doc = include_str!("../README.md")]
2extern crate alloc;
3
4mod impls;
5mod shared;
6use alloc::{
7    borrow::Cow,
8    boxed::Box,
9    string::{FromUtf8Error, String, ToString},
10    vec::Vec,
11};
12use shared::Shared;
13
14use core::{
15    borrow::Borrow,
16    mem::{ManuallyDrop, take},
17    ops::Deref,
18    ptr::NonNull,
19    slice,
20};
21
22nami_core::impl_constant!(Str);
23
24/// A string type that can be either a static reference or a ref-counted owned string.
25///
26/// `Str` combines the benefits of both `&'static str` and `String` with efficient
27/// cloning and passing, automatically using the most appropriate representation
28/// based on the source.
29pub struct Str {
30    /// Pointer to the string data.
31    ptr: NonNull<()>,
32
33    /// Length of the string in bytes.
34    /// If len >= 0, it points to a static string.
35    /// otherwise, it points to a Shared structure containing a reference-counted String,
36    len: isize,
37}
38
39impl Drop for Str {
40    /// Decrements the reference count for owned strings and frees the memory
41    /// when the reference count reaches zero.
42    ///
43    /// For static strings, this is a no-op.
44    fn drop(&mut self) {
45        if let Ok(shared) = self.as_shared() {
46            // SAFETY: `as_shared` returning `Ok` proves `len < 0`, so `ptr` is the
47            // leaked `Shared` box this `Str` holds a count on. `Drop` runs once, so the
48            // count is released once and the box is reclaimed only by the last owner.
49            unsafe {
50                if shared.is_unique() {
51                    let ptr = self.ptr.cast::<Shared>().as_ptr();
52                    let _ = Box::from_raw(ptr);
53                } else {
54                    shared.decrement_count();
55                }
56            }
57        }
58    }
59}
60
61impl Clone for Str {
62    /// Creates a clone of the string.
63    ///
64    /// For static strings, this is a simple pointer copy.
65    /// For owned strings, this increments the reference count.
66    fn clone(&self) -> Self {
67        if let Ok(shared) = self.as_shared() {
68            // SAFETY: `as_shared` returning `Ok` proves this is a shared string, so
69            // there is a live `Shared` to count; the clone below takes that new count.
70            unsafe {
71                shared.increment_count();
72            }
73        }
74
75        Self {
76            ptr: self.ptr,
77            len: self.len,
78        }
79    }
80}
81
82impl Deref for Str {
83    type Target = str;
84
85    /// Provides access to the underlying string slice.
86    fn deref(&self) -> &Self::Target {
87        self.as_str()
88    }
89}
90
91impl Borrow<str> for Str {
92    /// Allows borrowing a `Str` as a string slice.
93    fn borrow(&self) -> &str {
94        self.as_str()
95    }
96}
97
98impl AsRef<str> for Str {
99    /// Converts `Str` to a string slice reference.
100    fn as_ref(&self) -> &str {
101        self.as_str()
102    }
103}
104
105impl AsRef<[u8]> for Str {
106    /// Converts `Str` to a byte slice reference.
107    fn as_ref(&self) -> &[u8] {
108        self.as_bytes()
109    }
110}
111
112impl Default for Str {
113    /// Creates a new empty `Str`.
114    fn default() -> Self {
115        Self::new()
116    }
117}
118
119impl From<Cow<'static, str>> for Str {
120    /// Creates a `Str` from a `Cow<'static, str>`.
121    ///
122    /// This will borrow from static strings and own dynamic strings.
123    fn from(value: Cow<'static, str>) -> Self {
124        match value {
125            Cow::Borrowed(s) => s.into(),
126            Cow::Owned(s) => s.into(),
127        }
128    }
129}
130
131/// Implementations available when the `std` library is available.
132mod std_on {
133    use alloc::{string::FromUtf8Error, vec::IntoIter};
134
135    use crate::Str;
136
137    extern crate std;
138
139    use core::{net::SocketAddr, ops::Deref};
140    use std::{
141        ffi::{OsStr, OsString},
142        io,
143        net::ToSocketAddrs,
144        path::Path,
145    };
146
147    impl AsRef<OsStr> for Str {
148        /// Converts `Str` to an OS string slice reference.
149        fn as_ref(&self) -> &OsStr {
150            self.deref().as_ref()
151        }
152    }
153
154    impl AsRef<Path> for Str {
155        /// Converts `Str` to a path reference.
156        fn as_ref(&self) -> &Path {
157            self.deref().as_ref()
158        }
159    }
160
161    impl TryFrom<OsString> for Str {
162        type Error = FromUtf8Error;
163
164        /// Attempts to create a `Str` from an `OsString`.
165        ///
166        /// This will fail if the `OsString` contains invalid UTF-8 data.
167        fn try_from(value: OsString) -> Result<Self, Self::Error> {
168            Self::from_utf8(value.into_encoded_bytes())
169        }
170    }
171
172    impl ToSocketAddrs for Str {
173        type Iter = IntoIter<SocketAddr>;
174
175        /// Converts a string to a socket address.
176        fn to_socket_addrs(&self) -> io::Result<Self::Iter> {
177            self.deref().to_socket_addrs()
178        }
179    }
180}
181
182impl Str {
183    /// Creates a `Str` from a static string slice.
184    ///
185    /// This method allows creating a `Str` from a string with a static lifetime,
186    /// which will be stored as a pointer to the static data without any allocation.
187    ///
188    /// # Examples
189    ///
190    /// ```
191    /// use waterui_str::Str;
192    ///
193    /// let s = Str::from_static("hello");
194    /// assert_eq!(s, "hello");
195    /// // Reference count is intentionally not exposed
196    /// ```
197    #[must_use]
198    pub const fn from_static(s: &'static str) -> Self {
199        // SAFETY: a `&'static str` never has a null data pointer, and its non-negative
200        // length is what marks this `Str` as the static representation.
201        unsafe {
202            Self {
203                ptr: NonNull::new_unchecked(s.as_ptr().cast_mut().cast::<()>()),
204                len: s.len().cast_signed(),
205            }
206        }
207    }
208
209    fn from_string(string: String) -> Self {
210        let len = string.len();
211        if len == 0 {
212            // use static empty string
213            return Self::new();
214        }
215
216        Self {
217            ptr: NonNull::from(Box::leak(Box::new(Shared::new(string)))).cast::<()>(),
218            len: -len.cast_signed(),
219        }
220    }
221
222    const fn is_shared(&self) -> bool {
223        self.len < 0
224    }
225
226    const fn as_shared(&self) -> Result<&Shared, &'static str> {
227        if !self.is_shared() {
228            // SAFETY: `len >= 0` is the static representation, so `ptr`/`len` describe
229            // the original `&'static str` — live for the program, and UTF-8 already.
230            return Err(unsafe {
231                core::str::from_utf8_unchecked(slice::from_raw_parts(
232                    self.ptr.as_ptr().cast(),
233                    self.len(),
234                ))
235            });
236        }
237
238        // SAFETY: `len < 0` is the shared representation, so `ptr` is the leaked
239        // `Shared` this `Str` holds a count on; the borrow is tied to `&self`.
240        unsafe { Ok(self.ptr.cast::<Shared>().as_ref()) }
241    }
242
243    /// Returns a string slice of this `Str`.
244    ///
245    /// This method works for both static and owned strings.
246    ///
247    /// # Examples
248    ///
249    /// ```
250    /// use waterui_str::Str;
251    ///
252    /// let s1 = Str::from("hello");
253    /// assert_eq!(s1.as_str(), "hello");
254    ///
255    /// let s2 = Str::from(String::from("world"));
256    /// assert_eq!(s2.as_str(), "world");
257    /// ```
258    #[must_use]
259    pub const fn as_str(&self) -> &str {
260        match self.as_shared() {
261            // SAFETY: the `Shared` outlives this borrow because `self` holds a count
262            // on it.
263            Ok(shared) => unsafe { shared.as_str() },
264            Err(str) => str,
265        }
266    }
267
268    /// Returns the length of the string, in bytes.
269    ///
270    /// # Examples
271    ///
272    /// ```
273    /// use waterui_str::Str;
274    /// let s = Str::from("hello");
275    /// assert_eq!(s.len(), 5);
276    /// ```
277    #[must_use]
278    pub const fn len(&self) -> usize {
279        self.len.unsigned_abs()
280    }
281
282    /// Returns `true` if the string has a length of zero.
283    ///
284    /// # Examples
285    ///
286    /// ```
287    /// use waterui_str::Str;
288    /// let s = Str::new();
289    /// assert!(s.is_empty());
290    /// let s2 = Str::from("not empty");
291    /// assert!(!s2.is_empty());
292    /// ```
293    #[must_use]
294    pub const fn is_empty(&self) -> bool {
295        self.len() == 0
296    }
297
298    // Intentionally no public API exposing reference counts.
299
300    /// Converts this `Str` into a `String`.
301    ///
302    /// For static strings, this will allocate a new string and copy the contents.
303    /// For owned strings, this will attempt to take ownership of the string if the reference
304    /// count is 1, or create a new copy otherwise.
305    ///
306    /// # Examples
307    ///
308    /// ```
309    /// use waterui_str::Str;
310    ///
311    /// let s1 = Str::from("static");
312    /// let s1_string = s1.into_string();
313    /// assert_eq!(s1_string, "static");
314    ///
315    /// let s2 = Str::from(String::from("owned"));
316    /// let s2_string = s2.into_string();
317    /// assert_eq!(s2_string, "owned");
318    /// ```
319    #[must_use]
320    pub fn into_string(self) -> String {
321        let this = ManuallyDrop::new(self);
322        match this.as_shared() {
323            // SAFETY: `self` is wrapped in `ManuallyDrop`, so its count is not released
324            // twice. When unique, this is the last owner and may reclaim the box;
325            // otherwise it drops its own count and copies the contents.
326            Ok(shared) => unsafe {
327                if shared.is_unique() {
328                    let shared = Box::from_raw(this.ptr.cast::<Shared>().as_ptr());
329
330                    shared.take()
331                } else {
332                    shared.decrement_count();
333                    shared.as_str().to_string()
334                }
335            },
336            Err(str) => str.to_string(),
337        }
338    }
339}
340
341impl Str {
342    /// Creates a new empty `Str`.
343    ///
344    /// This returns a static empty string reference.
345    ///
346    /// # Examples
347    ///
348    /// ```
349    /// use waterui_str::Str;
350    ///
351    /// let s = Str::new();
352    /// assert_eq!(s, "");
353    /// // Reference count is intentionally not exposed
354    /// ```
355    #[must_use]
356    pub const fn new() -> Self {
357        Self::from_static("")
358    }
359
360    /// Creates a `Str` from a vector of bytes.
361    ///
362    /// This function will attempt to convert the vector to a UTF-8 string and
363    /// wrap it in a `Str`. If the vector does not contain valid UTF-8, an error
364    /// is returned.
365    ///
366    /// # Errors
367    ///
368    /// Returns an error if the provided byte vector does not contain valid UTF-8 data.
369    ///
370    /// # Examples
371    ///
372    /// ```
373    /// use waterui_str::Str;
374    ///
375    /// let bytes = vec![104, 101, 108, 108, 111]; // "hello" in UTF-8
376    /// let s = Str::from_utf8(bytes).unwrap();
377    /// assert_eq!(s, "hello");
378    /// // Reference count is intentionally not exposed
379    ///
380    /// // Invalid UTF-8 sequence
381    /// let invalid = vec![0xFF, 0xFF];
382    /// assert!(Str::from_utf8(invalid).is_err());
383    /// ```
384    pub fn from_utf8(bytes: Vec<u8>) -> Result<Self, FromUtf8Error> {
385        String::from_utf8(bytes).map(Self::from)
386    }
387
388    /// # Safety
389    ///
390    /// This function is unsafe because it does not check that the bytes passed
391    /// to it are valid UTF-8. If this constraint is violated, it may cause
392    /// memory unsafety issues with future users of the `Str`.
393    ///
394    /// # Examples
395    ///
396    /// ```
397    /// use waterui_str::Str;
398    ///
399    /// // SAFETY: We know these bytes form valid UTF-8
400    /// let bytes = vec![104, 101, 108, 108, 111]; // "hello" in UTF-8
401    /// let s = unsafe { Str::from_utf8_unchecked(bytes) };
402    /// assert_eq!(s, "hello");
403    /// ```
404    #[must_use]
405    pub unsafe fn from_utf8_unchecked(bytes: Vec<u8>) -> Self {
406        // SAFETY: this function's own contract requires `bytes` to be valid UTF-8.
407        unsafe { Self::from(String::from_utf8_unchecked(bytes)) }
408    }
409
410    /// Applies a function to the owned string representation of this `Str`.
411    ///
412    /// This is an internal utility method used for operations that need to modify
413    /// the string contents.
414    fn handle(&mut self, f: impl FnOnce(&mut String)) {
415        let mut string = take(self).into_string();
416        f(&mut string);
417        *self = Self::from(string);
418    }
419
420    /// Appends a string to this `Str`.
421    ///
422    /// This method will convert the `Str` to an owned string if it's a static reference.
423    ///
424    /// # Examples
425    ///
426    /// ```
427    /// use waterui_str::Str;
428    ///
429    /// let mut s = Str::from("hello");
430    /// s.append(" world");
431    /// assert_eq!(s, "hello world");
432    /// ```
433    pub fn append(&mut self, s: impl AsRef<str>) {
434        let mut string = take(self).into_string();
435        string.push_str(s.as_ref());
436        *self = Self::from(string);
437    }
438}
439impl From<&'static str> for Str {
440    /// Creates a `Str` from a static string slice.
441    ///
442    /// This stores a reference to the original string without any allocation.
443    ///
444    /// # Examples
445    ///
446    /// ```
447    /// use waterui_str::Str;
448    ///
449    /// let s = Str::from("hello");
450    /// assert_eq!(s, "hello");
451    /// // Reference count is intentionally not exposed
452    /// ```
453    fn from(value: &'static str) -> Self {
454        Self::from_static(value)
455    }
456}
457
458impl From<String> for Str {
459    /// Creates a `Str` from an owned `String`.
460    ///
461    /// This will store the string in a reference-counted container.
462    ///
463    /// # Examples
464    ///
465    /// ```
466    /// use waterui_str::Str;
467    ///
468    /// let s = Str::from(String::from("hello"));
469    /// assert_eq!(s, "hello");
470    /// // Reference count is intentionally not exposed
471    /// ```
472    fn from(value: String) -> Self {
473        Self::from_string(value)
474    }
475}
476
477impl From<Str> for String {
478    fn from(value: Str) -> Self {
479        value.into_string()
480    }
481}
482
483#[cfg(test)]
484mod tests {
485    use super::*;
486    use alloc::vec;
487
488    #[test]
489    fn test_static_string_creation() {
490        let s = Str::from_static("hello");
491        assert_eq!(s.as_str(), "hello");
492        assert_eq!(s.len(), 5);
493        assert!(!s.is_empty());
494        // no reference count exposed
495    }
496
497    #[test]
498    fn test_owned_string_creation() {
499        let s = Str::from(String::from("hello"));
500        assert_eq!(s.as_str(), "hello");
501        assert_eq!(s.len(), 5);
502        assert!(!s.is_empty());
503        // no reference count exposed
504    }
505
506    #[test]
507    fn test_empty_string() {
508        let s = Str::new();
509        assert_eq!(s.as_str(), "");
510        assert_eq!(s.len(), 0);
511        assert!(s.is_empty());
512        // no reference count exposed
513    }
514
515    #[test]
516    fn test_static_string_clone() {
517        let s1 = Str::from_static("hello");
518        let s2 = s1.clone();
519
520        assert_eq!(s1.as_str(), "hello");
521        assert_eq!(s2.as_str(), "hello");
522        // no reference count exposed
523    }
524
525    #[test]
526    fn test_owned_string_clone() {
527        let s1 = Str::from(String::from("hello"));
528        let s2 = s1.clone();
529
530        assert_eq!(s1.as_str(), "hello");
531        assert_eq!(s2.as_str(), "hello");
532    }
533
534    #[test]
535    fn test_multiple_clones() {
536        let s1 = Str::from(String::from("test"));
537        let s2 = s1.clone();
538        let s3 = s1;
539        let s4 = s2.clone();
540
541        // no reference count exposed
542
543        drop(s4);
544        // no reference count exposed
545
546        drop(s3);
547        drop(s2);
548        // no reference count exposed
549    }
550
551    #[test]
552    fn test_reference_counting_drop() {
553        let s1 = Str::from(String::from("hello"));
554
555        {
556            let _s2 = s1;
557        } // s2 is dropped here
558
559        // no reference count exposed
560    }
561
562    #[test]
563    fn test_into_string_unique() {
564        let s = Str::from(String::from("hello"));
565        // no reference count exposed
566
567        let string = s.into_string();
568        assert_eq!(string, "hello");
569    }
570
571    #[test]
572    fn test_into_string_shared() {
573        let s1 = Str::from(String::from("hello"));
574        let _s2 = s1.clone();
575
576        let string = s1.into_string();
577        assert_eq!(string, "hello");
578        // no reference count exposed
579    }
580
581    #[test]
582    fn test_into_string_static() {
583        let s = Str::from_static("hello");
584        let string = s.into_string();
585        assert_eq!(string, "hello");
586    }
587
588    #[test]
589    fn test_from_utf8_valid() {
590        let bytes = vec![104, 101, 108, 108, 111]; // "hello"
591        let s = Str::from_utf8(bytes).unwrap();
592        assert_eq!(s.as_str(), "hello");
593    }
594
595    #[test]
596    fn test_from_utf8_invalid() {
597        let bytes = vec![0xFF, 0xFF];
598        assert!(Str::from_utf8(bytes).is_err());
599    }
600
601    #[test]
602    fn test_from_utf8_unchecked() {
603        let bytes = vec![104, 101, 108, 108, 111]; // "hello"
604        // SAFETY: `bytes` is the ASCII literal spelled out just above, so it is
605        // valid UTF-8.
606        let s = unsafe { Str::from_utf8_unchecked(bytes) };
607        assert_eq!(s.as_str(), "hello");
608        // no reference count exposed
609    }
610
611    #[test]
612    fn test_append() {
613        let mut s = Str::from("hello");
614        s.append(" world");
615        assert_eq!(s.as_str(), "hello world");
616    }
617
618    #[test]
619    fn test_append_static_to_owned() {
620        let mut s = Str::from_static("hello");
621
622        s.append(" world");
623        assert_eq!(s.as_str(), "hello world");
624    }
625
626    #[test]
627    fn test_as_bytes() {
628        let s = Str::from("hello");
629        assert_eq!(s.as_bytes(), b"hello");
630    }
631
632    #[test]
633    fn test_deref() {
634        let s = Str::from("hello");
635        assert_eq!(&*s, "hello");
636        assert_eq!(s.chars().count(), 5);
637    }
638
639    #[test]
640    fn test_empty_string_from_string() {
641        let s = Str::from(String::new());
642        assert_eq!(s.as_str(), "");
643        assert!(s.is_empty());
644        // no reference count exposed
645    }
646
647    // Memory safety tests designed for Miri
648    #[test]
649    fn test_memory_safety_clone_drop_cycles() {
650        // Test multiple clone/drop cycles to ensure no memory leaks or double-frees
651        for _ in 0..100 {
652            let s1 = Str::from(String::from("test"));
653            let s2 = s1.clone();
654            let s3 = s2.clone();
655
656            drop(s1);
657            drop(s3);
658            drop(s2);
659        }
660    }
661
662    #[test]
663    fn test_memory_safety_interleaved_operations() {
664        let mut strings = vec![];
665
666        // Create multiple strings with shared references
667        for i in 0..10 {
668            let mut content = String::from("string_");
669            content.push_str(&(i.to_string()));
670            let s = Str::from(content);
671            strings.push(s.clone());
672            strings.push(s);
673        }
674
675        // Randomly drop some strings
676        for i in (0..strings.len()).step_by(3) {
677            if i < strings.len() {
678                strings.remove(i);
679            }
680        }
681
682        // Verify remaining strings are still valid
683        for s in &strings {
684            assert!(!s.as_str().is_empty());
685        }
686    }
687
688    #[test]
689    fn test_memory_safety_reference_counting() {
690        let original = Str::from(String::from("reference test"));
691        let mut clones = vec![];
692
693        // Create many clones
694        for _ in 0..50 {
695            clones.push(original.clone());
696        }
697        assert_eq!(clones.len(), 50);
698
699        // no reference count exposed
700
701        // Drop half the clones
702        clones.truncate(25);
703        assert_eq!(clones.len(), 25);
704        // no reference count exposed
705
706        // Drop all clones
707        clones.clear();
708        assert!(clones.is_empty());
709        // no reference count exposed
710    }
711
712    #[test]
713    fn test_memory_safety_into_string_with_clones() {
714        let s1 = Str::from(String::from("unique test"));
715        let _s2 = s1.clone();
716        let _s3 = s1.clone();
717
718        // no reference count exposed
719
720        // Converting to string should not affect other references
721        let string = s1.into_string();
722        assert_eq!(string, "unique test");
723        // no reference count exposed
724    }
725
726    #[test]
727    fn test_memory_safety_unique_into_string() {
728        // Test that unique references properly transfer ownership
729        let s = Str::from(String::from("unique"));
730        // no reference count exposed
731
732        let string = s.into_string();
733        assert_eq!(string, "unique");
734        // s is consumed, can't check reference count
735    }
736
737    #[test]
738    fn test_memory_safety_static_vs_owned() {
739        let static_str = Str::from_static("static");
740        let owned_str = Str::from(String::from("owned"));
741
742        // Clone both types many times
743        let mut static_clones = vec![];
744        let mut owned_clones = vec![];
745
746        for _ in 0..100 {
747            static_clones.push(static_str.clone());
748            owned_clones.push(owned_str.clone());
749        }
750
751        // no reference count exposed
752
753        // Verify all clones work correctly
754        for clone in &static_clones {
755            assert_eq!(clone.as_str(), "static");
756            // no reference count exposed
757        }
758
759        for clone in &owned_clones {
760            assert_eq!(clone.as_str(), "owned");
761            // no reference count exposed
762        }
763    }
764
765    #[test]
766    fn test_memory_safety_mixed_operations() {
767        let mut s = Str::from_static("hello");
768        // no reference count exposed
769
770        // Convert to owned by appending
771        s.append(" world");
772        // no reference count exposed
773
774        // Clone the owned string
775        let _s2 = s.clone();
776        // no reference count exposed
777
778        // Convert back to string
779        let string = s.into_string();
780        assert_eq!(string, "hello world");
781        // no reference count exposed
782    }
783
784    #[test]
785    fn test_memory_safety_zero_length_edge_cases() {
786        // Test various ways to create empty strings
787        let empty1 = Str::new();
788        let empty2 = Str::from("");
789        let empty3 = Str::from(String::new());
790        let empty4 = Str::from_utf8(vec![]).unwrap();
791
792        assert!(empty1.is_empty());
793        assert!(empty2.is_empty());
794        assert!(empty3.is_empty());
795        assert!(empty4.is_empty());
796
797        // All empty strings should be static references
798        // no reference count exposed
799    }
800
801    #[test]
802    fn test_memory_safety_large_strings() {
803        // Test with larger strings to ensure proper memory handling
804        let large_content = "x".repeat(10000);
805        let s1 = Str::from(large_content.clone());
806        // no reference count exposed
807
808        let s2 = s1.clone();
809        // no reference count exposed
810
811        assert_eq!(s1.len(), 10000);
812        assert_eq!(s2.len(), 10000);
813        assert_eq!(s1.as_str(), large_content);
814        assert_eq!(s2.as_str(), large_content);
815    }
816
817    #[test]
818    fn test_memory_safety_concurrent_like_pattern() {
819        // Simulate concurrent-like access patterns (single-threaded but similar stress)
820        let base = Str::from(String::from("base"));
821        let mut handles = vec![];
822
823        // Create many references
824        for _ in 0..1000 {
825            handles.push(base.clone());
826        }
827
828        // no reference count exposed
829
830        // Process in chunks, dropping some while keeping others
831        for chunk in handles.chunks_mut(100) {
832            for (i, handle) in chunk.iter().enumerate() {
833                assert_eq!(handle.as_str(), "base");
834                if i.is_multiple_of(2) {
835                    // Mark for keeping (we'll drop the others)
836                }
837            }
838        }
839
840        // Keep only every 3rd element
841        let mut i = 0;
842        handles.retain(|_| {
843            i += 1;
844            i % 3 == 0
845        });
846
847        // Verify reference count updated correctly
848        let _expected_count = handles.len() + 1; // +1 for base
849
850        // Verify all remaining handles are valid
851        for handle in &handles {
852            assert_eq!(handle.as_str(), "base");
853        }
854    }
855
856    #[test]
857    fn test_memory_safety_drop_order_stress() {
858        // Test various drop orders to ensure no use-after-free
859        let s1 = Str::from(String::from("original"));
860        let s2 = s1.clone();
861        let s3 = s1.clone();
862        let s4 = s2.clone();
863        let s5 = s3.clone();
864
865        // no reference count exposed
866
867        // Drop in different orders across multiple test runs
868        {
869            let temp1 = s1.clone();
870            let temp2 = s2.clone();
871            drop(temp2);
872            drop(temp1);
873            // temp1 and temp2 dropped first
874        }
875
876        // no reference count exposed
877
878        drop(s5); // Drop s5 first
879        // no reference count exposed
880
881        drop(s2); // Drop s2 (middle)
882        // no reference count exposed
883
884        drop(s1); // Drop original
885        // no reference count exposed
886
887        drop(s4); // Drop s4
888        // no reference count exposed
889
890        // s3 is the last one standing
891        assert_eq!(s3.as_str(), "original");
892    }
893
894    #[test]
895    fn test_memory_safety_ptr_stability() {
896        // Ensure string content pointer remains stable across clones
897        let s1 = Str::from(String::from("stable"));
898        let ptr1 = s1.as_str().as_ptr();
899
900        let s2 = s1.clone();
901        let ptr2 = s2.as_str().as_ptr();
902
903        // Clones should point to the same underlying data
904        assert_eq!(ptr1, ptr2);
905
906        let s3 = s2.clone();
907        let ptr3 = s3.as_str().as_ptr();
908
909        assert_eq!(ptr1, ptr3);
910        assert_eq!(ptr2, ptr3);
911
912        // Even after dropping some references, remaining should still be valid
913        drop(s1);
914        assert_eq!(s2.as_str().as_ptr(), ptr2);
915        assert_eq!(s3.as_str().as_ptr(), ptr3);
916    }
917
918    #[test]
919    fn test_memory_safety_alternating_clone_drop() {
920        let original = Str::from(String::from("alternating"));
921        let mut refs = vec![original];
922
923        // Alternating pattern: clone, clone, drop, clone, drop, etc.
924        for i in 0..100 {
925            if i % 4 == 0 || i % 4 == 1 {
926                // Clone phase
927                let new_ref = refs[0].clone();
928                refs.push(new_ref);
929            } else if i % 4 == 2 && refs.len() > 1 {
930                // Drop phase
931                refs.pop();
932            }
933
934            // Verify all remaining references are valid
935            for r in &refs {
936                assert_eq!(r.as_str(), "alternating");
937            }
938        }
939    }
940}