1use alloc::{borrow::Cow, ffi::CString, rc::Rc, sync::Arc};
2use core::{
3 ffi::{CStr, FromBytesWithNulError},
4 str::FromStr,
5};
6
7use crate::flex::{
8 FlexStr, ImmutableBytes, RefCounted, RefCountedMut, partial_eq_impl, ref_counted_mut_impl,
9};
10
11pub use flexstr_support::InteriorNulError;
12use flexstr_support::StringToFromBytes;
13use inline_flexstr::{InlineFlexStr, TooLongOrNulError};
14
15pub type LocalCStr = FlexStr<'static, CStr, Rc<CStr>>;
17
18pub type SharedCStr = FlexStr<'static, CStr, Arc<CStr>>;
20
21pub type LocalCStrRef<'s> = FlexStr<'s, CStr, Rc<CStr>>;
23
24pub type SharedCStrRef<'s> = FlexStr<'s, CStr, Arc<CStr>>;
26
27const _: () = assert!(
30 size_of::<Option<LocalCStr>>() <= size_of::<CString>() + size_of::<usize>(),
31 "Option<LocalCStr> must be less than or equal to the size of CString plus one machine word"
32);
33const _: () = assert!(
34 size_of::<Option<SharedCStr>>() <= size_of::<CString>() + size_of::<usize>(),
35 "Option<SharedCStr> must be less than or equal to the size of CString plus one machine word"
36);
37
38impl<'s, R: RefCounted<CStr>> FlexStr<'s, CStr, R> {
41 fn from_bytes_without_nul(bytes: &'s [u8]) -> Self {
42 match InlineFlexStr::try_from_bytes_with_or_without_nul(bytes) {
45 Ok(inline) => FlexStr::from_inline(inline),
46 Err(TooLongOrNulError::TooLong(_)) => FlexStr::from_owned(
48 #[cfg(feature = "safe")]
49 CString::new(bytes).expect("Unexpected interior NUL byte"),
51 #[cfg(not(feature = "safe"))]
52 unsafe {
54 CString::from_vec_unchecked(bytes.into())
55 },
56 ),
57 Err(TooLongOrNulError::NulError(e)) => {
59 unreachable!("Interior NUL byte found at position {}", e.position)
60 }
61 }
62 }
63
64 pub fn try_from_bytes_with_or_without_nul(bytes: &'s [u8]) -> Result<Self, InteriorNulError> {
66 match CStr::from_bytes_with_nul(bytes) {
67 Ok(cstr) => Ok(FlexStr::from_borrowed(cstr)),
69 Err(FromBytesWithNulError::NotNulTerminated) => Ok(Self::from_bytes_without_nul(bytes)),
71 Err(FromBytesWithNulError::InteriorNul { position }) => {
72 Err(InteriorNulError { position })
73 }
74 }
75 }
76
77 #[inline]
79 pub fn as_bytes_with_nul(&self) -> &[u8] {
80 self.as_raw_bytes()
81 }
82}
83
84impl ImmutableBytes for CStr {}
87
88ref_counted_mut_impl!(CStr);
91
92impl<'s, R: RefCounted<CStr>> From<CString> for FlexStr<'s, CStr, R> {
96 fn from(s: CString) -> Self {
97 FlexStr::from_owned(s)
98 }
99}
100
101impl<'s, R: RefCounted<CStr>> TryFrom<&'s str> for FlexStr<'s, CStr, R> {
104 type Error = InteriorNulError;
105
106 #[inline]
107 fn try_from(s: &'s str) -> Result<Self, Self::Error> {
108 FlexStr::try_from_bytes_with_or_without_nul(s.as_bytes())
109 }
110}
111
112impl<'s, R: RefCounted<CStr>> TryFrom<&'s [u8]> for FlexStr<'s, CStr, R> {
113 type Error = InteriorNulError;
114
115 #[inline]
116 fn try_from(bytes: &'s [u8]) -> Result<Self, Self::Error> {
117 FlexStr::try_from_bytes_with_or_without_nul(bytes)
118 }
119}
120
121partial_eq_impl!(CStr, CStr);
124partial_eq_impl!(&CStr, CStr);
125partial_eq_impl!(CString, CStr);
126partial_eq_impl!(Cow<'s, CStr>, CStr);
127
128impl<'s, S: ?Sized + StringToFromBytes, R: RefCounted<S>> AsRef<CStr> for FlexStr<'s, S, R>
131where
132 S: AsRef<CStr>,
133{
134 fn as_ref(&self) -> &CStr {
135 self.as_ref_type().as_ref()
136 }
137}
138
139impl<R: RefCounted<CStr>> FromStr for FlexStr<'static, CStr, R> {
142 type Err = InteriorNulError;
143
144 fn from_str(s: &str) -> Result<Self, Self::Err> {
145 FlexStr::try_from_bytes_with_or_without_nul(s.as_bytes()).map(FlexStr::into_owned)
146 }
147}