• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * This file is derived from src/ffi/c_str.rs in the Rust standard library, used
3  * under the Apache License, Version 2.0. The following is the original
4  * copyright information from the Rust project:
5  *
6  * Copyrights in the Rust project are retained by their contributors. No
7  * copyright assignment is required to contribute to the Rust project.
8  *
9  * Some files include explicit copyright notices and/or license notices.
10  * For full authorship information, see the version control history or
11  * https://thanks.rust-lang.org
12  *
13  * Except as otherwise noted (below and/or in individual files), Rust is
14  * licensed under the Apache License, Version 2.0 <LICENSE-APACHE> or
15  * <http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
16  * <LICENSE-MIT> or <http://opensource.org/licenses/MIT>, at your option.
17  *
18  *
19  * Licensed under the Apache License, Version 2.0 (the "License");
20  * you may not use this file except in compliance with the License.
21  * You may obtain a copy of the License at
22  *
23  *      http://www.apache.org/licenses/LICENSE-2.0
24  *
25  * Unless required by applicable law or agreed to in writing, software
26  * distributed under the License is distributed on an "AS IS" BASIS,
27  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
28  * See the License for the specific language governing permissions and
29  * limitations under the License.
30  */
31 
32 //! Implementation of CString and CStr for use in Trusty.
33 //!
34 //! This module is a lightly modified version of `ffi/c_str.rs` from the Rust
35 //! std crate. `CString::new()` is replaced by the fallible allocating
36 //! [`CString::try_new()`] and other APIs which can allocate infallibly are
37 //! removed.
38 
39 use crate::alloc::{AllocError, TryAllocInto};
40 use crate::TryClone;
41 use alloc::ffi::CString;
42 use alloc::vec::Vec;
43 use core::slice::memchr;
44 
45 #[derive(PartialEq, Eq, Debug)]
46 pub enum TryNewError {
47     /// An error indicating that an interior nul byte was found.
48     ///
49     /// While Rust strings may contain nul bytes in the middle, C strings
50     /// can't, as that byte would effectively truncate the string.
51     ///
52     /// This error is created by the [`CString::try_new`] method.
53     /// See its documentation for more.
54     ///
55     /// # Examples
56     ///
57     /// ```
58     /// use std::ffi::{CString, NulError};
59     ///
60     /// let _: NulError = CString::new(b"f\0oo".to_vec()).unwrap_err();
61     /// ```
62     NulError(usize, Vec<u8>),
63 
64     AllocError,
65 }
66 
67 impl From<AllocError> for TryNewError {
from(_err: AllocError) -> Self68     fn from(_err: AllocError) -> Self {
69         TryNewError::AllocError
70     }
71 }
72 
73 pub trait FallibleCString {
74     /// Creates a new C-compatible string from a container of bytes.
75     ///
76     /// This function will consume the provided data and use the
77     /// underlying bytes to construct a new string, ensuring that
78     /// there is a trailing 0 byte. This trailing 0 byte will be
79     /// appended by this function; the provided data should *not*
80     /// contain any 0 bytes in it.
81     ///
82     /// # Examples
83     ///
84     /// ```ignore (extern-declaration)
85     /// use std::ffi::CString;
86     /// use std::os::raw::c_char;
87     ///
88     /// extern "C" { fn puts(s: *const c_char); }
89     ///
90     /// let to_print = CString::new("Hello!").expect("CString::new failed");
91     /// unsafe {
92     ///     puts(to_print.as_ptr());
93     /// }
94     /// ```
95     ///
96     /// # Errors
97     ///
98     /// This function will return an error if the supplied bytes contain an
99     /// internal 0 byte. The [`TryNewError::NulError`] returned will contain the bytes as well as
100     /// the position of the nul byte.
try_new<T: TryAllocInto<Vec<u8>>>(t: T) -> Result<CString, TryNewError>101     fn try_new<T: TryAllocInto<Vec<u8>>>(t: T) -> Result<CString, TryNewError>;
102 
103     /// Creates a C-compatible string by consuming a byte vector,
104     /// without checking for interior 0 bytes.
105     ///
106     /// This method is equivalent to [`CString::try_new`] except that no runtime
107     /// assertion is made that `v` contains no 0 bytes, and it requires an
108     /// actual byte vector, not anything that can be converted to one with Into.
109     ///
110     /// # Examples
111     ///
112     /// ```
113     /// use std::ffi::CString;
114     ///
115     /// let raw = b"foo".to_vec();
116     /// unsafe {
117     ///     let c_string = CString::from_vec_unchecked(raw);
118     /// }
119     /// ```
try_from_vec_unchecked(v: Vec<u8>) -> Result<CString, AllocError>120     unsafe fn try_from_vec_unchecked(v: Vec<u8>) -> Result<CString, AllocError>;
121 }
122 
123 impl FallibleCString for CString {
try_new<T: TryAllocInto<Vec<u8>>>(t: T) -> Result<CString, TryNewError>124     fn try_new<T: TryAllocInto<Vec<u8>>>(t: T) -> Result<CString, TryNewError> {
125         trait SpecIntoVec {
126             fn into_vec(self) -> Result<Vec<u8>, AllocError>;
127         }
128         impl<T: TryAllocInto<Vec<u8>>> SpecIntoVec for T {
129             default fn into_vec(self) -> Result<Vec<u8>, AllocError> {
130                 self.try_alloc_into()
131             }
132         }
133         // Specialization for avoiding reallocation.
134         impl SpecIntoVec for &'_ [u8] {
135             fn into_vec(self) -> Result<Vec<u8>, AllocError> {
136                 let mut v = Vec::new();
137                 v.try_reserve_exact(self.len() + 1).or(Err(AllocError))?;
138                 v.extend_from_slice(self);
139                 Ok(v)
140             }
141         }
142         impl SpecIntoVec for &'_ str {
143             fn into_vec(self) -> Result<Vec<u8>, AllocError> {
144                 let mut v = Vec::new();
145                 v.try_reserve_exact(self.len() + 1).or(Err(AllocError))?;
146                 v.extend_from_slice(self.as_bytes());
147                 Ok(v)
148             }
149         }
150 
151         let bytes = SpecIntoVec::into_vec(t)?;
152         match memchr::memchr(0, &bytes) {
153             Some(i) => Err(TryNewError::NulError(i, bytes)),
154             None => Ok(unsafe { CString::try_from_vec_unchecked(bytes)? }),
155         }
156     }
157 
try_from_vec_unchecked(mut v: Vec<u8>) -> Result<CString, AllocError>158     unsafe fn try_from_vec_unchecked(mut v: Vec<u8>) -> Result<CString, AllocError> {
159         v.try_reserve_exact(1).or(Err(AllocError))?;
160         v.push(0);
161         Ok(CString::from_vec_with_nul_unchecked(v))
162     }
163 }
164 
165 impl TryClone for CString {
166     type Error = AllocError;
167 
try_clone(&self) -> Result<Self, Self::Error>168     fn try_clone(&self) -> Result<Self, Self::Error> {
169         let inner = self.as_bytes_with_nul().try_alloc_into()?;
170 
171         // SAFETY: The `Vec` used here was cloned directly from an existing `CString`,
172         // and so upholds the invariants required.
173         Ok(unsafe { CString::from_vec_with_nul_unchecked(inner) })
174     }
175 }
176