]> git.lizzy.rs Git - rust.git/blob - src/librustc_data_structures/small_c_str.rs
Rollup merge of #60187 - tmandry:generator-optimization, r=eddyb
[rust.git] / src / librustc_data_structures / small_c_str.rs
1 use std::ffi;
2 use std::ops::Deref;
3
4 use smallvec::SmallVec;
5
6 const SIZE: usize = 36;
7
8 /// Like SmallVec but for C strings.
9 #[derive(Clone)]
10 pub struct SmallCStr {
11     data: SmallVec<[u8; SIZE]>,
12 }
13
14 impl SmallCStr {
15     #[inline]
16     pub fn new(s: &str) -> SmallCStr {
17         let len = s.len();
18         let len1 = len + 1;
19         let data = if len < SIZE {
20             let mut buf = [0; SIZE];
21             buf[..len].copy_from_slice(s.as_bytes());
22             SmallVec::from_buf_and_len(buf, len1)
23         } else {
24             let mut data = Vec::with_capacity(len1);
25             data.extend_from_slice(s.as_bytes());
26             data.push(0);
27             SmallVec::from_vec(data)
28         };
29         if let Err(e) = ffi::CStr::from_bytes_with_nul(&data) {
30             panic!("The string \"{}\" cannot be converted into a CStr: {}", s, e);
31         }
32         SmallCStr { data }
33     }
34
35     #[inline]
36     pub fn new_with_nul(s: &str) -> SmallCStr {
37         let b = s.as_bytes();
38         if let Err(e) = ffi::CStr::from_bytes_with_nul(b) {
39             panic!("The string \"{}\" cannot be converted into a CStr: {}", s, e);
40         }
41         SmallCStr { data: SmallVec::from_slice(s.as_bytes()) }
42     }
43
44
45     #[inline]
46     pub fn as_c_str(&self) -> &ffi::CStr {
47         unsafe {
48             ffi::CStr::from_bytes_with_nul_unchecked(&self.data[..])
49         }
50     }
51
52     #[inline]
53     pub fn len_with_nul(&self) -> usize {
54         self.data.len()
55     }
56
57     pub fn spilled(&self) -> bool {
58         self.data.spilled()
59     }
60 }
61
62 impl Deref for SmallCStr {
63     type Target = ffi::CStr;
64
65     fn deref(&self) -> &ffi::CStr {
66         self.as_c_str()
67     }
68 }
69
70 #[test]
71 fn short() {
72     const TEXT: &str = "abcd";
73     let reference = ffi::CString::new(TEXT.to_string()).unwrap();
74
75     let scs = SmallCStr::new(TEXT);
76
77     assert_eq!(scs.len_with_nul(), TEXT.len() + 1);
78     assert_eq!(scs.as_c_str(), reference.as_c_str());
79     assert!(!scs.spilled());
80 }
81
82 #[test]
83 fn empty() {
84     const TEXT: &str = "";
85     let reference = ffi::CString::new(TEXT.to_string()).unwrap();
86
87     let scs = SmallCStr::new(TEXT);
88
89     assert_eq!(scs.len_with_nul(), TEXT.len() + 1);
90     assert_eq!(scs.as_c_str(), reference.as_c_str());
91     assert!(!scs.spilled());
92 }
93
94 #[test]
95 fn long() {
96     const TEXT: &str = "01234567890123456789012345678901234567890123456789\
97                         01234567890123456789012345678901234567890123456789\
98                         01234567890123456789012345678901234567890123456789";
99     let reference = ffi::CString::new(TEXT.to_string()).unwrap();
100
101     let scs = SmallCStr::new(TEXT);
102
103     assert_eq!(scs.len_with_nul(), TEXT.len() + 1);
104     assert_eq!(scs.as_c_str(), reference.as_c_str());
105     assert!(scs.spilled());
106 }
107
108 #[test]
109 #[should_panic]
110 fn internal_nul() {
111     let _ = SmallCStr::new("abcd\0def");
112 }