1#[cfg(all(not(feature = "std"), feature = "alloc"))]
2use alloc::{borrow::Cow, string::String};
3use core::cmp;
4use core::fmt;
5use core::str::{self, Utf8Error};
6#[cfg(feature = "std")]
7use std::{borrow::Cow, string::String};
8
9use crate::ffi::{ngx_str_t, u_char};
10
11#[macro_export]
17macro_rules! ngx_string {
18 ($s:expr) => {{
19 $crate::ffi::ngx_str_t {
20 len: $s.len() as _,
21 data: concat!($s, "\0").as_ptr() as *mut u8,
22 }
23 }};
24}
25
26#[cfg(feature = "alloc")]
27pub use self::_alloc::NgxString;
28
29#[derive(Hash, PartialEq, Eq, PartialOrd, Ord)]
33#[repr(transparent)]
34pub struct NgxStr([u_char]);
35
36impl NgxStr {
37 pub unsafe fn from_ngx_str<'a>(str: ngx_str_t) -> &'a NgxStr {
47 let bytes: &[u8] = str.as_bytes();
48 &*(bytes as *const [u8] as *const NgxStr)
49 }
50
51 #[inline]
53 pub fn from_bytes(bytes: &[u8]) -> &Self {
54 unsafe { &*(bytes as *const [u8] as *const NgxStr) }
56 }
57
58 #[inline]
60 pub fn from_bytes_mut(bytes: &mut [u8]) -> &mut Self {
61 unsafe { &mut *(bytes as *mut [u8] as *mut NgxStr) }
63 }
64
65 pub fn as_bytes(&self) -> &[u8] {
67 &self.0
68 }
69
70 pub fn to_str(&self) -> Result<&str, Utf8Error> {
72 str::from_utf8(self.as_bytes())
73 }
74
75 #[cfg(feature = "alloc")]
79 pub fn to_string_lossy(&self) -> Cow<'_, str> {
80 String::from_utf8_lossy(self.as_bytes())
81 }
82
83 pub fn is_empty(&self) -> bool {
85 self.0.is_empty()
86 }
87}
88
89impl AsRef<[u8]> for NgxStr {
90 #[inline]
91 fn as_ref(&self) -> &[u8] {
92 self.as_bytes()
93 }
94}
95
96impl AsMut<[u8]> for NgxStr {
97 #[inline]
98 fn as_mut(&mut self) -> &mut [u8] {
99 &mut self.0
100 }
101}
102
103impl fmt::Debug for NgxStr {
104 #[inline]
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 f.write_str("NgxStr(")?;
108 nginx_sys::detail::debug_bytes(f, &self.0)?;
109 f.write_str(")")
110 }
111}
112
113impl Default for &NgxStr {
114 fn default() -> Self {
115 NgxStr::from_bytes(&[])
116 }
117}
118
119impl fmt::Display for NgxStr {
120 #[inline]
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 nginx_sys::detail::display_bytes(f, &self.0)
123 }
124}
125
126macro_rules! impl_partial_ord_eq_from {
127 ($self:ty, $other:ty) => { impl_partial_ord_eq_from!($self, $other;); };
128
129 ($self:ty, $other:ty; $($args:tt)*) => {
130 impl<'a, $($args)*> From<$other> for &'a NgxStr {
131 #[inline]
132 fn from(other: $other) -> Self {
133 let other: &[u8] = other.as_ref();
134 NgxStr::from_bytes(other)
135 }
136 }
137
138 impl_partial_eq!($self, $other; $($args)*);
139 impl_partial_ord!($self, $other; $($args)*);
140 };
141}
142
143macro_rules! impl_partial_eq {
144 ($self:ty, $other:ty) => { impl_partial_eq!($self, $other;); };
145
146 ($self:ty, $other:ty; $($args:tt)*) => {
147 impl<'a, $($args)*> PartialEq<$other> for $self {
148 #[inline]
149 fn eq(&self, other: &$other) -> bool {
150 let other: &[u8] = other.as_ref();
151 PartialEq::eq(self.as_bytes(), other)
152 }
153 }
154
155 impl<'a, $($args)*> PartialEq<$self> for $other {
156 #[inline]
157 fn eq(&self, other: &$self) -> bool {
158 let this: &[u8] = self.as_ref();
159 PartialEq::eq(this, other.as_bytes())
160 }
161 }
162 };
163}
164
165macro_rules! impl_partial_ord {
166 ($self:ty, $other:ty) => { impl_partial_ord!($self, $other;); };
167
168 ($self:ty, $other:ty; $($args:tt)*) => {
169 impl<'a, $($args)*> PartialOrd<$other> for $self {
170 #[inline]
171 fn partial_cmp(&self, other: &$other) -> Option<cmp::Ordering> {
172 let other: &[u8] = other.as_ref();
173 PartialOrd::partial_cmp(self.as_bytes(), other)
174 }
175 }
176
177 impl<'a, $($args)*> PartialOrd<$self> for $other {
178 #[inline]
179 fn partial_cmp(&self, other: &$self) -> Option<cmp::Ordering> {
180 let this: &[u8] = self.as_ref();
181 PartialOrd::partial_cmp(this, other.as_bytes())
182 }
183 }
184 };
185}
186
187impl_partial_eq!(NgxStr, [u8]);
188impl_partial_eq!(NgxStr, [u8; N]; const N: usize);
189impl_partial_eq!(NgxStr, str);
190impl_partial_eq!(NgxStr, ngx_str_t);
191impl_partial_eq!(&'a NgxStr, ngx_str_t);
192impl_partial_ord!(NgxStr, [u8]);
193impl_partial_ord!(NgxStr, [u8; N]; const N: usize);
194impl_partial_ord!(NgxStr, str);
195impl_partial_ord!(NgxStr, ngx_str_t);
196impl_partial_ord!(&'a NgxStr, ngx_str_t);
197impl_partial_ord_eq_from!(NgxStr, &'a [u8]);
198impl_partial_ord_eq_from!(NgxStr, &'a [u8; N]; const N: usize);
199impl_partial_ord_eq_from!(NgxStr, &'a str);
200
201#[cfg(feature = "alloc")]
202mod _alloc {
203 use core::borrow::Borrow;
204 use core::hash;
205 use core::ops;
206 use core::ptr;
207
208 use super::*;
209
210 use crate::allocator::{self, Allocator};
211 use crate::collections::{TryReserveError, Vec};
212
213 #[derive(Clone)]
222 #[repr(transparent)]
223 pub struct NgxString<A>(Vec<u8, A>)
224 where
225 A: Allocator + Clone;
226
227 impl<A> NgxString<A>
228 where
229 A: Allocator + Clone,
230 {
231 pub fn new_in(alloc: A) -> Self {
235 Self(Vec::new_in(alloc))
236 }
237
238 #[inline]
240 pub fn try_from_bytes_in(
241 bytes: impl AsRef<[u8]>,
242 alloc: A,
243 ) -> Result<Self, TryReserveError> {
244 let mut this = Self::new_in(alloc);
245 this.try_reserve_exact(bytes.as_ref().len())?;
246 this.0.extend_from_slice(bytes.as_ref());
247 Ok(this)
248 }
249
250 #[inline]
252 pub fn allocator(&self) -> &A {
253 self.0.allocator()
254 }
255
256 #[inline]
258 pub fn capacity(&self) -> usize {
259 self.0.capacity()
260 }
261
262 #[inline]
264 pub fn is_empty(&self) -> bool {
265 self.0.is_empty()
266 }
267
268 #[inline]
270 pub fn len(&self) -> usize {
271 self.0.len()
272 }
273
274 #[inline]
278 pub fn append_within_capacity(&mut self, other: impl AsRef<[u8]>) -> Result<(), usize> {
279 let other = other.as_ref();
280 if self.0.len() == self.0.capacity() {
281 return Err(other.len());
282 }
283
284 let n = cmp::min(self.0.capacity() - self.0.len(), other.len());
285 unsafe {
286 let p = self.0.as_mut_ptr().add(self.0.len());
292 ptr::copy_nonoverlapping(other.as_ptr(), p, n);
293 self.0.set_len(self.0.len() + n);
294 }
295
296 match other.len() - n {
297 0 => Ok(()),
298 x => Err(x),
299 }
300 }
301
302 #[inline]
304 pub fn try_append(&mut self, other: impl AsRef<[u8]>) -> Result<(), TryReserveError> {
305 let other = other.as_ref();
306 self.0.try_reserve_exact(other.len())?;
307 self.0.extend_from_slice(other);
308 Ok(())
309 }
310
311 #[inline]
313 pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
314 self.0.try_reserve(additional)
315 }
316
317 #[inline]
319 pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
320 self.0.try_reserve_exact(additional)
321 }
322
323 #[inline]
324 pub(crate) fn as_bytes(&self) -> &[u8] {
325 &self.0
326 }
327
328 #[inline]
329 pub(crate) fn as_bytes_mut(&mut self) -> &mut [u8] {
330 &mut self.0
331 }
332
333 #[inline]
334 pub(crate) fn as_ngx_str(&self) -> &NgxStr {
335 NgxStr::from_bytes(self.0.as_slice())
336 }
337
338 #[inline]
339 pub(crate) fn as_ngx_str_mut(&mut self) -> &mut NgxStr {
340 NgxStr::from_bytes_mut(self.0.as_mut_slice())
341 }
342
343 #[inline]
349 pub unsafe fn from_raw_parts(
350 ptr: *mut u8,
351 length: usize,
352 capacity: usize,
353 alloc: A,
354 ) -> Self {
355 Self(Vec::from_raw_parts_in(ptr, length, capacity, alloc))
356 }
357
358 #[inline]
362 pub fn into_raw_parts(self) -> (*mut u8, usize, usize, A) {
363 self.0.into_raw_parts_with_alloc()
364 }
365 }
366
367 impl<A> AsRef<NgxStr> for NgxString<A>
368 where
369 A: Allocator + Clone,
370 {
371 fn as_ref(&self) -> &NgxStr {
372 self.as_ngx_str()
373 }
374 }
375
376 impl<A> AsMut<NgxStr> for NgxString<A>
377 where
378 A: Allocator + Clone,
379 {
380 fn as_mut(&mut self) -> &mut NgxStr {
381 self.as_ngx_str_mut()
382 }
383 }
384
385 impl<A> AsRef<[u8]> for NgxString<A>
386 where
387 A: Allocator + Clone,
388 {
389 #[inline]
390 fn as_ref(&self) -> &[u8] {
391 self.as_bytes()
392 }
393 }
394
395 impl<A> AsMut<[u8]> for NgxString<A>
396 where
397 A: Allocator + Clone,
398 {
399 #[inline]
400 fn as_mut(&mut self) -> &mut [u8] {
401 self.as_bytes_mut()
402 }
403 }
404
405 impl<A> Borrow<NgxStr> for NgxString<A>
406 where
407 A: Allocator + Clone,
408 {
409 fn borrow(&self) -> &NgxStr {
410 self.as_ngx_str()
411 }
412 }
413
414 impl<A> Borrow<[u8]> for NgxString<A>
415 where
416 A: Allocator + Clone,
417 {
418 fn borrow(&self) -> &[u8] {
419 self.0.as_slice()
420 }
421 }
422
423 impl<A> fmt::Debug for NgxString<A>
424 where
425 A: Allocator + Clone,
426 {
427 #[inline]
428 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
429 f.write_str("NgxString(")?;
431 nginx_sys::detail::debug_bytes(f, &self.0)?;
432 f.write_str(")")
433 }
434 }
435
436 impl<A> ops::Deref for NgxString<A>
437 where
438 A: Allocator + Clone,
439 {
440 type Target = NgxStr;
441
442 fn deref(&self) -> &Self::Target {
443 self.as_ngx_str()
444 }
445 }
446
447 impl<A> ops::DerefMut for NgxString<A>
448 where
449 A: Allocator + Clone,
450 {
451 fn deref_mut(&mut self) -> &mut Self::Target {
452 self.as_ngx_str_mut()
453 }
454 }
455
456 impl<A> fmt::Display for NgxString<A>
457 where
458 A: Allocator + Clone,
459 {
460 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
461 fmt::Display::fmt(self.as_ngx_str(), f)
462 }
463 }
464
465 impl<A> hash::Hash for NgxString<A>
466 where
467 A: Allocator + Clone,
468 {
469 fn hash<H: hash::Hasher>(&self, state: &mut H) {
470 self.0.hash(state);
471 }
472 }
473
474 impl<A1, A2> PartialEq<NgxString<A2>> for NgxString<A1>
477 where
478 A1: Allocator + Clone,
479 A2: Allocator + Clone,
480 {
481 fn eq(&self, other: &NgxString<A2>) -> bool {
482 PartialEq::eq(self.as_bytes(), other.as_bytes())
483 }
484 }
485
486 impl<A> Eq for NgxString<A> where A: Allocator + Clone {}
487
488 impl<A1, A2> PartialOrd<NgxString<A2>> for NgxString<A1>
489 where
490 A1: Allocator + Clone,
491 A2: Allocator + Clone,
492 {
493 fn partial_cmp(&self, other: &NgxString<A2>) -> Option<cmp::Ordering> {
494 Some(Ord::cmp(self.as_bytes(), other.as_bytes()))
495 }
496 }
497
498 impl<A> Ord for NgxString<A>
499 where
500 A: Allocator + Clone,
501 {
502 fn cmp(&self, other: &Self) -> cmp::Ordering {
503 Ord::cmp(self.as_bytes(), other.as_bytes())
504 }
505 }
506
507 impl<OA: Allocator + Clone> allocator::TryCloneIn for NgxString<OA> {
508 type Target<A: Allocator + Clone> = NgxString<A>;
509
510 fn try_clone_in<A: Allocator + Clone>(
511 &self,
512 alloc: A,
513 ) -> Result<Self::Target<A>, allocator::AllocError> {
514 NgxString::try_from_bytes_in(self.as_bytes(), alloc).map_err(|_| allocator::AllocError)
515 }
516 }
517
518 impl<A> fmt::Write for NgxString<A>
519 where
520 A: Allocator + Clone,
521 {
522 fn write_str(&mut self, s: &str) -> fmt::Result {
523 self.append_within_capacity(s).map_err(|_| fmt::Error)
524 }
525 }
526
527 impl_partial_eq!(NgxString<A>, &'a [u8]; A: Allocator + Clone);
530 impl_partial_eq!(NgxString<A>, &'a [u8; N]; A: Allocator + Clone, const N: usize);
531 impl_partial_eq!(NgxString<A>, &'a NgxStr; A: Allocator + Clone);
532 impl_partial_eq!(NgxString<A>, ngx_str_t; A: Allocator + Clone);
533
534 impl_partial_ord!(NgxString<A>, &'a [u8]; A: Allocator + Clone);
535 impl_partial_ord!(NgxString<A>, &'a [u8; N]; A: Allocator + Clone, const N: usize);
536 impl_partial_ord!(NgxString<A>, &'a NgxStr; A: Allocator + Clone);
537 impl_partial_ord!(NgxString<A>, ngx_str_t; A: Allocator + Clone);
538
539 impl_partial_eq!(NgxStr, String);
540 impl_partial_eq!(&'a NgxStr, String);
541 impl_partial_ord!(NgxStr, String);
542 impl_partial_ord!(&'a NgxStr, String);
543 impl_partial_ord_eq_from!(NgxStr, &'a String);
544}
545
546#[cfg(test)]
547mod tests {
548 extern crate alloc;
549
550 use alloc::string::ToString;
551
552 use super::*;
553
554 #[test]
555 fn test_str_comparisons() {
556 let string = "test".to_string();
557 let ngx_string = ngx_str_t {
558 data: string.as_ptr().cast_mut(),
559 len: string.len(),
560 };
561 let ns: &NgxStr = string.as_bytes().into();
562
563 #[cfg(feature = "alloc")]
564 assert_eq!(string, ns);
565 assert_eq!(ngx_string, ns);
566 assert_eq!(string.as_bytes(), ns);
567 assert_eq!(string.as_str(), ns);
568 assert_eq!(b"test", ns);
569 assert_eq!("test", ns);
570
571 #[cfg(feature = "alloc")]
572 assert_eq!(ns, string);
573 assert_eq!(ns, ngx_string);
574 assert_eq!(ns, string.as_bytes());
575 assert_eq!(ns, string.as_str());
576 assert_eq!(ns, b"test");
577 assert_eq!(ns, "test");
578 }
579
580 #[test]
581 #[cfg(feature = "alloc")]
582 fn test_string_comparisons() {
583 use crate::allocator::Global;
584
585 let string = "test".to_string();
586 let ngx_string = ngx_str_t {
587 data: string.as_ptr().cast_mut(),
588 len: string.len(),
589 };
590 let borrowed: &NgxStr = string.as_bytes().into();
591 let owned = NgxString::try_from_bytes_in(&string, Global).unwrap();
592
593 assert_eq!(string.as_bytes(), owned);
594 assert_eq!(ngx_string, owned);
595 assert_eq!(borrowed, owned);
596 assert_eq!(b"test", owned);
597 assert_eq!(owned, string.as_bytes());
598 assert_eq!(owned, ngx_string);
599 assert_eq!(owned, borrowed);
600 assert_eq!(owned, b"test");
601
602 assert_eq!(string, *owned);
604 assert_eq!(string.as_str(), *owned);
605 assert_eq!("test", *owned);
606 assert_eq!(*owned, string);
607 assert_eq!(*owned, string.as_str());
608 assert_eq!(*owned, "test");
609 }
610
611 #[test]
612 #[cfg(feature = "alloc")]
613 fn test_string_write() {
614 use core::fmt::Write;
615
616 use crate::allocator::Global;
617
618 let h = NgxStr::from_bytes(b"Hello");
619 let w = NgxStr::from_bytes(b"world");
620
621 let mut s = NgxString::new_in(Global);
622 s.try_reserve(16).expect("reserve");
623
624 let saved = (s.as_bytes().as_ptr(), s.capacity());
626
627 write!(s, "{h} {w}!").expect("write");
628
629 assert_eq!(s, b"Hello world!");
630 assert_eq!((s.as_bytes().as_ptr(), s.capacity()), saved);
631 }
632
633 #[test]
634 fn test_lifetimes() {
635 let a: &NgxStr = "Hello World!".into();
636
637 let s = "Hello World!".to_string();
638 let b: &NgxStr = s.as_bytes().into();
639
640 assert_eq!(a.0, b.0);
644 }
645}