1#![allow(missing_docs)]
3
4use core::fmt;
5use core::ptr::copy_nonoverlapping;
6
7use crate::bindings::{ngx_pnalloc, ngx_pool_t, u_char};
8
9pub unsafe fn bytes_to_uchar(pool: *mut ngx_pool_t, data: &[u8]) -> Option<*mut u_char> {
15 let ptr: *mut u_char = ngx_pnalloc(pool, data.len()) as _;
16 if ptr.is_null() {
17 return None;
18 }
19 copy_nonoverlapping(data.as_ptr(), ptr, data.len());
20 Some(ptr)
21}
22
23pub unsafe fn str_to_uchar(pool: *mut ngx_pool_t, data: &str) -> *mut u_char {
45 let ptr: *mut u_char = ngx_pnalloc(pool, data.len()) as _;
46 debug_assert!(!ptr.is_null());
47 copy_nonoverlapping(data.as_ptr(), ptr, data.len());
48 ptr
49}
50
51#[inline]
52pub fn debug_bytes(f: &mut fmt::Formatter<'_>, bytes: &[u8]) -> fmt::Result {
53 if f.alternate() {
54 match bytes.len() {
55 0 => Ok(()),
56 1 => write!(f, "{:02x}", bytes[0]),
57 x => {
58 for b in &bytes[..x - 1] {
59 write!(f, "{b:02x},")?;
60 }
61 write!(f, "{:02x}", bytes[x - 1])
62 }
63 }
64 } else {
65 f.write_str("\"")?;
66 display_bytes(f, bytes)?;
67 f.write_str("\"")
68 }
69}
70
71#[inline]
72pub fn display_bytes(f: &mut fmt::Formatter<'_>, bytes: &[u8]) -> fmt::Result {
73 for chunk in bytes.utf8_chunks() {
79 f.write_str(chunk.valid())?;
80 for byte in chunk.invalid() {
81 write!(f, "\\x{byte:02x}")?;
82 }
83 }
84
85 Ok(())
86}
87
88#[cfg(test)]
89mod tests {
90 extern crate alloc;
91 use alloc::format;
92 use alloc::string::ToString;
93
94 use super::*;
95
96 struct TestStr(&'static [u8]);
97
98 impl fmt::Debug for TestStr {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 f.write_str("TestStr(")?;
101 debug_bytes(f, self.0)?;
102 f.write_str(")")
103 }
104 }
105
106 impl fmt::Display for TestStr {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 display_bytes(f, self.0)
109 }
110 }
111
112 #[test]
113 fn test_display() {
114 let cases: &[(&[u8], &str)] = &[
115 (b"", ""),
116 (b"Ferris the \xf0\x9f\xa6\x80", "Ferris the 🦀"),
117 (b"\xF0\x90\x80", "\\xf0\\x90\\x80"),
118 (b"\xF0\x90\x80Hello World", "\\xf0\\x90\\x80Hello World"),
119 (b"Hello \xF0\x90\x80World", "Hello \\xf0\\x90\\x80World"),
120 (b"Hello World\xF0\x90\x80", "Hello World\\xf0\\x90\\x80"),
121 ];
122
123 for (bytes, expected) in cases {
124 let str = TestStr(bytes);
125 assert_eq!(str.to_string(), *expected);
126 }
127
128 for (bytes, expected) in &cases[2..3] {
130 let str = TestStr(bytes);
131 assert_eq!(format!("{str:12.12}"), *expected);
132 }
133 }
134
135 #[test]
136 fn test_debug() {
137 let cases: &[(&[u8], &str, &str)] = &[
138 (b"", "TestStr(\"\")", "TestStr()"),
139 (b"a", "TestStr(\"a\")", "TestStr(61)"),
140 (
141 b"Ferris the \xf0\x9f\xa6\x80",
142 "TestStr(\"Ferris the 🦀\")",
143 "TestStr(46,65,72,72,69,73,20,74,68,65,20,f0,9f,a6,80)",
144 ),
145 (
146 b"\xF0\x90\x80",
147 "TestStr(\"\\xf0\\x90\\x80\")",
148 "TestStr(f0,90,80)",
149 ),
150 ];
151 for (bytes, expected, alternate) in cases {
152 let str = TestStr(bytes);
153 assert_eq!(format!("{str:?}"), *expected);
154 assert_eq!(format!("{str:#?}"), *alternate);
155 }
156 }
157}