nc/
c_str.rs

1// Copyright (c) 2020 Xu Shaohua <shaohua@biofan.org>. All rights reserved.
2// Use of this source is governed by Apache-2.0 License that can be found
3// in the LICENSE file.
4
5extern crate alloc;
6extern crate core;
7
8#[cfg(not(feature = "std"))]
9use alloc::boxed::Box;
10#[cfg(not(feature = "std"))]
11use alloc::vec::Vec;
12use core::fmt;
13use core::fmt::Write;
14use core::mem;
15use core::ops;
16use core::ptr;
17
18/// Calculate the length of a string.
19///
20/// ```
21/// use nc::c_str::strlen;
22/// let buf = &[b'h', b'e', b'l', b'l', b'o', 0];
23/// let l = unsafe { strlen(buf.as_ptr() as usize, buf.len()) };
24/// assert_eq!(l, 5);
25/// ```
26///
27/// # Safety
28/// - `buf` is a pointer to raw buffer memory
29/// - `len` should not be larger than length of buffer
30#[must_use]
31pub(crate) unsafe fn strlen(buf: usize, len: usize) -> usize {
32    let buf_ptr = buf as *const u8;
33    for i in 0..len {
34        let chr: u8 = unsafe { *buf_ptr.wrapping_add(i) };
35        if chr == 0 {
36            return i;
37        }
38    }
39    len
40}
41
42pub struct CString {
43    inner: Box<[u8]>,
44}
45
46pub struct CStr {
47    inner: [u8],
48}
49
50impl CString {
51    pub fn new<T: Into<Vec<u8>>>(t: T) -> Self {
52        let mut v = t.into();
53        v.reserve_exact(1);
54        v.push(0);
55        Self {
56            inner: v.into_boxed_slice(),
57        }
58    }
59
60    #[must_use]
61    pub fn with_capacity(cap: usize) -> Self {
62        let mut v: Vec<u8> = vec![0; cap];
63        v.reserve_exact(1);
64        v.push(0);
65        Self {
66            inner: v.into_boxed_slice(),
67        }
68    }
69
70    #[must_use]
71    pub fn into_bytes_with_nul(self) -> Vec<u8> {
72        self.into_inner().into_vec()
73    }
74
75    #[must_use]
76    #[inline]
77    pub fn as_bytes(&self) -> &[u8] {
78        &self.inner[..self.inner.len() - 1]
79    }
80
81    #[must_use]
82    #[inline]
83    pub const fn as_bytes_with_nul(&self) -> &[u8] {
84        &self.inner
85    }
86
87    #[must_use]
88    #[inline]
89    #[allow(clippy::borrow_deref_ref)]
90    pub fn as_c_str(&self) -> &CStr {
91        &*self
92    }
93
94    #[must_use]
95    pub fn into_boxed_c_str(self) -> Box<CStr> {
96        unsafe { Box::from_raw(Box::into_raw(self.into_inner()) as *mut CStr) }
97    }
98
99    #[must_use]
100    fn into_inner(self) -> Box<[u8]> {
101        let this = mem::ManuallyDrop::new(self);
102        unsafe { ptr::read(&this.inner) }
103    }
104
105    #[must_use]
106    #[inline]
107    pub const fn len(&self) -> usize {
108        self.as_bytes_with_nul().len() - 1
109    }
110
111    #[must_use]
112    #[inline]
113    pub const fn is_empty(&self) -> bool {
114        // TODO(Shaohua): Check null bytes
115        self.as_bytes_with_nul().len() == 0
116    }
117
118    #[must_use]
119    pub fn strim_into_bytes(self) -> Vec<u8> {
120        let mut vec = self.into_inner().into_vec();
121        let mut nul_idx = 0;
122        for v in &vec {
123            if v == &0 {
124                break;
125            }
126            nul_idx += 1;
127        }
128        vec.resize(nul_idx, 0);
129        vec
130    }
131
132    #[must_use]
133    pub fn into_bytes(self) -> Vec<u8> {
134        let mut vec = self.into_inner().into_vec();
135        let _nul = vec.pop();
136        vec
137    }
138}
139
140impl CStr {
141    #[must_use]
142    pub const fn as_ptr(&self) -> *const u8 {
143        self.inner.as_ptr()
144    }
145
146    #[inline]
147    #[allow(clippy::missing_const_for_fn)]
148    unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &Self {
149        &*(bytes as *const [u8] as *const Self)
150    }
151
152    #[must_use]
153    pub fn to_bytes(&self) -> &[u8] {
154        let bytes = self.to_bytes_with_nul();
155        &bytes[..bytes.len() - 1]
156    }
157
158    #[must_use]
159    #[allow(clippy::borrow_as_ptr)]
160    #[allow(clippy::missing_const_for_fn)]
161    // NOTE(Shaohua): const unsafe feature is not available in rustc 1.46 stable.
162    pub fn to_bytes_with_nul(&self) -> &[u8] {
163        unsafe { &*(&self.inner as *const [u8]) }
164    }
165}
166
167impl ops::Deref for CString {
168    type Target = CStr;
169
170    #[inline]
171    fn deref(&self) -> &CStr {
172        unsafe { CStr::from_bytes_with_nul_unchecked(self.as_bytes_with_nul()) }
173    }
174}
175
176impl Drop for CString {
177    #[inline]
178    fn drop(&mut self) {
179        unsafe {
180            *self.inner.get_unchecked_mut(0) = 0;
181        }
182    }
183}
184
185impl fmt::Debug for CString {
186    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187        fmt::Debug::fmt(&**self, f)
188    }
189}
190
191impl fmt::Debug for CStr {
192    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193        write!(f, "\"")?;
194        for byte in self.to_bytes() {
195            f.write_char(*byte as char)?;
196        }
197        write!(f, "\"")
198    }
199}
200
201impl From<CString> for Vec<u8> {
202    #[inline]
203    fn from(s: CString) -> Self {
204        s.into_bytes()
205    }
206}