Thanks to visit codestin.com
Credit goes to docs.rs

c_fixed_string/
lib.rs

1//! The goal of this crate is to provide a safe wrapper around the notion of
2//! a C-style string with a predefined maximum length, known as the *limit*.
3//! 
4//! CFixedString and CFixedStr are the owning and non-owning variants
5//! respectively.
6extern crate memchr;
7
8use std::os::raw::c_char;
9use std::{slice, str, fmt, ascii, ops, ptr, mem};
10use std::borrow::{Cow,Borrow};
11use std::hash::{Hash, Hasher};
12use std::ffi::{CStr, CString};
13use std::cmp::Ordering;
14use std::error::Error;
15
16use memchr::memchr;
17
18#[derive(Clone, PartialEq, Eq, Debug)]
19pub struct IntoStringError {
20    inner: CFixedString,
21    error: str::Utf8Error,
22}
23
24impl IntoStringError {
25    /// Consumes this error, returning original [`CFixedString`] which generated the
26    /// error.
27    ///
28    /// [`CFixedString`]: struct.CFixedString.html
29    pub fn into_c_fixed_string(self) -> CFixedString {
30        self.inner
31    }
32
33    /// Access the underlying UTF-8 error that was the cause of this error.
34    pub fn utf8_error(&self) -> str::Utf8Error {
35        self.error
36    }
37}
38
39impl Error for IntoStringError {
40    fn description(&self) -> &str {
41        "C fixed string contained non-utf8 bytes"
42    }
43
44    fn cause(&self) -> Option<&Error> {
45        Some(&self.error)
46    }
47}
48
49impl fmt::Display for IntoStringError {
50    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
51        self.description().fmt(f)
52    }
53}
54
55/// This type owns a fixed length buffer containing a C-style string.
56/// The string is terminated by the first null byte in the buffer, or by the
57/// length of the buffer if no null byte is present.
58///
59/// The length of the buffer is known as the *limit* of the CFixedString.
60/// The `len()` method returns the length of the string within the buffer.
61///
62/// Any bytes after the first null byte are not considered part of the string
63/// and may contain garbage data. When comparing or hashing CFixedStrings,
64/// these bytes are not included.
65///
66/// # Performance
67///
68/// Be aware that many of the methods on this type which would normally be
69/// expected to run in constant time may run in time linear in the length of
70/// the string. For this reason, it is recommended to use the type only for
71/// interop with C code, and to convert to a standard string or byte buffer
72/// as soon as possible.
73pub struct CFixedString {
74    inner: Box<[u8]>
75}
76
77impl CFixedString {
78    /// Construct a new fixed-length C string from a byte slice
79    /// The first null byte terminates the string, but the length
80    /// of the slice determines the limit of this fixed-size string.
81    pub fn new<T: Into<Box<[u8]>>>(t: T) -> CFixedString {
82        CFixedString { inner: t.into() }
83    }
84    /// Construct an empty string with the specified limit. The buffer
85    /// is entirely zero-initialized.
86    pub fn with_limit(limit: usize) -> CFixedString {
87        CFixedString { inner: vec![0; limit].into_boxed_slice() }
88    }
89    /// Retakes ownership of a CFixedString that was transferred to C.
90    ///
91    /// # Safety
92    ///
93    /// This should only ever be called with a pointer and limit that was earlier
94    /// obtained from a CFixedString. The previous string must have been forgotten
95    /// to ensure that the memory has not already been freed.
96    pub unsafe fn from_raw_parts(ptr: *mut c_char, limit: usize) -> CFixedString {
97        CFixedString {
98            inner: Box::from_raw(slice::from_raw_parts_mut(ptr as *mut u8, limit))
99        }
100    }
101    /// Converts the CFixedString into a String if it contains valid Unicode data.
102    ///
103    /// On failure, ownership of the original CFixedString is returned.
104    pub fn into_string(self) -> Result<String, IntoStringError> {
105        String::from_utf8(self.into_bytes())
106            .map_err(|e| IntoStringError {
107                error: e.utf8_error(),
108                inner: CFixedString::new(e.into_bytes()),
109            })
110    }
111    /// Converts the CFixedString into a CString. If necessary, the buffer will be
112    /// extended to make room for a final null byte. If the buffer does not need
113    /// to be extended, no allocation will be performed.
114    pub fn into_c_string(self) -> CString {
115        unsafe { CString::from_vec_unchecked(self.into_bytes()) }
116    }
117    /// Converts the CFixedString into a byte buffer. The length of the buffer will
118    /// equal the length of the string up to but not including the first null byte.
119    /// The capacity of the buffer will equal the limit of the CFixedString.
120    pub fn into_bytes(self) -> Vec<u8> {
121        let l = self.len();
122        let mut v = self.into_inner().into_vec();
123        v.truncate(l);
124        v
125    }
126    /// Converts the CFixedString into a byte buffer. The length of the buffer will
127    /// equal the limit of the CFixedString. The buffer may contain garbage
128    /// values after the first null byte.
129    pub fn into_bytes_full(self) -> Vec<u8> {
130        self.into_inner().into_vec()
131    }
132    /// Extracts a CFixedStr slice containing the entire buffer.
133    pub fn as_c_fixed_str(&self) -> &CFixedStr {
134        &*self
135    }
136    /// Extracts a mutable CFixedStr slice containing the entire buffer.
137    pub fn as_c_fixed_str_mut(&mut self) -> &mut CFixedStr {
138        &mut *self
139    }
140    /// Converts this CFixedString into a boxed CFixedStr
141    pub fn into_boxed_c_fixed_str(self) -> Box<CFixedStr> {
142        unsafe { Box::from_raw(Box::into_raw(self.into_inner()) as *mut CFixedStr) }
143    }
144    fn into_inner(self) -> Box<[u8]> {
145        unsafe {
146            let result = ptr::read(&self.inner);
147            mem::forget(self);
148            result
149        }
150    }
151}
152
153impl Drop for CFixedString {
154    #[inline]
155    fn drop(&mut self) {
156        if let Some(c) = self.inner.first_mut() {
157            *c = 0
158        }
159    }
160}
161
162impl ops::Deref for CFixedString {
163    type Target = CFixedStr;
164
165    #[inline]
166    fn deref(&self) -> &CFixedStr {
167        CFixedStr::from_bytes(&self.inner)
168    }
169}
170
171impl ops::DerefMut for CFixedString {
172    #[inline]
173    fn deref_mut(&mut self) -> &mut CFixedStr {
174        CFixedStr::from_bytes_mut(&mut self.inner)
175    }
176}
177
178impl fmt::Debug for CFixedString {
179    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
180        fmt::Debug::fmt(&**self, f)
181    }
182}
183
184impl Clone for CFixedString {
185    fn clone(&self) -> Self {
186        CFixedString::new(self.as_bytes_full())
187    }
188}
189
190impl Hash for CFixedString {
191    fn hash<H: Hasher>(&self, state: &mut H) {
192        (**self).hash(state);
193    }
194}
195
196impl PartialEq for CFixedString {
197    fn eq(&self, other: &CFixedString) -> bool {
198        (**self).eq(other)
199    }
200}
201
202impl Eq for CFixedString {}
203
204impl PartialOrd for CFixedString {
205    fn partial_cmp(&self, other: &CFixedString) -> Option<Ordering> {
206        (**self).partial_cmp(other)
207    }
208}
209
210impl Ord for CFixedString {
211    fn cmp(&self, other: &CFixedString) -> Ordering {
212        (**self).cmp(other)
213    }
214}
215
216impl From<CFixedString> for Vec<u8> {
217    #[inline]
218    fn from(s: CFixedString) -> Vec<u8> {
219        s.into_bytes()
220    }
221}
222
223/// This type represents a view of a fixed length buffer containing a C-style string.
224/// The string is terminated by the first null byte in the buffer, or by the
225/// length of the buffer if no null byte is present.
226/// 
227/// The length of the buffer is known as the *limit* of the CFixedStr.
228/// The `len()` method returns the length of the string within the buffer.
229/// 
230/// Any bytes after the first null byte are not considered part of the string
231/// and may contain garbage data. When comparing or hashing CFixedStrs,
232/// these bytes are not included.
233///
234/// # Performance
235/// 
236/// Be aware that many of the methods on this type which would normally be
237/// expected to run in constant time may run in time linear in the length of
238/// the string. For this reason, it is recommended to use the type only for
239/// interop with C code, and to convert to a standard string or byte buffer
240/// as soon as possible.
241pub struct CFixedStr {
242    inner: [u8]
243}
244
245impl CFixedStr {
246    /// Cast a raw C-style buffer to a CFixedStr.
247    pub unsafe fn from_ptr<'a>(ptr: *const c_char, limit: usize) -> &'a CFixedStr {
248        Self::from_bytes(slice::from_raw_parts(ptr as *const u8, limit))
249    }
250    /// Cast a raw C-style buffer to a mutable CFixedStr.
251    pub unsafe fn from_mut_ptr<'a>(ptr: *mut c_char, limit: usize) -> &'a mut CFixedStr {
252        Self::from_bytes_mut(slice::from_raw_parts_mut(ptr as *mut u8, limit))
253    }
254    /// Create a CFixedStr from a string slice.
255    pub fn from_str(s: &str) -> &CFixedStr {
256        Self::from_bytes(s.as_bytes())
257    }
258    /// Create a CFixedStr from a byte slice.
259    pub fn from_bytes(bytes: &[u8]) -> &CFixedStr {
260        unsafe { &*(bytes as *const [u8] as *const CFixedStr) }
261    }
262    /// Create a CFixedStr from a mutable byte slice.
263    pub fn from_bytes_mut(bytes: &mut [u8]) -> &mut CFixedStr {
264        unsafe { &mut *(bytes as *mut [u8] as *mut CFixedStr) }
265    }
266    /// Create a CFixedStr from a variable length CStr.
267    pub fn from_c_str(c_str: &CStr) -> &CFixedStr {
268        Self::from_bytes(c_str.to_bytes_with_nul())
269    }
270    /// Returns the inner pointer to this CFixedStr.
271    ///
272    /// # Safety
273    /// 
274    /// It is your responsibility to make sure the underlying memory is not
275    /// freed to early.
276    pub fn as_ptr(&self) -> *const c_char {
277        self.inner.as_ptr() as *const c_char
278    }
279    /// Returns the inner pointer to this mutable CFixedStr.
280    ///
281    /// # Safety
282    /// 
283    /// It is your responsibility to make sure the underlying memory is not
284    /// freed to early.
285    pub fn as_mut_ptr(&mut self) -> *mut c_char {
286        self.inner.as_mut_ptr() as *mut c_char
287    }
288    /// Returns the limit of this CFixedStr. This corresponds to the longest
289    /// possible string that could be stored here.
290    pub fn limit(&self) -> usize {
291        self.inner.len()
292    }
293    /// Returns the length of the CFixedStr. This operation takes linear time.
294    pub fn len(&self) -> usize {
295        memchr(0, &self.inner).unwrap_or(self.limit())
296    }
297    /// Coverts this CFixedStr to a byte slice. The length of the slice is equal
298    /// to the length of the string up to but not including the first null byte.
299    pub fn to_bytes(&self) -> &[u8] {
300        &self.inner[0..self.len()]
301    }
302    /// Coverts this CFixedStr to a mutable byte slice. The length of the slice is equal
303    /// to the length of the string up to but not including the first null byte.
304    pub fn to_bytes_mut(&mut self) -> &mut [u8] {
305        let l = self.len();
306        &mut self.inner[0..l]
307    }
308    /// Coverts this CFixedStr to a byte slice. The length of the slice is equal
309    /// to the length of the string up to and including the first null byte, if it exists.
310    pub fn to_bytes_extended(&self) -> &[u8] {
311        if let Some(l) = memchr(0, &self.inner) {
312            &self.inner[0 .. l+1]
313        } else {
314            &self.inner
315        }
316    }
317    /// Coverts this CFixedStr to a mutable byte slice. The length of the slice is equal
318    /// to the length of the string up to and including the first null byte, if it exists.
319    pub fn to_bytes_mut_extended(&mut self) -> &mut [u8] {
320        if let Some(l) = memchr(0, &self.inner) {
321            &mut self.inner[0 .. l+1]
322        } else {
323            &mut self.inner
324        }
325    }
326    /// Coverts this CFixedStr to a byte slice. The length of the slice is equal
327    /// to the limit of the CFixedStr.
328    pub fn as_bytes_full(&self) -> &[u8] {
329        &self.inner
330    }
331    /// Coverts this CFixedStr to a mutable byte slice. The length of the slice is equal
332    /// to the limit of the CFixedStr.
333    pub fn as_bytes_mut_full(&mut self) -> &mut [u8] {
334        &mut self.inner
335    }
336    /// Yields a &str slice if the CFixedStr contains valid UTF-8.
337    ///
338    /// This function will calculate the length of this string and check for UTF-8 validity,
339    /// and then return the &str if it's valid.
340    pub fn to_str(&self) -> Result<&str, str::Utf8Error> {
341        str::from_utf8(self.to_bytes())
342    }
343    /// Converts a CFixedStr into a Cow<str>.
344    ///
345    /// This function will calculate the length of this string and then return the resulting
346    /// slice as a `Cow<str>`, replacing any invalid UTF-8 sequences with `U+FFFD REPLACEMENT CHARACTER`.
347    pub fn to_string_lossy(&self) -> Cow<str> {
348        String::from_utf8_lossy(self.to_bytes())
349    }
350    /// Converts a CFixedStr into a Cow<CStr>.
351    ///
352    /// This function will calculate the length of this string, and then ensure it has a
353    /// terminating null byte. If a null byte is already present, the `Borrowed` variant
354    /// can be returned.
355    pub fn to_c_str(&self) -> Cow<CStr> {
356        if let Some(l) = memchr(0, &self.inner) {
357            Cow::Borrowed(unsafe { CStr::from_bytes_with_nul_unchecked(&self.inner[0 .. l+1]) })
358        } else {
359            let mut v = Vec::with_capacity(self.inner.len() + 1);
360            v.extend(&self.inner);
361            Cow::Owned(unsafe { CString::from_vec_unchecked(v) })
362        }
363    }
364    /// Converts a `Box<CFixedStr>` into a `CFixedString` without copying or allocating.
365    pub fn into_c_fixed_string(self: Box<CFixedStr>) -> CFixedString {
366        let raw = Box::into_raw(self) as *mut [u8];
367        CFixedString { inner: unsafe { Box::from_raw(raw) } }
368    }
369}
370
371impl Hash for CFixedStr {
372    fn hash<H: Hasher>(&self, state: &mut H) {
373        self.to_bytes().hash(state);
374    }
375}
376
377impl fmt::Debug for CFixedStr {
378    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
379        write!(f, "\"")?;
380        for byte in self.to_bytes().iter().flat_map(|&b| ascii::escape_default(b)) {
381            fmt::Write::write_char(f, byte as char)?;
382        }
383        write!(f, "\"")
384    }
385}
386
387impl<'a> Default for &'a CFixedStr {
388    fn default() -> &'a CFixedStr {
389        const SLICE: &'static [u8] = &[0];
390        CFixedStr::from_bytes(SLICE)
391    }
392}
393
394impl Default for CFixedString {
395    fn default() -> CFixedString {
396        CFixedString { inner: Default::default() }
397    }
398}
399
400impl Borrow<CFixedStr> for CFixedString {
401    #[inline]
402    fn borrow(&self) -> &CFixedStr { self }
403}
404
405impl PartialEq for CFixedStr {
406    fn eq(&self, other: &CFixedStr) -> bool {
407        self.to_bytes().eq(other.to_bytes())
408    }
409}
410
411impl Eq for CFixedStr {}
412
413impl PartialOrd for CFixedStr {
414    fn partial_cmp(&self, other: &CFixedStr) -> Option<Ordering> {
415        self.to_bytes().partial_cmp(&other.to_bytes())
416    }
417}
418
419impl Ord for CFixedStr {
420    fn cmp(&self, other: &CFixedStr) -> Ordering {
421        self.to_bytes().cmp(&other.to_bytes())
422    }
423}
424
425impl ToOwned for CFixedStr {
426    type Owned = CFixedString;
427
428    fn to_owned(&self) -> CFixedString {
429        CFixedString { inner: self.to_bytes_extended().into() }
430    }
431}
432
433impl<'a> From<&'a CFixedStr> for CFixedString {
434    fn from(s: &'a CFixedStr) -> CFixedString {
435        s.to_owned()
436    }
437}
438
439impl ops::Index<ops::RangeFull> for CFixedString {
440    type Output = CFixedStr;
441
442    #[inline]
443    fn index(&self, _index: ops::RangeFull) -> &CFixedStr {
444        self
445    }
446}
447
448impl ops::IndexMut<ops::RangeFull> for CFixedString {
449    #[inline]
450    fn index_mut(&mut self, _index: ops::RangeFull) -> &mut CFixedStr {
451        self
452    }
453}
454
455impl AsRef<CFixedStr> for CFixedStr {
456    #[inline]
457    fn as_ref(&self) -> &CFixedStr {
458        self
459    }
460}
461
462impl AsRef<CFixedStr> for CFixedString {
463    #[inline]
464    fn as_ref(&self) -> &CFixedStr {
465        self
466    }
467}
468
469#[cfg(test)]
470mod tests {
471    use super::*;
472    #[test]
473    fn it_works() {
474        assert_eq!(&CFixedString::new(&b"hello,\0world!\0"[..])[..], CFixedStr::from_str("hello,"));
475    }
476}