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}