1use crate::pool::Pool;
3use std::ffi::{c_char, CStr, CString};
4use std::marker::PhantomData;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
11pub struct BStr<'a> {
12 data: &'a [u8],
13 _pool: PhantomData<&'a Pool<'a>>,
14}
15
16impl<'a> BStr<'a> {
17 pub unsafe fn from_ptr(ptr: *const c_char) -> Self {
24 if ptr.is_null() {
25 BStr {
26 data: &[],
27 _pool: PhantomData,
28 }
29 } else {
30 let cstr = CStr::from_ptr(ptr);
31 BStr {
32 data: cstr.to_bytes(),
33 _pool: PhantomData,
34 }
35 }
36 }
37
38 pub fn as_bytes(&self) -> &[u8] {
40 self.data
41 }
42
43 pub fn to_str(&self) -> Result<&str, std::str::Utf8Error> {
45 std::str::from_utf8(self.data)
46 }
47
48 pub fn to_string_lossy(&self) -> std::borrow::Cow<'_, str> {
50 String::from_utf8_lossy(self.data)
51 }
52
53 pub fn is_empty(&self) -> bool {
55 self.data.is_empty()
56 }
57
58 pub fn len(&self) -> usize {
60 self.data.len()
61 }
62}
63
64impl<'a> AsRef<[u8]> for BStr<'a> {
65 fn as_ref(&self) -> &[u8] {
66 self.data
67 }
68}
69
70impl<'a> std::ops::Deref for BStr<'a> {
71 type Target = [u8];
72
73 fn deref(&self) -> &Self::Target {
74 self.data
75 }
76}
77
78impl<'a> std::fmt::Display for BStr<'a> {
79 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80 write!(f, "{}", String::from_utf8_lossy(self.data))
81 }
82}
83
84impl<'a> From<&'a [u8]> for BStr<'a> {
85 fn from(data: &'a [u8]) -> Self {
86 BStr {
87 data,
88 _pool: PhantomData,
89 }
90 }
91}
92
93impl<'a> From<&'a str> for BStr<'a> {
94 fn from(s: &'a str) -> Self {
95 BStr {
96 data: s.as_bytes(),
97 _pool: PhantomData,
98 }
99 }
100}
101
102impl<'a> std::borrow::Borrow<[u8]> for BStr<'a> {
103 fn borrow(&self) -> &[u8] {
104 self.data
105 }
106}
107
108impl<'a> PartialEq<&str> for BStr<'a> {
109 fn eq(&self, other: &&str) -> bool {
110 self.data == other.as_bytes()
111 }
112}
113
114impl<'a> PartialEq<str> for BStr<'a> {
115 fn eq(&self, other: &str) -> bool {
116 self.data == other.as_bytes()
117 }
118}
119
120impl<'a> PartialEq<&[u8]> for BStr<'a> {
121 fn eq(&self, other: &&[u8]) -> bool {
122 self.data == *other
123 }
124}
125
126#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
128pub struct BStrUtf8<'a> {
129 data: &'a str,
130 _pool: PhantomData<&'a Pool<'a>>,
131}
132
133impl<'a> BStrUtf8<'a> {
134 pub unsafe fn from_ptr(ptr: *const c_char) -> Result<Self, std::str::Utf8Error> {
141 if ptr.is_null() {
142 Ok(BStrUtf8 {
143 data: "",
144 _pool: PhantomData,
145 })
146 } else {
147 let cstr = CStr::from_ptr(ptr);
148 let s = cstr.to_str()?;
149 Ok(BStrUtf8 {
150 data: s,
151 _pool: PhantomData,
152 })
153 }
154 }
155
156 pub fn as_str(&self) -> &str {
158 self.data
159 }
160
161 pub fn is_empty(&self) -> bool {
163 self.data.is_empty()
164 }
165
166 pub fn len(&self) -> usize {
168 self.data.len()
169 }
170}
171
172impl<'a> AsRef<str> for BStrUtf8<'a> {
173 fn as_ref(&self) -> &str {
174 self.data
175 }
176}
177
178impl<'a> std::ops::Deref for BStrUtf8<'a> {
179 type Target = str;
180
181 fn deref(&self) -> &Self::Target {
182 self.data
183 }
184}
185
186impl<'a> std::fmt::Display for BStrUtf8<'a> {
187 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188 write!(f, "{}", self.data)
189 }
190}
191
192impl<'a> From<&'a str> for BStrUtf8<'a> {
193 fn from(data: &'a str) -> Self {
194 BStrUtf8 {
195 data,
196 _pool: PhantomData,
197 }
198 }
199}
200
201impl<'a> TryFrom<&'a [u8]> for BStrUtf8<'a> {
202 type Error = std::str::Utf8Error;
203
204 fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
205 let s = std::str::from_utf8(data)?;
206 Ok(BStrUtf8 {
207 data: s,
208 _pool: PhantomData,
209 })
210 }
211}
212
213impl<'a> std::borrow::Borrow<str> for BStrUtf8<'a> {
214 fn borrow(&self) -> &str {
215 self.data
216 }
217}
218
219impl<'a> PartialEq<&str> for BStrUtf8<'a> {
220 fn eq(&self, other: &&str) -> bool {
221 self.data == *other
222 }
223}
224
225impl<'a> PartialEq<String> for BStrUtf8<'a> {
226 fn eq(&self, other: &String) -> bool {
227 self.data == other.as_str()
228 }
229}
230
231pub struct PoolString<'a> {
233 ptr: *const c_char,
234 _marker: PhantomData<&'a Pool<'a>>,
235}
236
237impl<'a> PoolString<'a> {
238 pub fn as_ptr(&self) -> *const c_char {
240 self.ptr
241 }
242
243 pub fn as_bstr(&self) -> BStr<'a> {
245 unsafe { BStr::from_ptr(self.ptr) }
246 }
247
248 pub fn as_str(&self) -> Result<&str, std::str::Utf8Error> {
250 unsafe {
251 let cstr = CStr::from_ptr(self.ptr);
252 cstr.to_str()
253 }
254 }
255
256 pub fn as_bytes(&self) -> &[u8] {
258 unsafe {
259 let cstr = CStr::from_ptr(self.ptr);
260 cstr.to_bytes()
261 }
262 }
263
264 pub fn len(&self) -> usize {
266 self.as_bstr().len()
267 }
268
269 pub fn is_empty(&self) -> bool {
271 self.len() == 0
272 }
273}
274
275impl<'a> std::fmt::Display for PoolString<'a> {
276 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
277 match self.as_str() {
278 Ok(s) => write!(f, "{}", s),
279 Err(_) => write!(f, "{:?}", self.as_bytes()),
280 }
281 }
282}
283
284impl<'a> std::fmt::Debug for PoolString<'a> {
285 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
286 match self.as_str() {
287 Ok(s) => write!(f, "PoolString({:?})", s),
288 Err(_) => write!(f, "PoolString({:?})", self.as_bytes()),
289 }
290 }
291}
292
293pub fn pstrdup<'a>(s: &str, pool: &'a Pool) -> Result<PoolString<'a>, std::ffi::NulError> {
295 let cstring = CString::new(s)?;
296 let ptr = unsafe { apr_sys::apr_pstrdup(pool.as_mut_ptr(), cstring.as_ptr()) };
297 Ok(PoolString {
298 ptr,
299 _marker: PhantomData,
300 })
301}
302
303pub fn pstrdup_raw(s: &str, pool: &Pool<'_>) -> Result<*const c_char, std::ffi::NulError> {
305 Ok(pstrdup(s, pool)?.as_ptr())
306}
307
308pub fn make_cstring<'a>(s: &str, pool: &'a Pool) -> Result<&'a CStr, std::ffi::NulError> {
323 let ptr = pstrdup_raw(s, pool)?;
324 Ok(unsafe { CStr::from_ptr(ptr) })
325}
326
327pub fn pstrndup<'a>(
329 s: &str,
330 n: usize,
331 pool: &'a Pool,
332) -> Result<PoolString<'a>, std::ffi::NulError> {
333 let cstring = CString::new(s)?;
334 let ptr = unsafe { apr_sys::apr_pstrndup(pool.as_mut_ptr(), cstring.as_ptr(), n) };
335 Ok(PoolString {
336 ptr,
337 _marker: PhantomData,
338 })
339}
340
341pub fn pmemdup<'a>(data: &[u8], pool: &'a Pool) -> &'a [u8] {
345 unsafe {
346 let ptr = apr_sys::apr_pmemdup(
347 pool.as_mut_ptr(),
348 data.as_ptr() as *const std::ffi::c_void,
349 data.len(),
350 ) as *const u8;
351 std::slice::from_raw_parts(ptr, data.len())
352 }
353}
354
355#[cfg(test)]
359mod tests {
360 use super::*;
361
362 #[test]
363 fn test_bstr() {
364 let test_str = "Hello, world!";
365 let cstring = CString::new(test_str).unwrap();
366
367 unsafe {
368 let bstr = BStr::from_ptr(cstring.as_ptr());
369 assert_eq!(bstr.as_bytes(), test_str.as_bytes());
370 assert_eq!(bstr.to_str().unwrap(), test_str);
371 assert!(!bstr.is_empty());
372 assert_eq!(bstr.len(), test_str.len());
373 }
374 }
375
376 #[test]
377 fn test_bstr_utf8() {
378 let test_str = "Hello, 世界!";
379 let cstring = CString::new(test_str).unwrap();
380
381 unsafe {
382 let bstr_utf8 = BStrUtf8::from_ptr(cstring.as_ptr()).unwrap();
383 assert_eq!(bstr_utf8.as_str(), test_str);
384 assert!(!bstr_utf8.is_empty());
385 assert_eq!(bstr_utf8.len(), test_str.len());
386 }
387 }
388
389 #[test]
390 fn test_pool_string_operations() {
391 let pool = Pool::new();
392
393 let pooled = pstrdup("test string", &pool).unwrap();
394 assert_eq!(pooled.as_str().unwrap(), "test string");
395 assert_eq!(pooled.len(), 11);
396 assert!(!pooled.is_empty());
397
398 let bstr = pooled.as_bstr();
399 assert_eq!(bstr.to_str().unwrap(), "test string");
400
401 let data = b"binary data";
403 let copied = pmemdup(data, &pool);
404 assert_eq!(copied, data);
405 }
406
407 #[test]
408 fn test_make_cstring() {
409 let pool = Pool::new();
410
411 let cstr = make_cstring("hello world", &pool).unwrap();
413 assert_eq!(cstr.to_str().unwrap(), "hello world");
414 assert_eq!(cstr.to_bytes(), b"hello world");
415
416 let empty_cstr = make_cstring("", &pool).unwrap();
418 assert_eq!(empty_cstr.to_str().unwrap(), "");
419 assert_eq!(empty_cstr.to_bytes(), b"");
420
421 let unicode_cstr = make_cstring("Hello, 世界!", &pool).unwrap();
423 assert_eq!(unicode_cstr.to_str().unwrap(), "Hello, 世界!");
424
425 let result = make_cstring("hello\0world", &pool);
427 assert!(result.is_err());
428 }
429
430 #[test]
431 fn test_pool_string_display() {
432 let pool = Pool::new();
433 let pooled = pstrdup("hello", &pool).unwrap();
434 assert_eq!(format!("{}", pooled), "hello");
435 assert_eq!(format!("{:?}", pooled), "PoolString(\"hello\")");
436 }
437
438 #[test]
439 fn test_bstr_traits() {
440 let data = b"hello world";
441 let bstr = BStr::from(&data[..]);
442
443 let bstr2 = bstr;
445 #[allow(clippy::clone_on_copy)]
447 let bstr3 = bstr.clone();
448 assert_eq!(bstr, bstr2);
449 assert_eq!(bstr2, bstr3);
450
451 assert_eq!(format!("{}", bstr), "hello world");
453 assert_eq!(bstr.len(), 11);
454 assert_eq!(&bstr[0..5], b"hello");
455
456 let from_str = BStr::from("test");
458 assert_eq!(from_str.as_bytes(), b"test");
459 }
460
461 #[test]
462 fn test_bstr_utf8_traits() {
463 let s = "hello 🦀";
464 let bstr_utf8 = BStrUtf8::from(s);
465
466 let bstr2 = bstr_utf8;
468 assert_eq!(bstr_utf8, bstr2);
469
470 assert_eq!(format!("{}", bstr_utf8), "hello 🦀");
472 assert_eq!(bstr_utf8.len(), 10); let from_bytes = BStrUtf8::try_from("hello".as_bytes()).unwrap();
476 assert_eq!(from_bytes.as_str(), "hello");
477
478 let invalid = BStrUtf8::try_from(&[0xFF, 0xFF][..]);
480 assert!(invalid.is_err());
481 }
482
483 #[test]
484 fn test_advanced_string_traits() {
485 let bstr = BStr::from("hello");
487 assert_eq!(bstr, "hello");
488 assert_eq!(bstr, "hello");
489 assert_eq!(bstr, &b"hello"[..]);
490
491 let borrowed: &[u8] = std::borrow::Borrow::borrow(&bstr);
493 assert_eq!(borrowed, b"hello");
494
495 let bstr_utf8 = BStrUtf8::from("hello");
497 assert_eq!(bstr_utf8, "hello");
498 assert_eq!(bstr_utf8, String::from("hello"));
499
500 let borrowed: &str = std::borrow::Borrow::borrow(&bstr_utf8);
502 assert_eq!(borrowed, "hello");
503 }
504}