use core::ffi::{c_int, c_uint, c_void};
#[repr(C)]
struct lshpack_header {
name: *const u8,
name_len: usize,
value: *const u8,
value_len: usize,
never_index: bool,
hpack_index: u16,
}
impl Default for lshpack_header {
fn default() -> Self {
Self {
name: core::ptr::null(),
name_len: 0,
value: core::ptr::null(),
value_len: 0,
never_index: false,
hpack_index: 255,
}
}
}
#[repr(C)]
pub struct HPACK {
self_: *mut c_void,
}
pub struct DecodeResult {
pub name: &'static [u8],
pub value: &'static [u8],
pub never_index: bool,
pub well_know: u16,
pub next: usize,
}
#[derive(thiserror::Error, strum::IntoStaticStr, Debug)]
pub enum HpackError {
#[error("UnableToDecode")]
UnableToDecode,
#[error("EmptyHeaderName")]
EmptyHeaderName,
#[error("UnableToEncode")]
UnableToEncode,
}
impl HPACK {
pub const LSHPACK_MAX_HEADER_SIZE: usize = 65536;
pub fn init(max_capacity: u32) -> *mut HPACK {
let ptr = lshpack_wrapper_init(
bun_alloc::mimalloc::mi_malloc,
bun_alloc::mimalloc::mi_free,
max_capacity as usize,
);
if ptr.is_null() {
bun_core::out_of_memory();
}
ptr
}
pub fn decode(&mut self, src: &[u8]) -> Result<DecodeResult, HpackError> {
let mut header = lshpack_header::default();
let offset = unsafe { lshpack_wrapper_decode(self, src.as_ptr(), src.len(), &mut header) };
if offset == 0 {
return Err(HpackError::UnableToDecode);
}
if header.name_len == 0 {
return Err(HpackError::EmptyHeaderName);
}
let (name, value) = unsafe {
(
core::slice::from_raw_parts(header.name, header.name_len),
core::slice::from_raw_parts(header.value, header.value_len),
)
};
Ok(DecodeResult {
name,
value,
next: offset,
never_index: header.never_index,
well_know: header.hpack_index,
})
}
pub fn encode(
&mut self,
name: &[u8],
value: &[u8],
never_index: bool,
dst_buffer: &mut [u8],
dst_buffer_offset: usize,
) -> Result<usize, HpackError> {
let offset = unsafe {
lshpack_wrapper_encode(
self,
name.as_ptr(),
name.len(),
value.as_ptr(),
value.len(),
never_index as c_int,
dst_buffer.as_mut_ptr(),
dst_buffer.len(),
dst_buffer_offset,
)
};
if offset == 0 {
return Err(HpackError::UnableToEncode);
}
Ok(offset)
}
pub fn set_encoder_max_capacity(&mut self, max_capacity: u32) {
lshpack_wrapper_enc_set_max_capacity(self, max_capacity as c_uint);
}
pub fn set_decoder_max_capacity(&mut self, max_capacity: u32) {
lshpack_wrapper_dec_set_max_capacity(self, max_capacity as c_uint);
}
}
pub struct HpackHandle(core::ptr::NonNull<HPACK>);
impl HpackHandle {
#[inline]
pub fn new(max_capacity: u32) -> Self {
Self(
core::ptr::NonNull::new(HPACK::init(max_capacity))
.expect("lshpack_wrapper_init returned null"),
)
}
}
impl core::ops::Deref for HpackHandle {
type Target = HPACK;
#[inline]
fn deref(&self) -> &HPACK {
unsafe { self.0.as_ref() }
}
}
impl core::ops::DerefMut for HpackHandle {
#[inline]
fn deref_mut(&mut self) -> &mut HPACK {
unsafe { self.0.as_mut() }
}
}
impl Drop for HpackHandle {
#[inline]
fn drop(&mut self) {
unsafe { lshpack_wrapper_deinit(self.0.as_ptr()) };
}
}
unsafe impl Send for HpackHandle {}
type LshpackWrapperAlloc = extern "C" fn(size: usize) -> *mut c_void;
type LshpackWrapperFree = unsafe extern "C" fn(ptr: *mut c_void);
unsafe extern "C" {
safe fn lshpack_wrapper_init(
alloc: LshpackWrapperAlloc,
free: LshpackWrapperFree,
capacity: usize,
) -> *mut HPACK;
safe fn lshpack_wrapper_enc_set_max_capacity(self_: &mut HPACK, max_capacity: c_uint);
safe fn lshpack_wrapper_dec_set_max_capacity(self_: &mut HPACK, max_capacity: c_uint);
fn lshpack_wrapper_deinit(self_: *mut HPACK);
fn lshpack_wrapper_decode(
self_: &mut HPACK,
src: *const u8,
src_len: usize,
output: &mut lshpack_header,
) -> usize;
fn lshpack_wrapper_encode(
self_: &mut HPACK,
name: *const u8,
name_len: usize,
value: *const u8,
value_len: usize,
never_index: c_int,
buffer: *mut u8,
buffer_len: usize,
buffer_offset: usize,
) -> usize;
}