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#[cfg(feature = "zeroize")]
14use zeroize::Zeroize;
15
16#[derive(Clone, Hash)]
20pub struct CString<const N: usize, LenT: LenType = usize> {
21 inner: Vec<u8, N, LenT>,
22}
23
24#[cfg(feature = "zeroize")]
25impl<const N: usize, LenT: LenType> Zeroize for CString<N, LenT> {
26 fn zeroize(&mut self) {
27 self.inner.zeroize();
28
29 const {
30 assert!(N > 0);
31 }
32
33 unsafe { self.inner.push_unchecked(b'\0') };
35 }
36}
37
38impl<const N: usize, LenT: LenType> CString<N, LenT> {
39 pub fn new() -> Self {
52 const {
53 assert!(N > 0);
54 }
55
56 let mut inner = Vec::new();
57
58 unsafe { inner.push_unchecked(b'\0') };
60
61 Self { inner }
62 }
63
64 pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> Result<Self, CapacityError> {
85 let mut inner = Vec::new();
86
87 inner.extend_from_slice(bytes)?;
88
89 Ok(Self { inner })
90 }
91
92 pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<Self, ExtendError> {
98 let mut string = Self::new();
99
100 string.extend_from_bytes(bytes)?;
101
102 Ok(string)
103 }
104
105 pub unsafe fn from_raw(ptr: *const c_char) -> Result<Self, ExtendError> {
133 Self::from_bytes_with_nul(unsafe { CStr::from_ptr(ptr).to_bytes_with_nul() })
135 }
136
137 #[inline]
139 pub fn as_c_str(&self) -> &CStr {
140 unsafe { CStr::from_bytes_with_nul_unchecked(&self.inner) }
141 }
142
143 fn capacity_with_bytes(&self, bytes: &[u8]) -> Option<usize> {
145 match bytes.last() {
146 None => None,
147 Some(0) if bytes.len() < 2 => None,
148 Some(0) => {
149 Some(self.inner.len() + bytes.len() - 1)
153 }
154 Some(_) => {
155 Some(self.inner.len() + bytes.len())
161 }
162 }
163 }
164
165 pub fn extend_from_bytes(&mut self, bytes: &[u8]) -> Result<(), ExtendError> {
183 let Some(capacity) = self.capacity_with_bytes(bytes) else {
184 return Ok(());
185 };
186
187 if capacity > N {
188 return Err(CapacityError.into());
190 }
191
192 match CStr::from_bytes_with_nul(bytes) {
193 Ok(_) => {
194 unsafe { self.extend_from_bytes_unchecked(bytes) }?;
197
198 Ok(())
199 }
200 Err(FromBytesWithNulError::InteriorNul { position }) => {
201 Err(ExtendError::InteriorNul { position })
202 }
203 Err(FromBytesWithNulError::NotNulTerminated) => {
204 unsafe {
212 self.extend_from_bytes_unchecked(bytes).unwrap();
213 self.inner.push_unchecked(0);
214 };
215
216 Ok(())
217 }
218 }
219 }
220
221 #[inline]
227 unsafe fn pop_terminator(&mut self) {
228 debug_assert_eq!(self.inner.last(), Some(&0));
229
230 unsafe { self.inner.pop_unchecked() };
232 }
233
234 unsafe fn extend_from_bytes_unchecked(
242 &mut self,
243 additional: &[u8],
244 ) -> Result<(), CapacityError> {
245 unsafe { self.pop_terminator() }
247
248 self.inner.extend_from_slice(additional)
249 }
250
251 #[inline]
264 pub fn as_bytes_with_nul(&self) -> &[u8] {
265 &self.inner
266 }
267
268 #[inline]
281 pub fn as_bytes(&self) -> &[u8] {
282 &self.inner[..self.inner.len() - 1]
283 }
284}
285
286impl<const N: usize, LenT: LenType> AsRef<CStr> for CString<N, LenT> {
287 #[inline]
288 fn as_ref(&self) -> &CStr {
289 self.as_c_str()
290 }
291}
292
293impl<const N: usize, LenT: LenType> Borrow<CStr> for CString<N, LenT> {
294 #[inline]
295 fn borrow(&self) -> &CStr {
296 self.as_c_str()
297 }
298}
299
300impl<const N: usize, LenT: LenType> Default for CString<N, LenT> {
301 #[inline]
302 fn default() -> Self {
303 Self::new()
304 }
305}
306
307impl<const N: usize, LenT: LenType> Deref for CString<N, LenT> {
308 type Target = CStr;
309
310 #[inline]
311 fn deref(&self) -> &Self::Target {
312 self.as_c_str()
313 }
314}
315
316impl<const N: usize, const M: usize, LenT1: LenType, LenT2: LenType> PartialEq<CString<M, LenT2>>
317 for CString<N, LenT1>
318{
319 #[inline]
320 fn eq(&self, rhs: &CString<M, LenT2>) -> bool {
321 self.as_c_str() == rhs.as_c_str()
322 }
323}
324
325impl<const N: usize, LenT: LenType> Eq for CString<N, LenT> {}
326
327impl<const N: usize, const M: usize, LenT1: LenType, LenT2: LenType> PartialOrd<CString<M, LenT2>>
328 for CString<N, LenT1>
329{
330 #[inline]
331 fn partial_cmp(&self, rhs: &CString<M, LenT2>) -> Option<Ordering> {
332 self.as_c_str().partial_cmp(rhs.as_c_str())
333 }
334}
335
336impl<const N: usize, LenT: LenType> Ord for CString<N, LenT> {
337 #[inline]
338 fn cmp(&self, rhs: &Self) -> Ordering {
339 self.as_c_str().cmp(rhs.as_c_str())
340 }
341}
342
343impl<const N: usize, LenT: LenType> fmt::Debug for CString<N, LenT> {
344 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345 self.as_c_str().fmt(f)
346 }
347}
348
349#[derive(Debug)]
351pub enum ExtendError {
352 Capacity(CapacityError),
354 InteriorNul {
356 position: usize,
358 },
359}
360
361impl Error for ExtendError {}
362
363impl fmt::Display for ExtendError {
364 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365 match self {
366 Self::Capacity(error) => write!(f, "{error}"),
367 Self::InteriorNul { position } => write!(f, "interior nul byte at {position}"),
368 }
369 }
370}
371
372impl From<CapacityError> for ExtendError {
373 fn from(error: CapacityError) -> Self {
374 Self::Capacity(error)
375 }
376}
377
378#[cfg(test)]
379mod tests {
380 use super::*;
381
382 #[test]
383 fn empty() {
384 let empty = CString::<1>::new();
385
386 assert_eq!(empty.as_c_str(), c"");
387 assert_eq!(empty.as_bytes(), &[]);
388 assert_eq!(empty.to_str(), Ok(""));
389 }
390
391 #[test]
392 fn create_with_capacity_error() {
393 assert!(CString::<1>::from_bytes_with_nul(b"a\0").is_err());
394 }
395
396 #[test]
397 fn extend_no_byte() {
398 let mut c_string = CString::<1>::new();
399
400 c_string.extend_from_bytes(b"").unwrap();
401 }
402
403 #[test]
404 fn extend_from_bytes() {
405 let mut c_string = CString::<11>::new();
406 assert_eq!(c_string.to_str(), Ok(""));
407
408 c_string.extend_from_bytes(b"hello").unwrap();
409
410 assert_eq!(c_string.to_str(), Ok("hello"));
411
412 assert!(matches!(
414 c_string.extend_from_bytes(b"w\0rld"),
415 Err(ExtendError::InteriorNul { position: 1 })
416 ));
417
418 assert_eq!(c_string.to_str(), Ok("hello"));
420
421 assert!(matches!(
423 c_string.extend_from_bytes(b" world"),
424 Err(ExtendError::Capacity(CapacityError))
425 ));
426
427 assert_eq!(c_string.to_str(), Ok("hello"));
430
431 c_string.extend_from_bytes(b" Bill").unwrap();
432
433 assert_eq!(c_string.to_str(), Ok("hello Bill"));
434 }
435
436 #[test]
437 fn calculate_capacity_with_additional_bytes() {
438 const INITIAL_BYTES: &[u8] = b"abc";
439
440 let mut c_string = CString::<5>::new();
441
442 c_string.extend_from_bytes(INITIAL_BYTES).unwrap();
443
444 assert_eq!(c_string.to_bytes_with_nul().len(), 4);
445 assert_eq!(c_string.capacity_with_bytes(b""), None);
446 assert_eq!(c_string.capacity_with_bytes(b"\0"), None);
447 assert_eq!(
448 c_string.capacity_with_bytes(b"d"),
449 Some(INITIAL_BYTES.len() + 2)
450 );
451 assert_eq!(
452 c_string.capacity_with_bytes(b"d\0"),
453 Some(INITIAL_BYTES.len() + 2)
454 );
455 assert_eq!(
456 c_string.capacity_with_bytes(b"defg"),
457 Some(INITIAL_BYTES.len() + 5)
458 );
459 assert_eq!(
460 c_string.capacity_with_bytes(b"defg\0"),
461 Some(INITIAL_BYTES.len() + 5)
462 );
463 }
464 #[test]
465 fn default() {
466 assert_eq!(CString::<1>::default().as_c_str(), c"");
467 }
468
469 #[test]
470 fn deref() {
471 assert_eq!(CString::<1>::new().deref(), c"");
472 assert_eq!(CString::<2>::new().deref(), c"");
473 assert_eq!(CString::<3>::new().deref(), c"");
474
475 let mut string = CString::<2>::new();
476 string.extend_from_bytes(&[65]).unwrap();
477
478 assert_eq!(string.deref(), c"A");
479
480 let mut string = CString::<3>::new();
481 string.extend_from_bytes(&[65, 66]).unwrap();
482
483 assert_eq!(string.deref(), c"AB");
484
485 let mut string = CString::<4>::new();
486 string.extend_from_bytes(&[65, 66, 67]).unwrap();
487
488 assert_eq!(string.deref(), c"ABC");
489 }
490
491 #[test]
492 fn as_ref() {
493 let mut string = CString::<4>::new();
494 string.extend_from_bytes(b"foo").unwrap();
495 assert_eq!(string.as_ref(), c"foo");
496 }
497
498 #[test]
499 fn borrow() {
500 let mut string = CString::<4>::new();
501 string.extend_from_bytes(b"foo").unwrap();
502 assert_eq!(Borrow::<CStr>::borrow(&string), c"foo");
503 }
504
505 #[test]
506 #[cfg(feature = "zeroize")]
507 fn test_cstring_zeroize() {
508 use zeroize::Zeroize;
509
510 let mut c_string = CString::<32>::from_bytes_with_nul(b"sensitive_password\0").unwrap();
511
512 assert_eq!(c_string.to_str(), Ok("sensitive_password"));
513 assert!(!c_string.to_bytes().is_empty());
514 let original_length = c_string.to_bytes().len();
515 assert_eq!(original_length, 18);
516
517 let new_string = CString::<32>::from_bytes_with_nul(b"short\0").unwrap();
518 c_string = new_string;
519
520 assert_eq!(c_string.to_str(), Ok("short"));
521 assert_eq!(c_string.to_bytes().len(), 5);
522
523 c_string.zeroize();
525
526 assert_eq!(c_string.to_bytes().len(), 0);
527 assert_eq!(c_string.to_bytes_with_nul(), &[0]);
528
529 c_string.extend_from_bytes(b"new_data").unwrap();
530 assert_eq!(c_string.to_str(), Ok("new_data"));
531 assert_eq!(c_string.to_bytes().len(), 8);
532 }
533
534 mod equality {
535 use super::*;
536
537 #[test]
538 fn c_string() {
539 assert!(CString::<1>::new() == CString::<1>::new());
541 assert!(CString::<1>::new() == CString::<2>::new());
542 assert!(CString::<1>::from_bytes_with_nul(b"\0").unwrap() == CString::<3>::new());
543
544 assert!(
546 CString::<2>::from_bytes_with_nul(b"a\0").unwrap()
547 == CString::<2>::from_bytes_with_nul(b"a\0").unwrap()
548 );
549 assert!(
550 CString::<2>::from_bytes_with_nul(b"a\0").unwrap()
551 == CString::<3>::from_bytes_with_nul(b"a\0").unwrap()
552 );
553 assert!(
554 CString::<2>::from_bytes_with_nul(b"a\0").unwrap()
555 != CString::<2>::from_bytes_with_nul(b"b\0").unwrap()
556 );
557
558 assert!(
560 CString::<4>::from_bytes_with_nul(b"abc\0").unwrap()
561 == CString::<4>::from_bytes_with_nul(b"abc\0").unwrap()
562 );
563 assert!(
564 CString::<3>::from_bytes_with_nul(b"ab\0").unwrap()
565 != CString::<4>::from_bytes_with_nul(b"abc\0").unwrap()
566 );
567 }
568 }
569
570 mod ordering {
571 use super::*;
572
573 #[test]
574 fn c_string() {
575 assert_eq!(
576 CString::<1>::new().partial_cmp(&CString::<1>::new()),
577 Some(Ordering::Equal)
578 );
579 assert_eq!(
580 CString::<2>::from_bytes_with_nul(b"a\0")
581 .unwrap()
582 .partial_cmp(&CString::<2>::from_bytes_with_nul(b"b\0").unwrap()),
583 Some(Ordering::Less)
584 );
585 assert_eq!(
586 CString::<2>::from_bytes_with_nul(b"b\0")
587 .unwrap()
588 .partial_cmp(&CString::<2>::from_bytes_with_nul(b"a\0").unwrap()),
589 Some(Ordering::Greater)
590 );
591 }
592
593 #[test]
594 fn c_str() {
595 assert_eq!(c"".partial_cmp(&CString::<1>::new()), Some(Ordering::Equal));
596 assert_eq!(
597 c"a".partial_cmp(&CString::<2>::from_bytes_with_nul(b"b\0").unwrap()),
598 Some(Ordering::Less)
599 );
600 assert_eq!(
601 c"b".partial_cmp(&CString::<2>::from_bytes_with_nul(b"a\0").unwrap()),
602 Some(Ordering::Greater)
603 );
604 }
605 }
606}