1use crate::ffi_util::CStrLike;
2
3use std::ffi::{CStr, CString};
4use std::ptr;
5
6#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
11#[repr(transparent)]
12pub struct PropName(CStr);
13
14impl PropName {
15 pub(crate) const fn new_unwrap(value: &str) -> &Self {
21 let Some((&0, bytes)) = value.as_bytes().split_last() else {
22 panic!("input was not nul-terminated");
23 };
24 let mut idx = 0;
25 while idx < bytes.len() {
26 assert!(bytes[idx] != 0, "input contained interior nul byte");
27 idx += 1;
28 }
29
30 unsafe {
34 let value = CStr::from_bytes_with_nul_unchecked(value.as_bytes());
35 &*(ptr::from_ref::<CStr>(value) as *const Self)
36 }
37 }
38
39 #[inline]
41 pub fn as_c_str(&self) -> &CStr {
42 &self.0
43 }
44
45 #[inline]
50 pub fn as_str(&self) -> &str {
51 unsafe { std::str::from_utf8_unchecked(self.0.to_bytes()) }
53 }
54}
55
56impl core::ops::Deref for PropName {
57 type Target = CStr;
58
59 #[inline]
60 fn deref(&self) -> &Self::Target {
61 self.as_c_str()
62 }
63}
64
65impl core::convert::AsRef<CStr> for PropName {
66 #[inline]
67 fn as_ref(&self) -> &CStr {
68 self.as_c_str()
69 }
70}
71
72impl core::convert::AsRef<str> for PropName {
73 #[inline]
74 fn as_ref(&self) -> &str {
75 self.as_str()
76 }
77}
78
79impl std::borrow::ToOwned for PropName {
80 type Owned = PropertyName;
81
82 #[inline]
83 fn to_owned(&self) -> Self::Owned {
84 PropertyName(self.0.to_owned())
85 }
86
87 #[inline]
88 fn clone_into(&self, target: &mut Self::Owned) {
89 self.0.clone_into(&mut target.0);
90 }
91}
92
93impl core::fmt::Display for PropName {
94 #[inline]
95 fn fmt(&self, fmtr: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
96 self.as_str().fmt(fmtr)
97 }
98}
99
100impl core::fmt::Debug for PropName {
101 #[inline]
102 fn fmt(&self, fmtr: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
103 self.as_str().fmt(fmtr)
104 }
105}
106
107impl core::cmp::PartialEq<CStr> for PropName {
108 #[inline]
109 fn eq(&self, other: &CStr) -> bool {
110 self.as_c_str().eq(other)
111 }
112}
113
114impl core::cmp::PartialEq<str> for PropName {
115 #[inline]
116 fn eq(&self, other: &str) -> bool {
117 self.as_str().eq(other)
118 }
119}
120
121impl core::cmp::PartialEq<PropName> for CStr {
122 #[inline]
123 fn eq(&self, other: &PropName) -> bool {
124 self.eq(other.as_c_str())
125 }
126}
127
128impl core::cmp::PartialEq<PropName> for str {
129 #[inline]
130 fn eq(&self, other: &PropName) -> bool {
131 self.eq(other.as_str())
132 }
133}
134
135impl<'a> CStrLike for &'a PropName {
136 type Baked = &'a CStr;
137 type Error = std::convert::Infallible;
138
139 #[inline]
140 fn bake(self) -> Result<Self::Baked, Self::Error> {
141 Ok(&self.0)
142 }
143
144 #[inline]
145 fn into_c_string(self) -> Result<CString, Self::Error> {
146 Ok(self.0.to_owned())
147 }
148}
149
150#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
155#[repr(transparent)]
156pub struct PropertyName(CString);
157
158impl PropertyName {
159 #[inline]
162 unsafe fn from_vec_with_nul_unchecked(inner: Vec<u8>) -> Self {
163 Self(unsafe { CString::from_vec_with_nul_unchecked(inner) })
165 }
166
167 #[inline]
169 pub fn into_c_string(self) -> CString {
170 self.0
171 }
172
173 #[inline]
178 pub fn into_string(self) -> String {
179 unsafe { String::from_utf8_unchecked(self.0.into_bytes()) }
181 }
182}
183
184impl std::ops::Deref for PropertyName {
185 type Target = PropName;
186
187 #[inline]
188 fn deref(&self) -> &Self::Target {
189 unsafe { &*(ptr::from_ref::<CStr>(self.0.as_c_str()) as *const PropName) }
193 }
194}
195
196impl core::convert::AsRef<CStr> for PropertyName {
197 #[inline]
198 fn as_ref(&self) -> &CStr {
199 self.as_c_str()
200 }
201}
202
203impl core::convert::AsRef<str> for PropertyName {
204 #[inline]
205 fn as_ref(&self) -> &str {
206 self.as_str()
207 }
208}
209
210impl std::borrow::Borrow<PropName> for PropertyName {
211 #[inline]
212 fn borrow(&self) -> &PropName {
213 self
214 }
215}
216
217impl core::fmt::Display for PropertyName {
218 #[inline]
219 fn fmt(&self, fmtr: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
220 self.as_str().fmt(fmtr)
221 }
222}
223
224impl core::fmt::Debug for PropertyName {
225 #[inline]
226 fn fmt(&self, fmtr: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
227 self.as_str().fmt(fmtr)
228 }
229}
230
231impl core::cmp::PartialEq<CString> for PropertyName {
232 #[inline]
233 fn eq(&self, other: &CString) -> bool {
234 self.as_c_str().eq(other.as_c_str())
235 }
236}
237
238impl core::cmp::PartialEq<String> for PropertyName {
239 #[inline]
240 fn eq(&self, other: &String) -> bool {
241 self.as_str().eq(other.as_str())
242 }
243}
244
245impl core::cmp::PartialEq<PropertyName> for CString {
246 #[inline]
247 fn eq(&self, other: &PropertyName) -> bool {
248 self.as_c_str().eq(other.as_c_str())
249 }
250}
251
252impl core::cmp::PartialEq<PropertyName> for String {
253 #[inline]
254 fn eq(&self, other: &PropertyName) -> bool {
255 self.as_str().eq(other.as_str())
256 }
257}
258
259impl CStrLike for PropertyName {
260 type Baked = CString;
261 type Error = std::convert::Infallible;
262
263 #[inline]
264 fn bake(self) -> Result<Self::Baked, Self::Error> {
265 Ok(self.0)
266 }
267
268 #[inline]
269 fn into_c_string(self) -> Result<CString, Self::Error> {
270 Ok(self.0)
271 }
272}
273
274impl<'a> CStrLike for &'a PropertyName {
275 type Baked = &'a CStr;
276 type Error = std::convert::Infallible;
277
278 #[inline]
279 fn bake(self) -> Result<Self::Baked, Self::Error> {
280 Ok(self.as_c_str())
281 }
282
283 #[inline]
284 fn into_c_string(self) -> Result<CString, Self::Error> {
285 Ok(self.0.clone())
286 }
287}
288
289pub(crate) unsafe fn level_property(name: &str, level: usize) -> PropertyName {
297 let bytes = format!("rocksdb.{name}{level}\0").into_bytes();
298 unsafe { PropertyName::from_vec_with_nul_unchecked(bytes) }
301}
302
303#[test]
304fn sanity_checks() {
305 let want = "rocksdb.cfstats-no-file-histogram";
306 assert_eq!(want, crate::properties::CFSTATS_NO_FILE_HISTOGRAM);
307
308 let want = "rocksdb.num-files-at-level5";
309 assert_eq!(want, &*crate::properties::num_files_at_level(5));
310}
311
312#[test]
313#[should_panic(expected = "input contained interior nul byte")]
314fn test_interior_nul() {
315 PropName::new_unwrap("interior nul\0\0");
316}
317
318#[test]
319#[should_panic(expected = "input was not nul-terminated")]
320fn test_non_nul_terminated() {
321 PropName::new_unwrap("no nul terminator");
322}