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
9#[cfg(feature = "use_crt")]
12unsafe extern "C"
13{
14 fn strncat(dest:*mut i8,src:*const i8,cch:usize)->*mut i8;
15 fn strncmp(s1:*const i8,s2:*const i8,cch:usize)->isize;
16 fn strncpy(dest:*mut i8,src:*const i8,cch:usize)->*mut i8;
17 pub(crate) fn strnlen(str:*const i8,cch:usize)->usize;
18}
19
20#[cfg(not(feature = "use_crt"))]
23#[unsafe(no_mangle)] pub(crate) unsafe extern "C" fn strnlen(str:*const i8,cch:usize)->usize
24{
25 let s=unsafe{slice::from_raw_parts(str,cch)};
26 s.iter().position(|&v| v==0).unwrap_or(cch)
27}
28
29#[cfg(not(feature = "use_crt"))]
30#[unsafe(no_mangle)] unsafe extern "C" fn strncpy(dest:*mut i8,src:*const i8,cch:usize)->*mut i8
31{
32 let s1=unsafe{slice::from_raw_parts_mut(dest,cch)};
33 let s2=unsafe{slice::from_raw_parts(src,cch)};
34 for (i,&c) in s2.iter().enumerate()
35 {
36 s1[i]=c;
37 if c==0
38 {
39 break;
40 }
41 }
42 dest
43}
44
45#[cfg(not(feature = "use_crt"))]
46#[unsafe(no_mangle)] unsafe extern "C" fn strncmp(str1:*const i8,str2:*const i8,cch:usize)->isize
47{
48 let s1=unsafe{slice::from_raw_parts(str1,cch)};
49 let s2=unsafe{slice::from_raw_parts(str2,cch)};
50 for i in 0..cch
51 {
52 let diff=s1[i]-s2[i];
53 if diff!=0
54 {
55 return diff as isize
56 }
57 }
58 0
59}
60
61#[cfg(not(feature = "use_crt"))]
62#[unsafe(no_mangle)] unsafe extern "C" fn strncat(dest:*mut i8,src:*const i8,cch:usize)->*mut i8
63{
64 let start_index=unsafe{strnlen(dest,usize::MAX)};
65 let s1=unsafe{slice::from_raw_parts_mut(dest.add(start_index),cch)};
66 let s2=unsafe{slice::from_raw_parts(src,cch)};
67 for (i,&c) in s2.iter().enumerate()
68 {
69 s1[i]=c;
70 if c==0
71 {
72 break;
73 }
74 }
75 dest
76}
77
78#[derive(Clone)]
82pub struct StaticCString<const N:usize>
83{
84 buffer:MaybeUninit<[i8;N]>
85}
86
87impl<const N:usize> StaticCString<N>
88{
89 pub const fn new()->Self
101 {
102 let mut s=Self{buffer:MaybeUninit::uninit()};
103 unsafe
104 {
105 s.buffer.assume_init_mut()[0]=0;
107 }
108 s
109 }
110
111 #[inline(always)] pub fn len(&self)->usize
120 {
121 unsafe
122 {
123 strnlen(self.buffer.assume_init_ref().as_ptr(),N)
124 }
125 }
126
127 #[inline(always)] pub fn is_empty(&self)->bool
128 {
129 self.len()==0
130 }
131
132 #[inline(always)] pub const fn capacity(&self)->usize
141 {
142 N
143 }
144
145 #[inline(always)] pub const fn as_ptr(&self)->*const i8
149 {
150 unsafe
151 {
152 self.buffer.assume_init_ref().as_ptr()
153 }
154 }
155
156 #[inline(always)] pub const fn as_mut_ptr(&mut self)->*mut i8
160 {
161 unsafe
162 {
163 self.buffer.assume_init_mut().as_mut_ptr()
164 }
165 }
166
167 #[inline(always)] pub fn as_bytes(&self)->&[u8]
173 {
174 let l=self.len();
175 unsafe
176 {
177 slice::from_raw_parts(self.as_ptr().cast(),l)
178 }
179 }
180
181 #[inline(always)] pub fn as_mut_bytes(&mut self)->&mut [u8]
187 {
188 let l=self.len();
189 unsafe
190 {
191 slice::from_raw_parts_mut(self.as_mut_ptr().cast(),l)
192 }
193 }
194
195 #[inline(always)] pub fn as_bytes_with_nul(&self)->&[u8]
197 {
198 let l=self.len();
199 unsafe
200 {
201 slice::from_raw_parts(self.as_ptr().cast(),l+1)
202 }
203 }
204
205 #[inline(always)] pub unsafe fn as_mut_bytes_with_nul(&mut self)->&mut [u8]
210 {
211 let l=self.len();
212 unsafe
213 {
214 slice::from_raw_parts_mut(self.as_mut_ptr().cast(),l+1)
215 }
216 }
217
218 #[inline(always)] pub fn as_c_str(&self)->&CStr
219 {
220 unsafe
221 {
222 CStr::from_ptr(self.as_ptr())
223 }
224 }
225}
226
227impl<'a,const N:usize> StaticCString<N>
228{
229 #[inline(always)] pub unsafe fn from_raw_ptr(ptr:*const i8)->Result<&'a Self,NotNullTerminatedError>
241 {
242 let r:&Self=unsafe{&*ptr.cast()};
243 if r.len()>=N
244 {
245 Err(NotNullTerminatedError)
246 }
247 else
248 {
249 Ok(r)
250 }
251 }
252
253 #[inline(always)] pub unsafe fn from_raw_mut_ptr(ptr:*mut i8)->Result<&'a mut Self,NotNullTerminatedError>
265 {
266 let r:&mut Self=unsafe{&mut *ptr.cast()};
267 if r.len()>=N
268 {
269 Err(NotNullTerminatedError)
270 }
271 else
272 {
273 Ok(r)
274 }
275 }
276}
277
278impl<const N:usize> From<&CStr> for StaticCString<N>
279{
280 fn from(value: &CStr) -> Self
281 {
282 let mut s=Self::new();
283 unsafe
284 {
285 let p=value.as_ptr();
286 let q=s.buffer.assume_init_mut().as_mut_ptr();
287 strncpy(q,p,N);
288 q.add(N-1).write(0);
290 }
291 s
292 }
293}
294
295impl<const M:usize,const N:usize> PartialEq<StaticCString<M>> for StaticCString<N>
296{
297 fn eq(&self, other: &StaticCString<M>) -> bool
298 {
299 unsafe
300 {
301 let p=self.buffer.assume_init_ref().as_ptr();
302 let q=other.buffer.assume_init_ref().as_ptr();
303 strncmp(p,q,if M<N {M} else {N})==0
304 }
305 }
306}
307
308impl<const M:usize,const N:usize> PartialOrd<StaticCString<M>> for StaticCString<N>
309{
310 fn partial_cmp(&self, other: &StaticCString<M>) -> Option<Ordering>
311 {
312 let r=unsafe
313 {
314 let p=self.buffer.assume_init_ref().as_ptr();
315 let q=other.buffer.assume_init_ref().as_ptr();
316 strncmp(p,q,if M<N {M} else {N})
317 };
318 match r
319 {
320 ..0=>Some(Ordering::Less),
321 0=>Some(Ordering::Equal),
322 1.. =>Some(Ordering::Greater)
323 }
324 }
325}
326
327impl<const M:usize,const N:usize> AddAssign<StaticCString<M>> for StaticCString<N>
328{
329 fn add_assign(&mut self, rhs: StaticCString<M>)
330 {
331 unsafe
332 {
333 let p=rhs.buffer.assume_init_ref().as_ptr();
334 let q=self.buffer.assume_init_mut().as_mut_ptr();
335 strncat(q,p,if M<N {M} else {N});
336 q.add(N-1).write(0);
338 }
339 }
340}
341
342impl<const N:usize> fmt::Debug for StaticCString<N>
345{
346 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
347 {
348 self.as_c_str().fmt(f)
349 }
350}
351
352impl<const N:usize> Default for StaticCString<N>
353{
354 fn default() -> Self
355 {
356 Self::new()
357 }
358}
359
360unsafe impl<const N:usize> Send for StaticCString<N> {}
361unsafe impl<const N:usize> Sync for StaticCString<N> {}