rusqlite/util/
small_cstr.rs1use smallvec::{smallvec, SmallVec};
2use std::ffi::{CStr, CString, NulError};
3
4#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
8pub struct SmallCString(SmallVec<[u8; 16]>);
9
10impl SmallCString {
11 #[inline]
12 pub fn new(s: &str) -> Result<Self, NulError> {
13 if s.as_bytes().contains(&0_u8) {
14 return Err(Self::fabricate_nul_error(s));
15 }
16 let mut buf = SmallVec::with_capacity(s.len() + 1);
17 buf.extend_from_slice(s.as_bytes());
18 buf.push(0);
19 let res = Self(buf);
20 res.debug_checks();
21 Ok(res)
22 }
23
24 #[inline]
25 pub fn as_str(&self) -> &str {
26 self.debug_checks();
27 unsafe { std::str::from_utf8_unchecked(self.as_bytes_without_nul()) }
29 }
30
31 #[inline]
36 pub fn as_bytes_without_nul(&self) -> &[u8] {
37 self.debug_checks();
38 &self.0[..self.len()]
39 }
40
41 #[inline]
44 pub fn as_bytes_with_nul(&self) -> &[u8] {
45 self.debug_checks();
46 &self.0
47 }
48
49 #[inline]
50 #[cfg(debug_assertions)]
51 fn debug_checks(&self) {
52 debug_assert_ne!(self.0.len(), 0);
53 debug_assert_eq!(self.0[self.0.len() - 1], 0);
54 let strbytes = &self.0[..(self.0.len() - 1)];
55 debug_assert!(!strbytes.contains(&0));
56 debug_assert!(std::str::from_utf8(strbytes).is_ok());
57 }
58
59 #[inline]
60 #[cfg(not(debug_assertions))]
61 fn debug_checks(&self) {}
62
63 #[inline]
64 pub fn len(&self) -> usize {
65 debug_assert_ne!(self.0.len(), 0);
66 self.0.len() - 1
67 }
68
69 #[inline]
70 #[allow(unused)] pub fn is_empty(&self) -> bool {
72 self.len() == 0
73 }
74
75 #[inline]
76 pub fn as_cstr(&self) -> &CStr {
77 let bytes = self.as_bytes_with_nul();
78 debug_assert!(CStr::from_bytes_with_nul(bytes).is_ok());
79 unsafe { CStr::from_bytes_with_nul_unchecked(bytes) }
80 }
81
82 #[cold]
83 fn fabricate_nul_error(b: &str) -> NulError {
84 CString::new(b).unwrap_err()
85 }
86}
87
88impl Default for SmallCString {
89 #[inline]
90 fn default() -> Self {
91 Self(smallvec![0])
92 }
93}
94
95impl std::fmt::Debug for SmallCString {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 f.debug_tuple("SmallCString").field(&self.as_str()).finish()
98 }
99}
100
101impl std::ops::Deref for SmallCString {
102 type Target = CStr;
103
104 #[inline]
105 fn deref(&self) -> &CStr {
106 self.as_cstr()
107 }
108}
109
110impl std::borrow::Borrow<str> for SmallCString {
111 #[inline]
112 fn borrow(&self) -> &str {
113 self.as_str()
114 }
115}
116
117#[cfg(test)]
118mod test {
119 use super::*;
120
121 #[test]
122 fn test_small_cstring() {
123 assert_eq!(SmallCString::default().0, SmallCString::new("").unwrap().0);
126 assert_eq!(SmallCString::new("foo").unwrap().len(), 3);
127 assert_eq!(
128 SmallCString::new("foo").unwrap().as_bytes_with_nul(),
129 b"foo\0"
130 );
131 assert_eq!(
132 SmallCString::new("foo").unwrap().as_bytes_without_nul(),
133 b"foo",
134 );
135
136 assert_eq!(SmallCString::new("😀").unwrap().len(), 4);
137 assert_eq!(
138 SmallCString::new("😀").unwrap().0.as_slice(),
139 b"\xf0\x9f\x98\x80\0",
140 );
141 assert_eq!(
142 SmallCString::new("😀").unwrap().as_bytes_without_nul(),
143 b"\xf0\x9f\x98\x80",
144 );
145
146 assert_eq!(SmallCString::new("").unwrap().len(), 0);
147 assert!(SmallCString::new("").unwrap().is_empty());
148
149 assert_eq!(SmallCString::new("").unwrap().0.as_slice(), b"\0");
150 assert_eq!(SmallCString::new("").unwrap().as_bytes_without_nul(), b"");
151
152 SmallCString::new("\0").unwrap_err();
153 SmallCString::new("\0abc").unwrap_err();
154 SmallCString::new("abc\0").unwrap_err();
155 }
156}