#![doc = include_str!("../README.md")]
#![allow(clippy::cast_possible_wrap)]
extern crate alloc;
mod impls;
mod shared;
use alloc::{
borrow::Cow,
boxed::Box,
string::{FromUtf8Error, String, ToString},
vec::Vec,
};
use shared::Shared;
use core::{
borrow::Borrow,
mem::{ManuallyDrop, take},
ops::Deref,
ptr::NonNull,
slice,
};
nami_core::impl_constant!(Str);
#[derive(Debug)]
pub struct Str {
ptr: NonNull<()>,
len: isize,
}
impl Drop for Str {
fn drop(&mut self) {
if let Ok(shared) = self.as_shared() {
unsafe {
if shared.is_unique() {
let ptr = self.ptr.cast::<Shared>().as_ptr();
let _ = Box::from_raw(ptr);
} else {
shared.decrement_count();
}
}
}
}
}
impl Clone for Str {
fn clone(&self) -> Self {
if let Ok(shared) = self.as_shared() {
unsafe {
shared.increment_count();
}
}
Self {
ptr: self.ptr,
len: self.len,
}
}
}
impl Deref for Str {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl Borrow<str> for Str {
fn borrow(&self) -> &str {
self.as_str()
}
}
impl AsRef<str> for Str {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl AsRef<[u8]> for Str {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl Default for Str {
fn default() -> Self {
Self::new()
}
}
impl From<Cow<'static, str>> for Str {
fn from(value: Cow<'static, str>) -> Self {
match value {
Cow::Borrowed(s) => s.into(),
Cow::Owned(s) => s.into(),
}
}
}
mod std_on {
use alloc::{string::FromUtf8Error, vec::IntoIter};
use crate::Str;
extern crate std;
use core::{net::SocketAddr, ops::Deref};
use std::{
ffi::{OsStr, OsString},
io,
net::ToSocketAddrs,
path::Path,
};
impl AsRef<OsStr> for Str {
fn as_ref(&self) -> &OsStr {
self.deref().as_ref()
}
}
impl AsRef<Path> for Str {
fn as_ref(&self) -> &Path {
self.deref().as_ref()
}
}
impl TryFrom<OsString> for Str {
type Error = FromUtf8Error;
fn try_from(value: OsString) -> Result<Self, Self::Error> {
Self::from_utf8(value.into_encoded_bytes())
}
}
impl ToSocketAddrs for Str {
type Iter = IntoIter<SocketAddr>;
fn to_socket_addrs(&self) -> io::Result<Self::Iter> {
self.deref().to_socket_addrs()
}
}
}
impl Str {
#[must_use]
pub const fn from_static(s: &'static str) -> Self {
unsafe {
Self {
ptr: NonNull::new_unchecked(s.as_ptr().cast_mut().cast::<()>()),
len: s.len() as isize,
}
}
}
fn from_string(string: String) -> Self {
let len = string.len();
if len == 0 {
return Self::new();
}
unsafe {
Self {
ptr: NonNull::new_unchecked(Box::into_raw(Box::new(Shared::new(string))))
.cast::<()>(),
len: -(len as isize),
}
}
}
const fn is_shared(&self) -> bool {
self.len < 0
}
const fn as_shared(&self) -> Result<&Shared, &'static str> {
if !self.is_shared() {
return Err(unsafe {
core::str::from_utf8_unchecked(slice::from_raw_parts(
self.ptr.as_ptr().cast(),
self.len(),
))
});
}
unsafe { Ok(self.ptr.cast::<Shared>().as_ref()) }
}
#[must_use]
pub const fn as_str(&self) -> &str {
match self.as_shared() {
Ok(shared) => unsafe { shared.as_str() },
Err(str) => str,
}
}
#[must_use]
pub const fn len(&self) -> usize {
self.len.unsigned_abs()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn into_string(self) -> String {
let this = ManuallyDrop::new(self);
match this.as_shared() {
Ok(shared) => unsafe {
if shared.is_unique() {
let shared = Box::from_raw(this.ptr.cast::<Shared>().as_ptr());
shared.take()
} else {
shared.decrement_count();
shared.as_str().to_string()
}
},
Err(str) => str.to_string(),
}
}
}
impl Str {
#[must_use]
pub const fn new() -> Self {
Self::from_static("")
}
pub fn from_utf8(bytes: Vec<u8>) -> Result<Self, FromUtf8Error> {
String::from_utf8(bytes).map(Self::from)
}
#[must_use]
pub unsafe fn from_utf8_unchecked(bytes: Vec<u8>) -> Self {
unsafe { Self::from(String::from_utf8_unchecked(bytes)) }
}
fn handle(&mut self, f: impl FnOnce(&mut String)) {
let mut string = take(self).into_string();
f(&mut string);
*self = Self::from(string);
}
pub fn append(&mut self, s: impl AsRef<str>) {
let mut string = take(self).into_string();
string.push_str(s.as_ref());
*self = Self::from(string);
}
}
impl From<&'static str> for Str {
fn from(value: &'static str) -> Self {
Self::from_static(value)
}
}
impl From<String> for Str {
fn from(value: String) -> Self {
Self::from_string(value)
}
}
impl From<Str> for String {
fn from(value: Str) -> Self {
value.into_string()
}
}
#[cfg(test)]
#[allow(unused)]
#[allow(clippy::redundant_clone)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn test_static_string_creation() {
let s = Str::from_static("hello");
assert_eq!(s.as_str(), "hello");
assert_eq!(s.len(), 5);
assert!(!s.is_empty());
}
#[test]
fn test_owned_string_creation() {
let s = Str::from(String::from("hello"));
assert_eq!(s.as_str(), "hello");
assert_eq!(s.len(), 5);
assert!(!s.is_empty());
}
#[test]
fn test_empty_string() {
let s = Str::new();
assert_eq!(s.as_str(), "");
assert_eq!(s.len(), 0);
assert!(s.is_empty());
}
#[test]
fn test_static_string_clone() {
let s1 = Str::from_static("hello");
let s2 = s1.clone();
assert_eq!(s1.as_str(), "hello");
assert_eq!(s2.as_str(), "hello");
}
#[test]
fn test_owned_string_clone() {
let s1 = Str::from(String::from("hello"));
let s2 = s1.clone();
assert_eq!(s1.as_str(), "hello");
assert_eq!(s2.as_str(), "hello");
}
#[test]
fn test_multiple_clones() {
let s1 = Str::from(String::from("test"));
let s2 = s1.clone();
let s3 = s1.clone();
let s4 = s2.clone();
drop(s4);
drop(s3);
drop(s2);
}
#[test]
fn test_reference_counting_drop() {
let s1 = Str::from(String::from("hello"));
{
let s2 = s1;
}
}
#[test]
fn test_into_string_unique() {
let s = Str::from(String::from("hello"));
let string = s.into_string();
assert_eq!(string, "hello");
}
#[test]
fn test_into_string_shared() {
let s1 = Str::from(String::from("hello"));
let s2 = s1.clone();
let string = s1.into_string();
assert_eq!(string, "hello");
}
#[test]
fn test_into_string_static() {
let s = Str::from_static("hello");
let string = s.into_string();
assert_eq!(string, "hello");
}
#[test]
fn test_from_utf8_valid() {
let bytes = vec![104, 101, 108, 108, 111]; let s = Str::from_utf8(bytes).unwrap();
assert_eq!(s.as_str(), "hello");
}
#[test]
fn test_from_utf8_invalid() {
let bytes = vec![0xFF, 0xFF];
assert!(Str::from_utf8(bytes).is_err());
}
#[test]
fn test_from_utf8_unchecked() {
let bytes = vec![104, 101, 108, 108, 111]; let s = unsafe { Str::from_utf8_unchecked(bytes) };
assert_eq!(s.as_str(), "hello");
}
#[test]
fn test_append() {
let mut s = Str::from("hello");
s.append(" world");
assert_eq!(s.as_str(), "hello world");
}
#[test]
fn test_append_static_to_owned() {
let mut s = Str::from_static("hello");
s.append(" world");
assert_eq!(s.as_str(), "hello world");
}
#[test]
fn test_as_bytes() {
let s = Str::from("hello");
assert_eq!(s.as_bytes(), b"hello");
}
#[test]
fn test_deref() {
let s = Str::from("hello");
assert_eq!(&*s, "hello");
assert_eq!(s.chars().count(), 5);
}
#[test]
fn test_empty_string_from_string() {
let s = Str::from(String::new());
assert_eq!(s.as_str(), "");
assert!(s.is_empty());
}
#[test]
fn test_memory_safety_clone_drop_cycles() {
for _ in 0..100 {
let s1 = Str::from(String::from("test"));
let s2 = s1.clone();
let s3 = s2.clone();
drop(s1);
drop(s3);
drop(s2);
}
}
#[test]
fn test_memory_safety_interleaved_operations() {
let mut strings = vec![];
for i in 0..10 {
let mut content = String::from("string_");
content.push_str(&(i.to_string()));
let s = Str::from(content);
strings.push(s.clone());
strings.push(s);
}
for i in (0..strings.len()).step_by(3) {
if i < strings.len() {
strings.remove(i);
}
}
for s in &strings {
assert!(!s.as_str().is_empty());
}
}
#[test]
fn test_memory_safety_reference_counting() {
let original = Str::from(String::from("reference test"));
#[allow(clippy::collection_is_never_read)]
let mut clones = vec![];
for _ in 0..50 {
clones.push(original.clone());
}
clones.truncate(25);
clones.clear();
}
#[test]
fn test_memory_safety_into_string_with_clones() {
let s1 = Str::from(String::from("unique test"));
let s2 = s1.clone();
let s3 = s1.clone();
let string = s1.into_string();
assert_eq!(string, "unique test");
}
#[test]
fn test_memory_safety_unique_into_string() {
let s = Str::from(String::from("unique"));
let string = s.into_string();
assert_eq!(string, "unique");
}
#[test]
fn test_memory_safety_static_vs_owned() {
let static_str = Str::from_static("static");
let owned_str = Str::from(String::from("owned"));
let mut static_clones = vec![];
let mut owned_clones = vec![];
for _ in 0..100 {
static_clones.push(static_str.clone());
owned_clones.push(owned_str.clone());
}
for clone in &static_clones {
assert_eq!(clone.as_str(), "static");
}
for clone in &owned_clones {
assert_eq!(clone.as_str(), "owned");
}
}
#[test]
fn test_memory_safety_mixed_operations() {
let mut s = Str::from_static("hello");
s.append(" world");
let s2 = s.clone();
let string = s.into_string();
assert_eq!(string, "hello world");
}
#[test]
fn test_memory_safety_zero_length_edge_cases() {
let empty1 = Str::new();
let empty2 = Str::from("");
let empty3 = Str::from(String::new());
let empty4 = Str::from_utf8(vec![]).unwrap();
assert!(empty1.is_empty());
assert!(empty2.is_empty());
assert!(empty3.is_empty());
assert!(empty4.is_empty());
}
#[test]
fn test_memory_safety_large_strings() {
let large_content = "x".repeat(10000);
let s1 = Str::from(large_content.clone());
let s2 = s1.clone();
assert_eq!(s1.len(), 10000);
assert_eq!(s2.len(), 10000);
assert_eq!(s1.as_str(), large_content);
assert_eq!(s2.as_str(), large_content);
}
#[test]
fn test_memory_safety_concurrent_like_pattern() {
let base = Str::from(String::from("base"));
let mut handles = vec![];
for _ in 0..1000 {
handles.push(base.clone());
}
for chunk in handles.chunks_mut(100) {
for (i, handle) in chunk.iter().enumerate() {
assert_eq!(handle.as_str(), "base");
#[allow(clippy::manual_is_multiple_of)]
if i % 2 == 0 {
}
}
}
let mut i = 0;
handles.retain(|_| {
i += 1;
i % 3 == 0
});
let _expected_count = handles.len() + 1;
for handle in &handles {
assert_eq!(handle.as_str(), "base");
}
}
#[test]
fn test_memory_safety_drop_order_stress() {
let s1 = Str::from(String::from("original"));
let s2 = s1.clone();
let s3 = s1.clone();
let s4 = s2.clone();
let s5 = s3.clone();
{
let temp1 = s1.clone();
let temp2 = s2.clone();
drop(temp2);
drop(temp1);
}
drop(s5);
drop(s2);
drop(s1);
drop(s4);
assert_eq!(s3.as_str(), "original");
}
#[test]
fn test_memory_safety_ptr_stability() {
let s1 = Str::from(String::from("stable"));
let ptr1 = s1.as_str().as_ptr();
let s2 = s1.clone();
let ptr2 = s2.as_str().as_ptr();
assert_eq!(ptr1, ptr2);
let s3 = s2.clone();
let ptr3 = s3.as_str().as_ptr();
assert_eq!(ptr1, ptr3);
assert_eq!(ptr2, ptr3);
drop(s1);
assert_eq!(s2.as_str().as_ptr(), ptr2);
assert_eq!(s3.as_str().as_ptr(), ptr3);
}
#[test]
fn test_memory_safety_alternating_clone_drop() {
let original = Str::from(String::from("alternating"));
let mut refs = vec![original];
for i in 0..100 {
if i % 4 == 0 || i % 4 == 1 {
let new_ref = refs[0].clone();
refs.push(new_ref);
} else if i % 4 == 2 && refs.len() > 1 {
refs.pop();
}
for r in &refs {
assert_eq!(r.as_str(), "alternating");
}
}
}
}