]> git.lizzy.rs Git - rust.git/blob - src/librustc_data_structures/small_c_str.rs
Auto merge of #52994 - varkor:trim_direction, r=alexcrichton
[rust.git] / src / librustc_data_structures / small_c_str.rs
1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use std::ffi;
12 use std::ops::Deref;
13
14 use smallvec::SmallVec;
15
16 const SIZE: usize = 36;
17
18 /// Like SmallVec but for C strings.
19 #[derive(Clone)]
20 pub struct SmallCStr {
21     data: SmallVec<[u8; SIZE]>,
22 }
23
24 impl SmallCStr {
25     #[inline]
26     pub fn new(s: &str) -> SmallCStr {
27         let len = s.len();
28         let len1 = len + 1;
29         let data = if len < SIZE {
30             let mut buf = [0; SIZE];
31             buf[..len].copy_from_slice(s.as_bytes());
32             SmallVec::from_buf_and_len(buf, len1)
33         } else {
34             let mut data = Vec::with_capacity(len1);
35             data.extend_from_slice(s.as_bytes());
36             data.push(0);
37             SmallVec::from_vec(data)
38         };
39         if let Err(e) = ffi::CStr::from_bytes_with_nul(&data) {
40             panic!("The string \"{}\" cannot be converted into a CStr: {}", s, e);
41         }
42         SmallCStr { data }
43     }
44
45     #[inline]
46     pub fn new_with_nul(s: &str) -> SmallCStr {
47         let b = s.as_bytes();
48         if let Err(e) = ffi::CStr::from_bytes_with_nul(b) {
49             panic!("The string \"{}\" cannot be converted into a CStr: {}", s, e);
50         }
51         SmallCStr { data: SmallVec::from_slice(s.as_bytes()) }
52     }
53
54
55     #[inline]
56     pub fn as_c_str(&self) -> &ffi::CStr {
57         unsafe {
58             ffi::CStr::from_bytes_with_nul_unchecked(&self.data[..])
59         }
60     }
61
62     #[inline]
63     pub fn len_with_nul(&self) -> usize {
64         self.data.len()
65     }
66
67     pub fn spilled(&self) -> bool {
68         self.data.spilled()
69     }
70 }
71
72 impl Deref for SmallCStr {
73     type Target = ffi::CStr;
74
75     fn deref(&self) -> &ffi::CStr {
76         self.as_c_str()
77     }
78 }
79
80 #[test]
81 fn short() {
82     const TEXT: &str = "abcd";
83     let reference = ffi::CString::new(TEXT.to_string()).unwrap();
84
85     let scs = SmallCStr::new(TEXT);
86
87     assert_eq!(scs.len_with_nul(), TEXT.len() + 1);
88     assert_eq!(scs.as_c_str(), reference.as_c_str());
89     assert!(!scs.spilled());
90 }
91
92 #[test]
93 fn empty() {
94     const TEXT: &str = "";
95     let reference = ffi::CString::new(TEXT.to_string()).unwrap();
96
97     let scs = SmallCStr::new(TEXT);
98
99     assert_eq!(scs.len_with_nul(), TEXT.len() + 1);
100     assert_eq!(scs.as_c_str(), reference.as_c_str());
101     assert!(!scs.spilled());
102 }
103
104 #[test]
105 fn long() {
106     const TEXT: &str = "01234567890123456789012345678901234567890123456789\
107                         01234567890123456789012345678901234567890123456789\
108                         01234567890123456789012345678901234567890123456789";
109     let reference = ffi::CString::new(TEXT.to_string()).unwrap();
110
111     let scs = SmallCStr::new(TEXT);
112
113     assert_eq!(scs.len_with_nul(), TEXT.len() + 1);
114     assert_eq!(scs.as_c_str(), reference.as_c_str());
115     assert!(scs.spilled());
116 }
117
118 #[test]
119 #[should_panic]
120 fn internal_nul() {
121     let _ = SmallCStr::new("abcd\0def");
122 }