1use crate::{vec::Vec, CapacityError, LenType};
4use core::{
5 borrow::Borrow,
6 cmp::Ordering,
7 error::Error,
8 ffi::{c_char, CStr, FromBytesWithNulError},
9 fmt,
10 ops::Deref,
11};
12
13#[derive(Clone, Hash)]
17pub struct CString<const N: usize, LenT: LenType = usize> {
18 inner: Vec<u8, N, LenT>,
19}
20
21impl<const N: usize, LenT: LenType> CString<N, LenT> {
22 pub fn new() -> Self {
35 const {
36 assert!(N > 0);
37 }
38
39 let mut inner = Vec::new();
40
41 unsafe { inner.push_unchecked(b'\0') };
43
44 Self { inner }
45 }
46
47 pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> Result<Self, CapacityError> {
68 let mut inner = Vec::new();
69
70 inner.extend_from_slice(bytes)?;
71
72 Ok(Self { inner })
73 }
74
75 pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<Self, ExtendError> {
81 let mut string = Self::new();
82
83 string.extend_from_bytes(bytes)?;
84
85 Ok(string)
86 }
87
88 pub unsafe fn from_raw(ptr: *const c_char) -> Result<Self, ExtendError> {
115 Self::from_bytes_with_nul(unsafe { CStr::from_ptr(ptr).to_bytes_with_nul() })
117 }
118
119 #[inline]
121 pub fn as_c_str(&self) -> &CStr {
122 unsafe { CStr::from_bytes_with_nul_unchecked(&self.inner) }
123 }
124
125 fn capacity_with_bytes(&self, bytes: &[u8]) -> Option<usize> {
127 match bytes.last() {
128 None => None,
129 Some(0) if bytes.len() < 2 => None,
130 Some(0) => {
131 Some(self.inner.len() + bytes.len() - 1)
135 }
136 Some(_) => {
137 Some(self.inner.len() + bytes.len())
143 }
144 }
145 }
146
147 pub fn extend_from_bytes(&mut self, bytes: &[u8]) -> Result<(), ExtendError> {
165 let Some(capacity) = self.capacity_with_bytes(bytes) else {
166 return Ok(());
167 };
168
169 if capacity > N {
170 return Err(CapacityError.into());
172 }
173
174 match CStr::from_bytes_with_nul(bytes) {
175 Ok(_) => {
176 unsafe { self.extend_from_bytes_unchecked(bytes) }?;
178
179 Ok(())
180 }
181 Err(FromBytesWithNulError::InteriorNul { position }) => {
182 Err(ExtendError::InteriorNul { position })
183 }
184 Err(FromBytesWithNulError::NotNulTerminated) => {
185 unsafe {
193 self.extend_from_bytes_unchecked(bytes).unwrap();
194 self.inner.push_unchecked(0);
195 };
196
197 Ok(())
198 }
199 }
200 }
201
202 #[inline]
208 unsafe fn pop_terminator(&mut self) {
209 debug_assert_eq!(self.inner.last(), Some(&0));
210
211 unsafe { self.inner.pop_unchecked() };
213 }
214
215 unsafe fn extend_from_bytes_unchecked(
223 &mut self,
224 additional: &[u8],
225 ) -> Result<(), CapacityError> {
226 unsafe { self.pop_terminator() }
228
229 self.inner.extend_from_slice(additional)
230 }
231
232 #[inline]
245 pub fn as_bytes_with_nul(&self) -> &[u8] {
246 &self.inner
247 }
248
249 #[inline]
262 pub fn as_bytes(&self) -> &[u8] {
263 &self.inner[..self.inner.len() - 1]
264 }
265}
266
267impl<const N: usize, LenT: LenType> AsRef<CStr> for CString<N, LenT> {
268 #[inline]
269 fn as_ref(&self) -> &CStr {
270 self.as_c_str()
271 }
272}
273
274impl<const N: usize, LenT: LenType> Borrow<CStr> for CString<N, LenT> {
275 #[inline]
276 fn borrow(&self) -> &CStr {
277 self.as_c_str()
278 }
279}
280
281impl<const N: usize, LenT: LenType> Default for CString<N, LenT> {
282 #[inline]
283 fn default() -> Self {
284 Self::new()
285 }
286}
287
288impl<const N: usize, LenT: LenType> Deref for CString<N, LenT> {
289 type Target = CStr;
290
291 #[inline]
292 fn deref(&self) -> &Self::Target {
293 self.as_c_str()
294 }
295}
296
297impl<const N: usize, const M: usize, LenT1: LenType, LenT2: LenType> PartialEq<CString<M, LenT2>>
298 for CString<N, LenT1>
299{
300 #[inline]
301 fn eq(&self, rhs: &CString<M, LenT2>) -> bool {
302 self.as_c_str() == rhs.as_c_str()
303 }
304}
305
306impl<const N: usize, LenT: LenType> Eq for CString<N, LenT> {}
307
308impl<const N: usize, const M: usize, LenT1: LenType, LenT2: LenType> PartialOrd<CString<M, LenT2>>
309 for CString<N, LenT1>
310{
311 #[inline]
312 fn partial_cmp(&self, rhs: &CString<M, LenT2>) -> Option<Ordering> {
313 self.as_c_str().partial_cmp(rhs.as_c_str())
314 }
315}
316
317impl<const N: usize, LenT: LenType> Ord for CString<N, LenT> {
318 #[inline]
319 fn cmp(&self, rhs: &Self) -> Ordering {
320 self.as_c_str().cmp(rhs.as_c_str())
321 }
322}
323
324impl<const N: usize, LenT: LenType> fmt::Debug for CString<N, LenT> {
325 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
326 self.as_c_str().fmt(f)
327 }
328}
329
330#[derive(Debug)]
332pub enum ExtendError {
333 Capacity(CapacityError),
335 InteriorNul {
337 position: usize,
339 },
340}
341
342impl Error for ExtendError {}
343
344impl fmt::Display for ExtendError {
345 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
346 match self {
347 Self::Capacity(error) => write!(f, "{error}"),
348 Self::InteriorNul { position } => write!(f, "interior nul byte at {position}"),
349 }
350 }
351}
352
353impl From<CapacityError> for ExtendError {
354 fn from(error: CapacityError) -> Self {
355 Self::Capacity(error)
356 }
357}
358
359#[cfg(test)]
360mod tests {
361 use super::*;
362
363 #[test]
364 fn empty() {
365 let empty = CString::<1>::new();
366
367 assert_eq!(empty.as_c_str(), c"");
368 assert_eq!(empty.as_bytes(), &[]);
369 assert_eq!(empty.to_str(), Ok(""));
370 }
371
372 #[test]
373 fn create_with_capacity_error() {
374 assert!(CString::<1>::from_bytes_with_nul(b"a\0").is_err());
375 }
376
377 #[test]
378 fn extend_no_byte() {
379 let mut c_string = CString::<1>::new();
380
381 c_string.extend_from_bytes(b"").unwrap();
382 }
383
384 #[test]
385 fn extend_from_bytes() {
386 let mut c_string = CString::<11>::new();
387 assert_eq!(c_string.to_str(), Ok(""));
388
389 c_string.extend_from_bytes(b"hello").unwrap();
390
391 assert_eq!(c_string.to_str(), Ok("hello"));
392
393 assert!(matches!(
395 c_string.extend_from_bytes(b"w\0rld"),
396 Err(ExtendError::InteriorNul { position: 1 })
397 ));
398
399 assert_eq!(c_string.to_str(), Ok("hello"));
401
402 assert!(matches!(
404 c_string.extend_from_bytes(b" world"),
405 Err(ExtendError::Capacity(CapacityError))
406 ));
407
408 assert_eq!(c_string.to_str(), Ok("hello"));
411
412 c_string.extend_from_bytes(b" Bill").unwrap();
413
414 assert_eq!(c_string.to_str(), Ok("hello Bill"));
415 }
416
417 #[test]
418 fn calculate_capacity_with_additional_bytes() {
419 const INITIAL_BYTES: &[u8] = b"abc";
420
421 let mut c_string = CString::<5>::new();
422
423 c_string.extend_from_bytes(INITIAL_BYTES).unwrap();
424
425 assert_eq!(c_string.to_bytes_with_nul().len(), 4);
426 assert_eq!(c_string.capacity_with_bytes(b""), None);
427 assert_eq!(c_string.capacity_with_bytes(b"\0"), None);
428 assert_eq!(
429 c_string.capacity_with_bytes(b"d"),
430 Some(INITIAL_BYTES.len() + 2)
431 );
432 assert_eq!(
433 c_string.capacity_with_bytes(b"d\0"),
434 Some(INITIAL_BYTES.len() + 2)
435 );
436 assert_eq!(
437 c_string.capacity_with_bytes(b"defg"),
438 Some(INITIAL_BYTES.len() + 5)
439 );
440 assert_eq!(
441 c_string.capacity_with_bytes(b"defg\0"),
442 Some(INITIAL_BYTES.len() + 5)
443 );
444 }
445 #[test]
446 fn default() {
447 assert_eq!(CString::<1>::default().as_c_str(), c"");
448 }
449
450 #[test]
451 fn deref() {
452 assert_eq!(CString::<1>::new().deref(), c"");
453 assert_eq!(CString::<2>::new().deref(), c"");
454 assert_eq!(CString::<3>::new().deref(), c"");
455
456 let mut string = CString::<2>::new();
457 string.extend_from_bytes(&[65]).unwrap();
458
459 assert_eq!(string.deref(), c"A");
460
461 let mut string = CString::<3>::new();
462 string.extend_from_bytes(&[65, 66]).unwrap();
463
464 assert_eq!(string.deref(), c"AB");
465
466 let mut string = CString::<4>::new();
467 string.extend_from_bytes(&[65, 66, 67]).unwrap();
468
469 assert_eq!(string.deref(), c"ABC");
470 }
471
472 #[test]
473 fn as_ref() {
474 let mut string = CString::<4>::new();
475 string.extend_from_bytes(b"foo").unwrap();
476 assert_eq!(string.as_ref(), c"foo");
477 }
478
479 #[test]
480 fn borrow() {
481 let mut string = CString::<4>::new();
482 string.extend_from_bytes(b"foo").unwrap();
483 assert_eq!(Borrow::<CStr>::borrow(&string), c"foo");
484 }
485
486 mod equality {
487 use super::*;
488
489 #[test]
490 fn c_string() {
491 assert!(CString::<1>::new() == CString::<1>::new());
493 assert!(CString::<1>::new() == CString::<2>::new());
494 assert!(CString::<1>::from_bytes_with_nul(b"\0").unwrap() == CString::<3>::new());
495
496 assert!(
498 CString::<2>::from_bytes_with_nul(b"a\0").unwrap()
499 == CString::<2>::from_bytes_with_nul(b"a\0").unwrap()
500 );
501 assert!(
502 CString::<2>::from_bytes_with_nul(b"a\0").unwrap()
503 == CString::<3>::from_bytes_with_nul(b"a\0").unwrap()
504 );
505 assert!(
506 CString::<2>::from_bytes_with_nul(b"a\0").unwrap()
507 != CString::<2>::from_bytes_with_nul(b"b\0").unwrap()
508 );
509
510 assert!(
512 CString::<4>::from_bytes_with_nul(b"abc\0").unwrap()
513 == CString::<4>::from_bytes_with_nul(b"abc\0").unwrap()
514 );
515 assert!(
516 CString::<3>::from_bytes_with_nul(b"ab\0").unwrap()
517 != CString::<4>::from_bytes_with_nul(b"abc\0").unwrap()
518 );
519 }
520 }
521
522 mod ordering {
523 use super::*;
524
525 #[test]
526 fn c_string() {
527 assert_eq!(
528 CString::<1>::new().partial_cmp(&CString::<1>::new()),
529 Some(Ordering::Equal)
530 );
531 assert_eq!(
532 CString::<2>::from_bytes_with_nul(b"a\0")
533 .unwrap()
534 .partial_cmp(&CString::<2>::from_bytes_with_nul(b"b\0").unwrap()),
535 Some(Ordering::Less)
536 );
537 assert_eq!(
538 CString::<2>::from_bytes_with_nul(b"b\0")
539 .unwrap()
540 .partial_cmp(&CString::<2>::from_bytes_with_nul(b"a\0").unwrap()),
541 Some(Ordering::Greater)
542 );
543 }
544
545 #[test]
546 fn c_str() {
547 assert_eq!(c"".partial_cmp(&CString::<1>::new()), Some(Ordering::Equal));
548 assert_eq!(
549 c"a".partial_cmp(&CString::<2>::from_bytes_with_nul(b"b\0").unwrap()),
550 Some(Ordering::Less)
551 );
552 assert_eq!(
553 c"b".partial_cmp(&CString::<2>::from_bytes_with_nul(b"a\0").unwrap()),
554 Some(Ordering::Greater)
555 );
556 }
557 }
558}