std/sys/process/unix/common/
cstring_array.rs

1use crate::ffi::{CStr, CString, c_char};
2use crate::ops::Index;
3use crate::{fmt, mem, ptr};
4
5/// Helper type to manage ownership of the strings within a C-style array.
6///
7/// This type manages an array of C-string pointers terminated by a null
8/// pointer. The pointer to the array (as returned by `as_ptr`) can be used as
9/// a value of `argv` or `environ`.
10pub struct CStringArray {
11    ptrs: Vec<*const c_char>,
12}
13
14impl CStringArray {
15    /// Creates a new `CStringArray` with enough capacity to hold `capacity`
16    /// strings.
17    pub fn with_capacity(capacity: usize) -> Self {
18        let mut result = CStringArray { ptrs: Vec::with_capacity(capacity + 1) };
19        result.ptrs.push(ptr::null());
20        result
21    }
22
23    /// Replace the string at position `index`.
24    pub fn write(&mut self, index: usize, item: CString) {
25        let argc = self.ptrs.len() - 1;
26        let ptr = &mut self.ptrs[..argc][index];
27        let old = mem::replace(ptr, item.into_raw());
28        // SAFETY:
29        // `CStringArray` owns all of its strings, and they were all transformed
30        // into pointers using `CString::into_raw`. Also, this is not the null
31        // pointer since the indexing above would have failed.
32        drop(unsafe { CString::from_raw(old.cast_mut()) });
33    }
34
35    /// Push an additional string to the array.
36    pub fn push(&mut self, item: CString) {
37        let argc = self.ptrs.len() - 1;
38        // Replace the null pointer at the end of the array...
39        self.ptrs[argc] = item.into_raw();
40        // ... and recreate it to restore the data structure invariant.
41        self.ptrs.push(ptr::null());
42    }
43
44    /// Returns a pointer to the C-string array managed by this type.
45    pub fn as_ptr(&self) -> *const *const c_char {
46        self.ptrs.as_ptr()
47    }
48
49    /// Returns an iterator over all `CStr`s contained in this array.
50    pub fn iter(&self) -> CStringIter<'_> {
51        CStringIter { iter: self.ptrs[..self.ptrs.len() - 1].iter() }
52    }
53}
54
55impl Index<usize> for CStringArray {
56    type Output = CStr;
57    fn index(&self, index: usize) -> &CStr {
58        let ptr = self.ptrs[..self.ptrs.len() - 1][index];
59        // SAFETY:
60        // `CStringArray` owns all of its strings. Also, this is not the null
61        // pointer since the indexing above would have failed.
62        unsafe { CStr::from_ptr(ptr) }
63    }
64}
65
66impl fmt::Debug for CStringArray {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        f.debug_list().entries(self.iter()).finish()
69    }
70}
71
72// SAFETY: `CStringArray` is basically just a `Vec<CString>`
73unsafe impl Send for CStringArray {}
74// SAFETY: `CStringArray` is basically just a `Vec<CString>`
75unsafe impl Sync for CStringArray {}
76
77impl Drop for CStringArray {
78    fn drop(&mut self) {
79        // SAFETY:
80        // `CStringArray` owns all of its strings, and they were all transformed
81        // into pointers using `CString::into_raw`.
82        self.ptrs[..self.ptrs.len() - 1]
83            .iter()
84            .for_each(|&p| drop(unsafe { CString::from_raw(p.cast_mut()) }))
85    }
86}
87
88/// An iterator over all `CStr`s contained in a `CStringArray`.
89#[derive(Clone)]
90pub struct CStringIter<'a> {
91    iter: crate::slice::Iter<'a, *const c_char>,
92}
93
94impl<'a> Iterator for CStringIter<'a> {
95    type Item = &'a CStr;
96    fn next(&mut self) -> Option<&'a CStr> {
97        // SAFETY:
98        // `CStringArray` owns all of its strings. Also, this is not the null
99        // pointer since the last element is excluded when creating `iter`.
100        self.iter.next().map(|&p| unsafe { CStr::from_ptr(p) })
101    }
102
103    fn size_hint(&self) -> (usize, Option<usize>) {
104        self.iter.size_hint()
105    }
106}
107
108impl<'a> ExactSizeIterator for CStringIter<'a> {
109    fn len(&self) -> usize {
110        self.iter.len()
111    }
112    fn is_empty(&self) -> bool {
113        self.iter.is_empty()
114    }
115}