use crate::pool::Pool;
use alloc::ffi::CString;
use alloc::string::String;
pub use apr_sys::{apr_array_header_t, apr_table_t};
use core::ffi::{c_char, c_void, CStr};
use core::marker::PhantomData;
pub struct Array<'pool> {
ptr: *mut apr_array_header_t,
_phantom: PhantomData<&'pool Pool<'pool>>,
}
impl<'pool> Array<'pool> {
pub fn new(pool: &'pool Pool<'pool>, nelts: i32, elt_size: i32) -> Self {
let ptr = unsafe { apr_sys::apr_array_make(pool.as_mut_ptr(), nelts, elt_size) };
Self {
ptr,
_phantom: PhantomData,
}
}
pub unsafe fn from_ptr(ptr: *mut apr_array_header_t) -> Self {
Self {
ptr,
_phantom: PhantomData,
}
}
pub unsafe fn push_raw(&mut self, data: &[u8]) {
let dst = apr_sys::apr_array_push(self.ptr);
core::ptr::copy_nonoverlapping(data.as_ptr(), dst as *mut u8, data.len());
}
pub unsafe fn get_raw(&self, index: usize) -> *mut c_void {
let header = &*self.ptr;
if index >= header.nelts as usize {
panic!("Array index out of bounds");
}
let elts = header.elts as *mut u8;
elts.add(index * header.elt_size as usize) as *mut c_void
}
pub fn len(&self) -> usize {
unsafe { (*self.ptr).nelts as usize }
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn clear(&mut self) {
unsafe {
apr_sys::apr_array_clear(self.ptr);
}
}
pub unsafe fn as_ptr(&self) -> *const apr_array_header_t {
self.ptr
}
pub unsafe fn as_mut_ptr(&mut self) -> *mut apr_array_header_t {
self.ptr
}
}
pub struct TypedArray<'pool, T: Copy> {
inner: Array<'pool>,
_phantom: PhantomData<T>,
}
impl<'pool, T: Copy> TypedArray<'pool, T> {
pub fn new(pool: &'pool Pool<'pool>, initial_size: i32) -> Self {
Self {
inner: Array::new(pool, initial_size, core::mem::size_of::<T>() as i32),
_phantom: PhantomData,
}
}
pub unsafe fn from_ptr(ptr: *mut apr_array_header_t) -> Self {
Self {
inner: Array::from_ptr(ptr),
_phantom: PhantomData,
}
}
pub fn push(&mut self, value: T) {
unsafe {
let bytes = core::slice::from_raw_parts(
&value as *const T as *const u8,
core::mem::size_of::<T>(),
);
self.inner.push_raw(bytes);
}
}
pub fn get(&self, index: usize) -> Option<T> {
if index >= self.len() {
return None;
}
unsafe {
let ptr = self.inner.get_raw(index) as *const T;
Some(*ptr)
}
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn clear(&mut self) {
self.inner.clear()
}
pub fn iter(&self) -> TypedArrayIter<'_, 'pool, T> {
TypedArrayIter {
array: self,
index: 0,
}
}
pub unsafe fn as_ptr(&self) -> *const apr_array_header_t {
self.inner.as_ptr()
}
pub unsafe fn as_mut_ptr(&mut self) -> *mut apr_array_header_t {
self.inner.as_mut_ptr()
}
}
pub struct TypedArrayIter<'a, 'pool, T: Copy> {
array: &'a TypedArray<'pool, T>,
index: usize,
}
impl<'a, 'pool, T: Copy> Iterator for TypedArrayIter<'a, 'pool, T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.array.len() {
let value = self.array.get(self.index);
self.index += 1;
value
} else {
None
}
}
}
impl<'pool, T: Copy> TypedArray<'pool, T> {
pub fn from_iter<I>(pool: &'pool Pool<'pool>, iter: I) -> Self
where
I: IntoIterator<Item = T>,
I::IntoIter: ExactSizeIterator,
{
let iter = iter.into_iter();
let mut array = Self::new(pool, iter.len() as i32);
for value in iter {
array.push(value);
}
array
}
}
impl<'pool, T: Copy> Extend<T> for TypedArray<'pool, T> {
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
for value in iter {
self.push(value);
}
}
}
pub struct Table<'pool> {
ptr: *mut apr_table_t,
_phantom: PhantomData<&'pool Pool<'pool>>,
}
impl<'pool> Table<'pool> {
pub fn new(pool: &'pool Pool<'pool>, nelts: i32) -> Self {
let ptr = unsafe { apr_sys::apr_table_make(pool.as_mut_ptr(), nelts) };
Self {
ptr,
_phantom: PhantomData,
}
}
pub unsafe fn from_ptr(ptr: *mut apr_table_t) -> Self {
Self {
ptr,
_phantom: PhantomData,
}
}
pub unsafe fn set_raw(&mut self, key: *const c_char, val: *const c_char) {
apr_sys::apr_table_set(self.ptr, key, val);
}
pub unsafe fn get_raw(&self, key: *const c_char) -> *const c_char {
apr_sys::apr_table_get(self.ptr, key)
}
pub unsafe fn add_raw(&mut self, key: *const c_char, val: *const c_char) {
apr_sys::apr_table_add(self.ptr, key, val);
}
pub unsafe fn unset_raw(&mut self, key: *const c_char) {
apr_sys::apr_table_unset(self.ptr, key);
}
pub fn len(&self) -> usize {
unsafe {
let entries = apr_sys::apr_table_elts(self.ptr);
(*entries).nelts as usize
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn clear(&mut self) {
unsafe {
apr_sys::apr_table_clear(self.ptr);
}
}
pub unsafe fn as_ptr(&self) -> *const apr_table_t {
self.ptr
}
pub unsafe fn as_mut_ptr(&mut self) -> *mut apr_table_t {
self.ptr
}
}
pub struct StringTable<'pool> {
inner: Table<'pool>,
}
impl<'pool> StringTable<'pool> {
pub fn new(pool: &'pool Pool, initial_size: i32) -> Self {
Self {
inner: Table::new(pool, initial_size),
}
}
pub unsafe fn from_ptr(ptr: *mut apr_table_t, _pool: &'pool Pool) -> Self {
Self {
inner: Table::from_ptr(ptr),
}
}
pub fn set(&mut self, key: &str, value: &str) {
let key_cstr = CString::new(key).expect("Invalid key");
let val_cstr = CString::new(value).expect("Invalid value");
unsafe {
self.inner.set_raw(key_cstr.as_ptr(), val_cstr.as_ptr());
}
}
pub fn get(&self, key: &str) -> Option<&str> {
let key_cstr = CString::new(key).ok()?;
unsafe {
let val_ptr = self.inner.get_raw(key_cstr.as_ptr());
if val_ptr.is_null() {
None
} else {
let val_cstr = CStr::from_ptr(val_ptr);
val_cstr.to_str().ok()
}
}
}
pub fn add(&mut self, key: &str, value: &str) {
let key_cstr = CString::new(key).expect("Invalid key");
let val_cstr = CString::new(value).expect("Invalid value");
unsafe {
self.inner.add_raw(key_cstr.as_ptr(), val_cstr.as_ptr());
}
}
pub fn unset(&mut self, key: &str) {
if let Ok(key_cstr) = CString::new(key) {
unsafe {
self.inner.unset_raw(key_cstr.as_ptr());
}
}
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn clear(&mut self) {
self.inner.clear()
}
pub fn iter(&self) -> StringTableIter<'_, 'pool> {
StringTableIter {
table: &self.inner,
index: 0,
_phantom: PhantomData,
}
}
}
pub struct StringTableIter<'a, 'pool> {
table: &'a Table<'pool>,
index: usize,
_phantom: PhantomData<&'pool ()>,
}
impl<'a, 'pool> Iterator for StringTableIter<'a, 'pool> {
type Item = (&'a str, &'a str);
fn next(&mut self) -> Option<Self::Item> {
unsafe {
let elts = apr_sys::apr_table_elts(self.table.ptr);
let header = &*elts;
if self.index >= header.nelts as usize {
return None;
}
let entry_size = core::mem::size_of::<(*const c_char, *const c_char, u32)>();
let entry_ptr = (header.elts as *const u8).add(self.index * entry_size);
let key_ptr = *(entry_ptr as *const *const c_char);
let val_ptr =
*(entry_ptr.add(core::mem::size_of::<*const c_char>()) as *const *const c_char);
self.index += 1;
if key_ptr.is_null() {
return self.next();
}
let key = CStr::from_ptr(key_ptr).to_str().ok()?;
let val = if val_ptr.is_null() {
""
} else {
CStr::from_ptr(val_ptr).to_str().ok()?
};
Some((key, val))
}
}
}
impl<'pool> StringTable<'pool> {
pub fn from_iter<'a, I>(pool: &'pool Pool, iter: I) -> Self
where
I: IntoIterator<Item = (&'a str, &'a str)>,
{
let mut table = Self::new(pool, 16);
for (key, value) in iter {
table.set(key, value);
}
table
}
}
impl<'pool> Extend<(String, String)> for StringTable<'pool> {
fn extend<I: IntoIterator<Item = (String, String)>>(&mut self, iter: I) {
for (key, value) in iter {
self.set(&key, &value);
}
}
}
impl<'pool, 'a> Extend<(&'a str, &'a str)> for StringTable<'pool> {
fn extend<I: IntoIterator<Item = (&'a str, &'a str)>>(&mut self, iter: I) {
for (key, value) in iter {
self.set(key, value);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::ToString;
use alloc::vec;
use alloc::vec::Vec;
#[test]
fn test_array_basic() {
let pool = Pool::new();
let mut array = Array::new(&pool, 10, core::mem::size_of::<i32>() as i32);
assert!(array.is_empty());
let value1 = 42i32;
let value2 = 84i32;
unsafe {
array.push_raw(&value1.to_ne_bytes());
array.push_raw(&value2.to_ne_bytes());
}
assert_eq!(array.len(), 2);
unsafe {
let ptr1 = array.get_raw(0) as *const i32;
let ptr2 = array.get_raw(1) as *const i32;
assert_eq!(*ptr1, 42);
assert_eq!(*ptr2, 84);
}
}
#[test]
fn test_typed_array() {
let pool = Pool::new();
let mut array = TypedArray::<i32>::new(&pool, 10);
assert!(array.is_empty());
array.push(42);
array.push(84);
array.push(126);
assert_eq!(array.len(), 3);
assert_eq!(array.get(0), Some(42));
assert_eq!(array.get(1), Some(84));
assert_eq!(array.get(2), Some(126));
assert_eq!(array.get(3), None);
let values: Vec<_> = array.iter().collect();
assert_eq!(values, vec![42, 84, 126]);
}
#[test]
fn test_table_basic() {
let pool = Pool::new();
let mut table = Table::new(&pool, 10);
assert!(table.is_empty());
let key1 = CString::new("key1").unwrap();
let val1 = CString::new("value1").unwrap();
let key2 = CString::new("key2").unwrap();
let val2 = CString::new("value2").unwrap();
unsafe {
table.set_raw(key1.as_ptr(), val1.as_ptr());
table.set_raw(key2.as_ptr(), val2.as_ptr());
}
assert_eq!(table.len(), 2);
unsafe {
let result = table.get_raw(key1.as_ptr());
assert!(!result.is_null());
assert_eq!(CStr::from_ptr(result).to_str().unwrap(), "value1");
}
}
#[test]
fn test_string_table() {
let pool = Pool::new();
let mut table = StringTable::new(&pool, 10);
assert!(table.is_empty());
table.set("key1", "value1");
table.set("key2", "value2");
assert_eq!(table.len(), 2);
assert_eq!(table.get("key1"), Some("value1"));
assert_eq!(table.get("key2"), Some("value2"));
assert_eq!(table.get("key3"), None);
table.add("key1", "another_value1");
assert!(table.len() > 2);
table.unset("key1");
assert_eq!(table.get("key1"), None);
}
#[test]
fn test_typed_array_from_iter() {
let pool = Pool::new();
let data = vec![1, 2, 3, 4, 5];
let array = TypedArray::<i32>::from_iter(&pool, data.clone());
assert_eq!(array.len(), 5);
for (i, &val) in data.iter().enumerate() {
assert_eq!(array.get(i), Some(val));
}
}
#[test]
fn test_typed_array_extend() {
let pool = Pool::new();
let mut array = TypedArray::<i32>::new(&pool, 10);
array.push(1);
array.push(2);
assert_eq!(array.len(), 2);
array.extend(vec![3, 4, 5]);
assert_eq!(array.len(), 5);
assert_eq!(array.get(0), Some(1));
assert_eq!(array.get(4), Some(5));
}
#[test]
fn test_string_table_from_iter() {
let pool = Pool::new();
let data = vec![("key1", "value1"), ("key2", "value2"), ("key3", "value3")];
let table = StringTable::from_iter(&pool, data);
assert_eq!(table.len(), 3);
assert_eq!(table.get("key1"), Some("value1"));
assert_eq!(table.get("key2"), Some("value2"));
assert_eq!(table.get("key3"), Some("value3"));
}
#[test]
fn test_string_table_extend() {
let pool = Pool::new();
let mut table = StringTable::new(&pool, 10);
table.set("a", "1");
assert_eq!(table.len(), 1);
table.extend(vec![("b", "2"), ("c", "3")]);
assert_eq!(table.len(), 3);
assert_eq!(table.get("a"), Some("1"));
assert_eq!(table.get("b"), Some("2"));
assert_eq!(table.get("c"), Some("3"));
table.extend(vec![
("d".to_string(), "4".to_string()),
("e".to_string(), "5".to_string()),
]);
assert_eq!(table.len(), 5);
}
}