static_collections/ffi/
c_str.rs1use core::{cmp::Ordering, ffi::CStr, fmt, mem::MaybeUninit, ops::AddAssign, slice};
4
5#[derive(Debug)]
7pub struct NotNullTerminatedError;
8
9unsafe extern "C"
11{
12 fn strncat(dest:*mut i8,src:*const i8,cch:usize)->*mut i8;
13 fn strncmp(s1:*const i8,s2:*const i8,cch:usize)->isize;
14 fn strncpy(dest:*mut i8,src:*const i8,cch:usize)->*mut i8;
15 pub(crate) fn strnlen(str:*const i8,cch:usize)->usize;
16}
17
18pub struct StaticCString<const N:usize>
23{
24 buffer:MaybeUninit<[i8;N]>
25}
26
27impl<const N:usize> StaticCString<N>
28{
29 pub const fn new()->Self
41 {
42 let mut s=Self{buffer:MaybeUninit::uninit()};
43 unsafe
44 {
45 s.buffer.assume_init_mut()[0]=0;
47 }
48 s
49 }
50
51 #[inline(always)] pub fn len(&self)->usize
60 {
61 unsafe
62 {
63 strnlen(self.buffer.assume_init_ref().as_ptr(),N)
64 }
65 }
66
67 #[inline(always)] pub fn is_empty(&self)->bool
68 {
69 self.len()==0
70 }
71
72 #[inline(always)] pub const fn capacity(&self)->usize
81 {
82 N
83 }
84
85 #[inline(always)] pub const fn as_ptr(&self)->*const i8
89 {
90 unsafe
91 {
92 self.buffer.assume_init_ref().as_ptr()
93 }
94 }
95
96 #[inline(always)] pub const fn as_mut_ptr(&mut self)->*mut i8
100 {
101 unsafe
102 {
103 self.buffer.assume_init_mut().as_mut_ptr()
104 }
105 }
106
107 #[inline(always)] pub fn as_bytes(&self)->&[u8]
113 {
114 let l=self.len();
115 unsafe
116 {
117 slice::from_raw_parts(self.as_ptr().cast(),l)
118 }
119 }
120
121 #[inline(always)] pub fn as_mut_bytes(&mut self)->&mut [u8]
127 {
128 let l=self.len();
129 unsafe
130 {
131 slice::from_raw_parts_mut(self.as_mut_ptr().cast(),l)
132 }
133 }
134
135 #[inline(always)] pub fn as_bytes_with_nul(&self)->&[u8]
137 {
138 let l=self.len();
139 unsafe
140 {
141 slice::from_raw_parts(self.as_ptr().cast(),l+1)
142 }
143 }
144
145 #[inline(always)] pub unsafe fn as_mut_bytes_with_nul(&mut self)->&mut [u8]
150 {
151 let l=self.len();
152 unsafe
153 {
154 slice::from_raw_parts_mut(self.as_mut_ptr().cast(),l+1)
155 }
156 }
157
158 #[inline(always)] pub fn as_c_str(&self)->&CStr
159 {
160 unsafe
161 {
162 CStr::from_ptr(self.as_ptr())
163 }
164 }
165}
166
167impl<'a,const N:usize> StaticCString<N>
168{
169 #[inline(always)] pub fn from_raw_ptr(ptr:*const i8)->Result<&'a Self,NotNullTerminatedError>
170 {
171 let r:&Self=unsafe{&*ptr.cast()};
172 if r.len()>=N
173 {
174 Err(NotNullTerminatedError)
175 }
176 else
177 {
178 Ok(r)
179 }
180 }
181
182 #[inline(always)] pub fn from_raw_mut_ptr(ptr:*mut i8)->Result<&'a mut Self,NotNullTerminatedError>
183 {
184 let r:&mut Self=unsafe{&mut *ptr.cast()};
185 if r.len()>=N
186 {
187 Err(NotNullTerminatedError)
188 }
189 else
190 {
191 Ok(r)
192 }
193 }
194}
195
196impl<const N:usize> From<&CStr> for StaticCString<N>
197{
198 fn from(value: &CStr) -> Self
199 {
200 let mut s=Self::new();
201 unsafe
202 {
203 let p=value.as_ptr();
204 let q=s.buffer.assume_init_mut().as_mut_ptr();
205 strncpy(q,p,N);
206 q.add(N-1).write(0);
208 }
209 s
210 }
211}
212
213impl<const M:usize,const N:usize> PartialEq<StaticCString<M>> for StaticCString<N>
214{
215 fn eq(&self, other: &StaticCString<M>) -> bool
216 {
217 unsafe
218 {
219 let p=self.buffer.assume_init_ref().as_ptr();
220 let q=other.buffer.assume_init_ref().as_ptr();
221 strncmp(p,q,if M<N {M} else {N})==0
222 }
223 }
224}
225
226impl<const M:usize,const N:usize> PartialOrd<StaticCString<M>> for StaticCString<N>
227{
228 fn partial_cmp(&self, other: &StaticCString<M>) -> Option<Ordering>
229 {
230 let r=unsafe
231 {
232 let p=self.buffer.assume_init_ref().as_ptr();
233 let q=other.buffer.assume_init_ref().as_ptr();
234 strncmp(p,q,if M<N {M} else {N})
235 };
236 match r
237 {
238 ..0=>Some(Ordering::Less),
239 0=>Some(Ordering::Equal),
240 1.. =>Some(Ordering::Greater)
241 }
242 }
243}
244
245impl<const M:usize,const N:usize> AddAssign<StaticCString<M>> for StaticCString<N>
246{
247 fn add_assign(&mut self, rhs: StaticCString<M>)
248 {
249 unsafe
250 {
251 let p=rhs.buffer.assume_init_ref().as_ptr();
252 let q=self.buffer.assume_init_mut().as_mut_ptr();
253 strncat(q,p,if M<N {M} else {N});
254 q.add(N-1).write(0);
256 }
257 }
258}
259
260impl<const N:usize> fmt::Debug for StaticCString<N>
263{
264 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
265 {
266 self.as_c_str().fmt(f)
267 }
268}
269
270impl<const N:usize> Default for StaticCString<N>
271{
272 fn default() -> Self
273 {
274 Self::new()
275 }
276}
277
278unsafe impl<const N:usize> Send for StaticCString<N> {}
279unsafe impl<const N:usize> Sync for StaticCString<N> {}