use bumpalo::Bump;
use std::collections::HashMap;
use std::sync::RwLock;
const DEFAULT_CHUNK_SIZE: usize = 64 * 1024;
static COMMON_HEADER_NAMES: &[&str] = &[
"accept",
"accept-encoding",
"accept-language",
"cache-control",
"connection",
"content-encoding",
"content-length",
"content-type",
"cookie",
"date",
"etag",
"expires",
"host",
"last-modified",
"location",
"referer",
"server",
"set-cookie",
"transfer-encoding",
"user-agent",
"vary",
"x-frame-options",
"x-content-type-options",
];
pub struct StringInterner {
cache: RwLock<HashMap<String, &'static str>>,
}
impl StringInterner {
pub fn new() -> Self {
let mut cache = HashMap::new();
for &name in COMMON_HEADER_NAMES {
cache.insert(name.to_string(), name);
}
Self {
cache: RwLock::new(cache),
}
}
pub fn intern(&self, s: &str) -> Option<&'static str> {
if let Ok(cache) = self.cache.read()
&& let Some(&interned) = cache.get(s)
{
return Some(interned);
}
None
}
}
impl Default for StringInterner {
fn default() -> Self {
Self::new()
}
}
pub struct ResponseArena {
bump: Bump,
}
impl ResponseArena {
#[must_use]
pub fn new() -> Self {
Self::with_capacity(DEFAULT_CHUNK_SIZE)
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
bump: Bump::with_capacity(capacity),
}
}
pub fn alloc_str(&self, s: &str) -> &str {
self.bump.alloc_str(s)
}
pub fn alloc_bytes(&self, bytes: &[u8]) -> &[u8] {
self.bump.alloc_slice_copy(bytes)
}
pub fn reset(&mut self) {
self.bump.reset();
}
#[must_use]
pub fn bytes_allocated(&self) -> usize {
self.bump.allocated_bytes()
}
#[must_use]
pub fn bump(&self) -> &Bump {
&self.bump
}
}
impl Default for ResponseArena {
fn default() -> Self {
Self::new()
}
}
pub struct ResponseBuffer<'arena> {
arena: &'arena ResponseArena,
parts: bumpalo::collections::Vec<'arena, &'arena str>,
}
impl<'arena> ResponseBuffer<'arena> {
#[must_use]
pub fn new(arena: &'arena ResponseArena) -> Self {
Self::with_capacity(arena, 20) }
#[must_use]
pub fn with_capacity(arena: &'arena ResponseArena, capacity: usize) -> Self {
Self {
arena,
parts: bumpalo::collections::Vec::with_capacity_in(capacity, arena.bump()),
}
}
pub fn push_str(&mut self, s: &str) {
if !s.is_empty() {
let allocated = self.arena.alloc_str(s);
self.parts.push(allocated);
}
}
pub fn push_bytes(&mut self, bytes: &[u8]) {
if let Ok(s) = std::str::from_utf8(bytes) {
self.push_str(s);
}
}
#[must_use]
pub fn as_str(&self) -> String {
self.parts.iter().copied().collect()
}
#[must_use]
pub fn len(&self) -> usize {
self.parts.iter().map(|s| s.len()).sum()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.parts.is_empty()
}
#[must_use]
pub fn part_count(&self) -> usize {
self.parts.len()
}
pub fn clear(&mut self) {
self.parts.clear();
}
}
#[derive(Debug)]
pub struct ArenaResponse<'arena> {
pub status: u16,
pub status_text: &'arena str,
pub headers: Vec<(&'arena str, &'arena str)>,
pub body_chunks: Vec<&'arena [u8]>,
}
impl<'arena> ArenaResponse<'arena> {
#[must_use]
pub fn new(arena: &'arena ResponseArena) -> Self {
Self {
status: 0,
status_text: arena.alloc_str(""),
headers: Vec::with_capacity(20), body_chunks: Vec::with_capacity(10),
}
}
pub fn set_status(&mut self, arena: &'arena ResponseArena, status: u16, text: &str) {
self.status = status;
self.status_text = arena.alloc_str(text);
}
pub fn add_header(&mut self, arena: &'arena ResponseArena, name: &str, value: &str) {
let name_ref = arena.alloc_str(name);
let value_ref = arena.alloc_str(value);
self.headers.push((name_ref, value_ref));
}
pub fn add_header_interned(
&mut self,
arena: &'arena ResponseArena,
interner: &StringInterner,
name: &str,
value: &str,
) {
let name_ref = if let Some(interned) = interner.intern(name) {
interned
} else {
arena.alloc_str(name)
};
let value_ref = arena.alloc_str(value);
self.headers.push((name_ref, value_ref));
}
pub fn add_body_chunk(&mut self, arena: &'arena ResponseArena, data: &[u8]) {
let chunk = arena.alloc_bytes(data);
self.body_chunks.push(chunk);
}
#[must_use]
pub fn body(&self) -> Vec<u8> {
let total_len: usize = self.body_chunks.iter().map(|c| c.len()).sum();
let mut body = Vec::with_capacity(total_len);
for chunk in &self.body_chunks {
body.extend_from_slice(chunk);
}
body
}
#[must_use]
pub fn body_text(&self) -> Option<String> {
let bytes = self.body();
String::from_utf8(bytes).ok()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arena_basic() {
let arena = ResponseArena::new();
let s1 = arena.alloc_str("hello");
let s2 = arena.alloc_str(" world");
assert_eq!(s1, "hello");
assert_eq!(s2, " world");
}
#[test]
fn test_arena_empty() {
let arena = ResponseArena::new();
let empty = arena.alloc_str("");
assert_eq!(empty, "");
}
#[test]
fn test_arena_large_allocation() {
let arena = ResponseArena::with_capacity(1024);
let large_str = "x".repeat(2048);
let allocated = arena.alloc_str(&large_str);
assert_eq!(allocated.len(), 2048);
assert_eq!(allocated, large_str);
}
#[test]
fn test_arena_bytes() {
let arena = ResponseArena::new();
let bytes = b"binary data";
let allocated = arena.alloc_bytes(bytes);
assert_eq!(allocated, bytes);
}
#[test]
fn test_arena_reset() {
let mut arena = ResponseArena::new();
arena.alloc_str("test1");
arena.alloc_str("test2");
let used_before = arena.bytes_allocated();
assert!(used_before > 0);
arena.reset();
let s = arena.alloc_str("after reset");
assert_eq!(s, "after reset");
}
#[test]
fn test_response_buffer_basic() {
let arena = ResponseArena::new();
let mut buffer = ResponseBuffer::new(&arena);
buffer.push_str("HTTP/1.1 200 OK\r\n");
buffer.push_str("Content-Type: text/html\r\n");
buffer.push_str("\r\n");
buffer.push_str("<html><body>Hello</body></html>");
let content = buffer.as_str();
assert!(content.contains("HTTP/1.1 200 OK"));
assert!(content.contains("<html>"));
assert_eq!(buffer.part_count(), 4);
}
#[test]
fn test_response_buffer_empty_strings() {
let arena = ResponseArena::new();
let mut buffer = ResponseBuffer::new(&arena);
buffer.push_str("hello");
buffer.push_str(""); buffer.push_str("world");
assert_eq!(buffer.part_count(), 2);
assert_eq!(buffer.as_str(), "helloworld");
}
#[test]
fn test_response_buffer_len() {
let arena = ResponseArena::new();
let mut buffer = ResponseBuffer::new(&arena);
assert_eq!(buffer.len(), 0);
assert!(buffer.is_empty());
buffer.push_str("test");
assert_eq!(buffer.len(), 4);
assert!(!buffer.is_empty());
buffer.push_str(" data");
assert_eq!(buffer.len(), 9);
}
#[test]
fn test_response_buffer_clear() {
let arena = ResponseArena::new();
let mut buffer = ResponseBuffer::new(&arena);
buffer.push_str("test");
buffer.push_str("data");
assert_eq!(buffer.part_count(), 2);
buffer.clear();
assert_eq!(buffer.part_count(), 0);
assert!(buffer.is_empty());
buffer.push_str("new");
assert_eq!(buffer.as_str(), "new");
}
#[test]
fn test_arena_response_basic() {
let arena = ResponseArena::new();
let mut response = ArenaResponse::new(&arena);
response.set_status(&arena, 200, "OK");
response.add_header(&arena, "Content-Type", "text/html");
response.add_header(&arena, "Content-Length", "13");
response.add_body_chunk(&arena, b"<html></html>");
assert_eq!(response.status, 200);
assert_eq!(response.status_text, "OK");
assert_eq!(response.headers.len(), 2);
assert_eq!(response.body_chunks.len(), 1);
let body_text = response.body_text().unwrap();
assert_eq!(body_text, "<html></html>");
}
#[test]
fn test_arena_response_multiple_chunks() {
let arena = ResponseArena::new();
let mut response = ArenaResponse::new(&arena);
response.add_body_chunk(&arena, b"<html>");
response.add_body_chunk(&arena, b"<body>");
response.add_body_chunk(&arena, b"Hello");
response.add_body_chunk(&arena, b"</body>");
response.add_body_chunk(&arena, b"</html>");
let body_text = response.body_text().unwrap();
assert_eq!(body_text, "<html><body>Hello</body></html>");
}
#[test]
fn test_string_interner_common_headers() {
let interner = StringInterner::new();
let content_type1 = interner.intern("content-type");
let content_type2 = interner.intern("content-type");
assert!(content_type1.is_some());
assert!(content_type2.is_some());
assert_eq!(
std::ptr::from_ref::<str>(content_type1.unwrap()),
std::ptr::from_ref::<str>(content_type2.unwrap())
);
}
#[test]
fn test_string_interner_uncommon_strings() {
let interner = StringInterner::new();
let custom = interner.intern("x-custom-header-12345");
assert!(custom.is_none());
}
#[test]
fn test_arena_response_with_interning() {
let arena = ResponseArena::new();
let interner = StringInterner::new();
let mut response = ArenaResponse::new(&arena);
response.add_header_interned(&arena, &interner, "content-type", "text/html");
response.add_header_interned(&arena, &interner, "server", "nginx");
response.add_header_interned(&arena, &interner, "x-custom", "value");
assert_eq!(response.headers.len(), 3);
assert_eq!(response.headers[0].0, "content-type");
assert_eq!(response.headers[1].0, "server");
assert_eq!(response.headers[2].0, "x-custom");
}
}