waterui_str/
lib.rs

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