use crate::header::TryIntoHeader;
use crate::StreamId;
use std::collections::HashMap;
use std::fmt::Debug;
use std::fmt::Display;
use std::marker::PhantomPinned;
use std::pin::Pin;
pub struct EncoderError;
pub struct Encoder {
inner: Pin<Box<InnerEncoder>>,
seqnos: HashMap<StreamId, u32>,
}
impl Encoder {
#[inline]
pub fn new() -> Self {
Self {
inner: InnerEncoder::new(),
seqnos: HashMap::new(),
}
}
#[inline]
pub fn configure(
&mut self,
max_table_size: u32,
dyn_table_size: u32,
max_blocked_streams: u32,
) -> Result<SDTCInstruction, EncoderError> {
self.inner
.as_mut()
.init(max_table_size, dyn_table_size, max_blocked_streams)
.map(SDTCInstruction)
}
pub fn encode_all<I, H>(
&mut self,
stream_id: StreamId,
headers: I,
) -> Result<BuffersEncoded, EncoderError>
where
I: IntoIterator<Item = H>,
H: TryIntoHeader,
{
let mut encoding = self.encoding(stream_id);
for header in headers {
encoding.append(header)?;
}
encoding.encode()
}
#[inline]
pub fn encoding(&mut self, stream_id: StreamId) -> EncodingBlock<'_> {
let seqno = {
let seqno_ref = self.seqnos.entry(stream_id).or_default();
std::mem::replace(seqno_ref, seqno_ref.wrapping_add(1))
};
EncodingBlock::new(self, stream_id, seqno)
}
pub fn feed<D>(&mut self, data: D) -> Result<(), EncoderError>
where
D: AsRef<[u8]>,
{
self.inner.as_mut().feed_decoder_data(data.as_ref())
}
#[inline]
pub fn ratio(&self) -> f32 {
self.inner.as_ref().ratio()
}
#[inline]
fn inner_mut(&mut self) -> Pin<&mut InnerEncoder> {
self.inner.as_mut()
}
}
impl Default for Encoder {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct SDTCInstruction(Box<[u8]>);
impl SDTCInstruction {
#[inline]
pub fn data(&self) -> &[u8] {
&self.0
}
#[inline]
pub fn take(self) -> Box<[u8]> {
self.0
}
}
impl AsRef<[u8]> for SDTCInstruction {
#[inline]
fn as_ref(&self) -> &[u8] {
self.data()
}
}
impl From<SDTCInstruction> for Box<[u8]> {
fn from(sdtc_instruction: SDTCInstruction) -> Self {
sdtc_instruction.0
}
}
pub struct EncodingBlock<'a>(&'a mut Encoder);
impl<'a> EncodingBlock<'a> {
fn new(encoder: &'a mut Encoder, stream_id: StreamId, seqno: u32) -> Self {
encoder
.inner_mut()
.start_header_block(stream_id, seqno)
.map(|()| Self(encoder))
.unwrap() }
pub fn append<H>(&mut self, header: H) -> Result<&mut Self, EncoderError>
where
H: TryIntoHeader,
{
self.0.inner_mut().encode(header).map(|()| self)
}
pub fn encode(self) -> Result<BuffersEncoded, EncoderError> {
self.0
.inner_mut()
.end_header_block()
.map(|(header, stream)| BuffersEncoded {
header: header.into_boxed_slice(),
stream: stream.into_boxed_slice(),
})
}
}
pub struct BuffersEncoded {
header: Box<[u8]>,
stream: Box<[u8]>,
}
impl BuffersEncoded {
pub fn header(&self) -> &[u8] {
&self.header
}
pub fn stream(&self) -> &[u8] {
&self.stream
}
pub fn take(self) -> (Box<[u8]>, Box<[u8]>) {
self.into()
}
}
impl From<BuffersEncoded> for (Box<[u8]>, Box<[u8]>) {
fn from(buffers_encoded: BuffersEncoded) -> Self {
(buffers_encoded.header, buffers_encoded.stream)
}
}
struct InnerEncoder {
encoder: ls_qpack_rs_sys::lsqpack_enc,
enc_buffer: Vec<u8>,
hdr_buffer: Vec<u8>,
_marker: PhantomPinned,
}
impl InnerEncoder {
fn new() -> Pin<Box<Self>> {
let mut this = Box::new(Self {
encoder: ls_qpack_rs_sys::lsqpack_enc::default(),
enc_buffer: Vec::new(),
hdr_buffer: Vec::new(),
_marker: PhantomPinned,
});
unsafe {
ls_qpack_rs_sys::lsqpack_enc_preinit(&mut this.encoder, std::ptr::null_mut());
}
Box::into_pin(this)
}
fn init(
self: Pin<&mut Self>,
max_table_size: u32,
dyn_table_size: u32,
max_blocked_streams: u32,
) -> Result<Box<[u8]>, EncoderError> {
let this = unsafe { self.get_unchecked_mut() };
let mut buffer = vec![0; ls_qpack_rs_sys::LSQPACK_LONGEST_SDTC as usize];
let mut sdtc_buffer_size = buffer.len();
let result = unsafe {
ls_qpack_rs_sys::lsqpack_enc_init(
&mut this.encoder,
std::ptr::null_mut(),
max_table_size,
dyn_table_size,
max_blocked_streams,
ls_qpack_rs_sys::lsqpack_enc_opts_LSQPACK_ENC_OPT_STAGE_2,
buffer.as_mut_ptr(),
&mut sdtc_buffer_size,
)
};
if result == 0 {
buffer.truncate(sdtc_buffer_size);
Ok(buffer.into_boxed_slice())
} else {
Err(EncoderError)
}
}
fn start_header_block(
self: Pin<&mut Self>,
stream_id: StreamId,
seqno: u32,
) -> Result<(), EncoderError> {
let this = unsafe { self.get_unchecked_mut() };
let result = unsafe {
ls_qpack_rs_sys::lsqpack_enc_start_header(&mut this.encoder, stream_id.value(), seqno)
};
if result == 0 {
this.enc_buffer.clear();
this.hdr_buffer.clear();
Ok(())
} else {
Err(EncoderError)
}
}
fn encode<H>(self: Pin<&mut Self>, header: H) -> Result<(), EncoderError>
where
H: TryIntoHeader,
{
const BUFFER_SIZE: usize = 4096;
let mut header = header.try_into_header().map_err(|_| EncoderError)?;
let this = unsafe { self.get_unchecked_mut() };
let enc_buffer_offset = this.enc_buffer.len();
this.enc_buffer.resize(enc_buffer_offset + BUFFER_SIZE, 0);
let hdr_buffer_offset = this.hdr_buffer.len();
this.hdr_buffer.resize(hdr_buffer_offset + BUFFER_SIZE, 0);
let mut enc_buffer_size = this.enc_buffer.len() - enc_buffer_offset;
let mut hdr_buffer_size = this.hdr_buffer.len() - hdr_buffer_offset;
let result = unsafe {
ls_qpack_rs_sys::lsqpack_enc_encode(
&mut this.encoder,
this.enc_buffer.as_mut_ptr().add(enc_buffer_offset),
&mut enc_buffer_size,
this.hdr_buffer.as_mut_ptr().add(hdr_buffer_offset),
&mut hdr_buffer_size,
header.build_lsxpack_header().as_ref(),
0,
)
};
if result == ls_qpack_rs_sys::lsqpack_enc_status_LQES_OK {
this.enc_buffer
.truncate(enc_buffer_offset + enc_buffer_size);
this.hdr_buffer
.truncate(hdr_buffer_offset + hdr_buffer_size);
Ok(())
} else {
this.enc_buffer.truncate(enc_buffer_offset);
this.hdr_buffer.truncate(hdr_buffer_offset);
Err(EncoderError)
}
}
fn end_header_block(self: Pin<&mut Self>) -> Result<(Vec<u8>, Vec<u8>), EncoderError> {
let this = unsafe { self.get_unchecked_mut() };
let max_prefix_len =
unsafe { ls_qpack_rs_sys::lsqpack_enc_header_block_prefix_size(&this.encoder) };
let mut hdr_block = vec![0; max_prefix_len + this.hdr_buffer.len()];
let hdr_prefix_len = unsafe {
ls_qpack_rs_sys::lsqpack_enc_end_header(
&mut this.encoder,
hdr_block.as_mut_ptr(),
max_prefix_len,
std::ptr::null_mut(),
)
};
if hdr_prefix_len > 0 {
hdr_block.truncate(hdr_prefix_len as usize);
hdr_block.extend_from_slice(&this.hdr_buffer);
Ok((hdr_block, std::mem::take(&mut this.enc_buffer)))
} else {
Err(EncoderError)
}
}
fn feed_decoder_data(self: Pin<&mut Self>, data: &[u8]) -> Result<(), EncoderError> {
let this = unsafe { self.get_unchecked_mut() };
let result = unsafe {
ls_qpack_rs_sys::lsqpack_enc_decoder_in(&mut this.encoder, data.as_ptr(), data.len())
};
if result == 0 {
Ok(())
} else {
Err(EncoderError)
}
}
fn ratio(self: Pin<&Self>) -> f32 {
unsafe { ls_qpack_rs_sys::lsqpack_enc_ratio(&self.encoder) }
}
}
impl Drop for InnerEncoder {
fn drop(&mut self) {
unsafe { ls_qpack_rs_sys::lsqpack_enc_cleanup(&mut self.encoder) }
}
}
impl Debug for EncoderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EncoderError").finish()
}
}
impl Display for EncoderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Debug::fmt(self, f)
}
}
impl std::error::Error for EncoderError {}
#[cfg(test)]
mod tests {
use super::Encoder;
use super::StreamId;
#[test]
fn test_encoder_determinism_static() {
let mut encoder = Encoder::new();
let results = (0..1024)
.map(|_| {
encoder
.encode_all(StreamId::new(0), utilities::HEADERS_LIST_1)
.unwrap()
})
.collect::<Vec<_>>();
assert!(results.iter().all(|b| b.header() == results[0].header()));
assert!(results.iter().all(|b| b.stream().is_empty()));
}
mod utilities {
pub(super) const HEADERS_LIST_1: [(&str, &str); 2] =
[(":status", "404"), (":method", "connect")];
}
}