Skip to content

Commit ee33a5b

Browse files
committed
fix: remove alloc usage in nginx-sys
follow bavshin-f5's comment: nginx#111 (comment) copied implementation: Display for ngx_str_t, and test <= bavshin-f5/ngx-rust@3f054e3 add_to_ngx_table <= bavshin-f5/ngx-rust@ae2ee99
1 parent c98ee2b commit ee33a5b

File tree

2 files changed

+53
-58
lines changed

2 files changed

+53
-58
lines changed

nginx-sys/Cargo.toml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,5 @@ ureq = { version = "2.9.6", features = ["tls"], optional = true }
2727
which = { version = "6.0.0", optional = true }
2828

2929
[features]
30-
default = ["vendored","std"]
31-
alloc = []
32-
std = ["alloc"]
30+
default = ["vendored"]
3331
vendored = ["dep:which", "dep:duct", "dep:ureq", "dep:flate2", "dep:tar"]

nginx-sys/src/lib.rs

Lines changed: 52 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
11
#![doc = include_str!("../README.md")]
22
#![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)]
64

75
use core::ptr::copy_nonoverlapping;
86
use core::slice;
97

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-
158
#[doc(hidden)]
169
mod bindings {
1710
#![allow(missing_docs)]
@@ -123,28 +116,6 @@ impl ngx_str_t {
123116
bytes_to_uchar(pool, src).map(|data| Self { data, len: src.len() })
124117
}
125118

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-
148119
/// Create an `ngx_str_t` instance from a string slice (`&str`).
149120
///
150121
/// # Arguments
@@ -176,20 +147,21 @@ impl From<ngx_str_t> for &[u8] {
176147
}
177148
}
178149

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")]
190150
impl core::fmt::Display for ngx_str_t {
191151
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(())
193165
}
194166
}
195167

@@ -228,22 +200,47 @@ impl TryFrom<ngx_str_t> for &str {
228200
/// let result = add_to_ngx_table(table, pool, key, value);
229201
/// # }
230202
/// ```
231-
#[cfg(feature = "alloc")]
232203
pub unsafe fn add_to_ngx_table(
233204
table: *mut ngx_table_elt_t,
234205
pool: *mut ngx_pool_t,
235-
key: &str,
236-
value: &str,
206+
key: impl AsRef<[u8]>,
207+
value: impl AsRef<[u8]>,
237208
) -> 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+
}
240245
}
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-
})
249246
}

0 commit comments

Comments
 (0)