]> git.lizzy.rs Git - rust.git/blob - src/librustc_data_structures/small_c_str.rs
Auto merge of #53080 - hermord:rc-opt, 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 const SIZE: usize = 38;
15
16 /// Like SmallVec but for C strings.
17 #[derive(Clone)]
18 pub enum SmallCStr {
19     OnStack {
20         data: [u8; SIZE],
21         len_with_nul: u8,
22     },
23     OnHeap {
24         data: ffi::CString,
25     }
26 }
27
28 impl SmallCStr {
29     #[inline]
30     pub fn new(s: &str) -> SmallCStr {
31         if s.len() < SIZE {
32             let mut data = [0; SIZE];
33             data[.. s.len()].copy_from_slice(s.as_bytes());
34             let len_with_nul = s.len() + 1;
35
36             // Make sure once that this is a valid CStr
37             if let Err(e) = ffi::CStr::from_bytes_with_nul(&data[.. len_with_nul]) {
38                 panic!("The string \"{}\" cannot be converted into a CStr: {}", s, e);
39             }
40
41             SmallCStr::OnStack {
42                 data,
43                 len_with_nul: len_with_nul as u8,
44             }
45         } else {
46             SmallCStr::OnHeap {
47                 data: ffi::CString::new(s).unwrap()
48             }
49         }
50     }
51
52     #[inline]
53     pub fn as_c_str(&self) -> &ffi::CStr {
54         match *self {
55             SmallCStr::OnStack { ref data, len_with_nul } => {
56                 unsafe {
57                     let slice = &data[.. len_with_nul as usize];
58                     ffi::CStr::from_bytes_with_nul_unchecked(slice)
59                 }
60             }
61             SmallCStr::OnHeap { ref data } => {
62                 data.as_c_str()
63             }
64         }
65     }
66
67     #[inline]
68     pub fn len_with_nul(&self) -> usize {
69         match *self {
70             SmallCStr::OnStack { len_with_nul, .. } => {
71                 len_with_nul as usize
72             }
73             SmallCStr::OnHeap { ref data } => {
74                 data.as_bytes_with_nul().len()
75             }
76         }
77     }
78 }
79
80 impl Deref for SmallCStr {
81     type Target = ffi::CStr;
82
83     fn deref(&self) -> &ffi::CStr {
84         self.as_c_str()
85     }
86 }
87
88
89 #[test]
90 fn short() {
91     const TEXT: &str = "abcd";
92     let reference = ffi::CString::new(TEXT.to_string()).unwrap();
93
94     let scs = SmallCStr::new(TEXT);
95
96     assert_eq!(scs.len_with_nul(), TEXT.len() + 1);
97     assert_eq!(scs.as_c_str(), reference.as_c_str());
98     assert!(if let SmallCStr::OnStack { .. } = scs { true } else { false });
99 }
100
101 #[test]
102 fn empty() {
103     const TEXT: &str = "";
104     let reference = ffi::CString::new(TEXT.to_string()).unwrap();
105
106     let scs = SmallCStr::new(TEXT);
107
108     assert_eq!(scs.len_with_nul(), TEXT.len() + 1);
109     assert_eq!(scs.as_c_str(), reference.as_c_str());
110     assert!(if let SmallCStr::OnStack { .. } = scs { true } else { false });
111 }
112
113 #[test]
114 fn long() {
115     const TEXT: &str = "01234567890123456789012345678901234567890123456789\
116                         01234567890123456789012345678901234567890123456789\
117                         01234567890123456789012345678901234567890123456789";
118     let reference = ffi::CString::new(TEXT.to_string()).unwrap();
119
120     let scs = SmallCStr::new(TEXT);
121
122     assert_eq!(scs.len_with_nul(), TEXT.len() + 1);
123     assert_eq!(scs.as_c_str(), reference.as_c_str());
124     assert!(if let SmallCStr::OnHeap { .. } = scs { true } else { false });
125 }
126
127 #[test]
128 #[should_panic]
129 fn internal_nul() {
130     let _ = SmallCStr::new("abcd\0def");
131 }