|
1 | 1 | #![doc = include_str!("../README.md")] |
2 | 2 | #![warn(missing_docs)] |
3 | | -#![cfg_attr(not(feature = "std"), no_std)] |
4 | | -#[cfg(all(not(feature = "std"), feature = "alloc"))] |
5 | | -extern crate alloc; |
| 3 | +#![cfg_attr(not(test), no_std)] |
6 | 4 |
|
7 | 5 | use core::ptr::copy_nonoverlapping; |
8 | 6 | use core::slice; |
9 | 7 |
|
10 | | -#[cfg(all(not(feature = "std"), feature = "alloc"))] |
11 | | -use alloc::string::{FromUtf8Error, String}; |
12 | | -#[cfg(feature = "std")] |
13 | | -use std::string::{FromUtf8Error, String}; |
14 | | - |
15 | 8 | #[doc(hidden)] |
16 | 9 | mod bindings { |
17 | 10 | #![allow(missing_docs)] |
@@ -123,28 +116,6 @@ impl ngx_str_t { |
123 | 116 | bytes_to_uchar(pool, src).map(|data| Self { data, len: src.len() }) |
124 | 117 | } |
125 | 118 |
|
126 | | - /// Create an `ngx_str_t` instance from a `String`. |
127 | | - /// |
128 | | - /// # Arguments |
129 | | - /// |
130 | | - /// * `pool` - A pointer to the nginx memory pool (`ngx_pool_t`). |
131 | | - /// * `data` - The `String` from which to create the nginx string. |
132 | | - /// |
133 | | - /// # Safety |
134 | | - /// This function is marked as unsafe because it accepts a raw pointer argument. There is no |
135 | | - /// way to know if `pool` is pointing to valid memory. The caller must provide a valid pool to |
136 | | - /// avoid indeterminate behavior. |
137 | | - /// |
138 | | - /// # Returns |
139 | | - /// An `ngx_str_t` instance representing the given `String`. |
140 | | - #[cfg(feature = "alloc")] |
141 | | - pub unsafe fn from_string(pool: *mut ngx_pool_t, data: String) -> Self { |
142 | | - ngx_str_t { |
143 | | - data: str_to_uchar(pool, data.as_str()), |
144 | | - len: data.len(), |
145 | | - } |
146 | | - } |
147 | | - |
148 | 119 | /// Create an `ngx_str_t` instance from a string slice (`&str`). |
149 | 120 | /// |
150 | 121 | /// # Arguments |
@@ -176,20 +147,21 @@ impl From<ngx_str_t> for &[u8] { |
176 | 147 | } |
177 | 148 | } |
178 | 149 |
|
179 | | -#[cfg(feature = "alloc")] |
180 | | -impl TryFrom<ngx_str_t> for String { |
181 | | - type Error = FromUtf8Error; |
182 | | - |
183 | | - fn try_from(s: ngx_str_t) -> Result<Self, Self::Error> { |
184 | | - let bytes: &[u8] = s.into(); |
185 | | - String::from_utf8(bytes.into()) |
186 | | - } |
187 | | -} |
188 | | - |
189 | | -#[cfg(feature = "alloc")] |
190 | 150 | impl core::fmt::Display for ngx_str_t { |
191 | 151 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
192 | | - write!(f, "{}", String::from_utf8_lossy((*self).into())) |
| 152 | + // The implementation is similar to an inlined `String::from_utf8_lossy`, with two |
| 153 | + // important differences: |
| 154 | + // |
| 155 | + // - it writes directly to the Formatter instead of allocating a temporary String |
| 156 | + // - invalid sequences are represented as escaped individual bytes |
| 157 | + for chunk in self.as_bytes().utf8_chunks() { |
| 158 | + f.write_str(chunk.valid())?; |
| 159 | + for byte in chunk.invalid() { |
| 160 | + f.write_str("\\x")?; |
| 161 | + core::fmt::LowerHex::fmt(byte, f)?; |
| 162 | + } |
| 163 | + } |
| 164 | + Ok(()) |
193 | 165 | } |
194 | 166 | } |
195 | 167 |
|
@@ -228,22 +200,47 @@ impl TryFrom<ngx_str_t> for &str { |
228 | 200 | /// let result = add_to_ngx_table(table, pool, key, value); |
229 | 201 | /// # } |
230 | 202 | /// ``` |
231 | | -#[cfg(feature = "alloc")] |
232 | 203 | pub unsafe fn add_to_ngx_table( |
233 | 204 | table: *mut ngx_table_elt_t, |
234 | 205 | pool: *mut ngx_pool_t, |
235 | | - key: &str, |
236 | | - value: &str, |
| 206 | + key: impl AsRef<[u8]>, |
| 207 | + value: impl AsRef<[u8]>, |
237 | 208 | ) -> Option<()> { |
238 | | - if table.is_null() { |
239 | | - return None; |
| 209 | + if let Some(table) = table.as_mut() { |
| 210 | + let key = key.as_ref(); |
| 211 | + table.key = ngx_str_t::from_bytes(pool, key)?; |
| 212 | + table.value = ngx_str_t::from_bytes(pool, value.as_ref())?; |
| 213 | + table.lowcase_key = ngx_pnalloc(pool, table.key.len).cast(); |
| 214 | + if table.lowcase_key.is_null() { |
| 215 | + return None; |
| 216 | + } |
| 217 | + table.hash = ngx_hash_strlow(table.lowcase_key, table.key.data, table.key.len); |
| 218 | + return Some(()); |
| 219 | + } |
| 220 | + None |
| 221 | +} |
| 222 | + |
| 223 | +#[cfg(test)] |
| 224 | +mod tests { |
| 225 | + use super::*; |
| 226 | + |
| 227 | + #[test] |
| 228 | + fn ngx_str_display() { |
| 229 | + let pairs: &[(&[u8], &str)] = &[ |
| 230 | + (b"", ""), |
| 231 | + (b"Ferris the \xf0\x9f\xa6\x80", "Ferris the 🦀"), |
| 232 | + (b"\xF0\x90\x80", "\\xf0\\x90\\x80"), |
| 233 | + (b"\xF0\x90\x80Hello World", "\\xf0\\x90\\x80Hello World"), |
| 234 | + (b"Hello \xF0\x90\x80World", "Hello \\xf0\\x90\\x80World"), |
| 235 | + (b"Hello World\xF0\x90\x80", "Hello World\\xf0\\x90\\x80"), |
| 236 | + ]; |
| 237 | + |
| 238 | + for (bytes, expected) in pairs { |
| 239 | + let str = ngx_str_t { |
| 240 | + data: bytes.as_ptr().cast_mut(), |
| 241 | + len: bytes.len(), |
| 242 | + }; |
| 243 | + assert_eq!(str.to_string(), *expected); |
| 244 | + } |
240 | 245 | } |
241 | | - table.as_mut().map(|table| { |
242 | | - table.hash = 1; |
243 | | - table.key.len = key.len(); |
244 | | - table.key.data = str_to_uchar(pool, key); |
245 | | - table.value.len = value.len(); |
246 | | - table.value.data = str_to_uchar(pool, value); |
247 | | - table.lowcase_key = str_to_uchar(pool, String::from(key).to_ascii_lowercase().as_str()); |
248 | | - }) |
249 | 246 | } |
0 commit comments