nginx_sys/
detail.rs

1//! Implementation details shared between nginx-sys and ngx.
2#![allow(missing_docs)]
3
4use core::fmt;
5use core::ptr::copy_nonoverlapping;
6
7use crate::bindings::{ngx_pnalloc, ngx_pool_t, u_char};
8
9/// Convert a byte slice to a raw pointer (`*mut u_char`) allocated in the given nginx memory pool.
10///
11/// # Safety
12///
13/// The caller must provide a valid pointer to the memory pool.
14pub 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
23/// Convert a string slice (`&str`) to a raw pointer (`*mut u_char`) allocated in the given nginx
24/// memory pool.
25///
26/// # Arguments
27///
28/// * `pool` - A pointer to the nginx memory pool (`ngx_pool_t`).
29/// * `data` - The string slice to convert to a raw pointer.
30///
31/// # Safety
32/// This function is marked as unsafe because it involves raw pointer manipulation and direct memory
33/// allocation using `ngx_pnalloc`.
34///
35/// # Returns
36/// A raw pointer (`*mut u_char`) to the allocated memory containing the converted string data.
37///
38/// # Example
39/// ```rust,ignore
40/// let pool: *mut ngx_pool_t = ...; // Obtain a pointer to the nginx memory pool
41/// let data: &str = "example"; // The string to convert
42/// let ptr = str_to_uchar(pool, data);
43/// ```
44pub 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    // The implementation is similar to an inlined `String::from_utf8_lossy`, with two
74    // important differences:
75    //
76    //  - it writes directly to the Formatter instead of allocating a temporary String
77    //  - invalid sequences are represented as escaped individual bytes
78    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        // Check that the formatter arguments are ignored correctly
129        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}