1use crate::header::TryIntoHeader;
27use crate::StreamId;
28use std::collections::HashMap;
29use std::fmt::Debug;
30use std::fmt::Display;
31use std::marker::PhantomPinned;
32use std::pin::Pin;
33
34pub struct EncoderError;
36
37pub struct Encoder {
39 inner: Pin<Box<InnerEncoder>>,
40 seqnos: HashMap<StreamId, u32>,
41}
42
43impl Encoder {
44 #[inline]
51 pub fn new() -> Self {
52 Self {
53 inner: InnerEncoder::new(),
54 seqnos: HashMap::new(),
55 }
56 }
57
58 #[inline]
68 pub fn configure(
69 &mut self,
70 max_table_size: u32,
71 dyn_table_size: u32,
72 max_blocked_streams: u32,
73 ) -> Result<SDTCInstruction, EncoderError> {
74 self.inner
75 .as_mut()
76 .init(max_table_size, dyn_table_size, max_blocked_streams)
77 .map(SDTCInstruction)
78 }
79
80 pub fn encode_all<I, H>(
100 &mut self,
101 stream_id: StreamId,
102 headers: I,
103 ) -> Result<BuffersEncoded, EncoderError>
104 where
105 I: IntoIterator<Item = H>,
106 H: TryIntoHeader,
107 {
108 let mut encoding = self.encoding(stream_id);
109
110 for header in headers {
111 encoding.append(header)?;
112 }
113
114 encoding.encode()
115 }
116
117 #[inline]
138 pub fn encoding(&mut self, stream_id: StreamId) -> EncodingBlock<'_> {
139 let seqno = {
140 let seqno_ref = self.seqnos.entry(stream_id).or_default();
141 std::mem::replace(seqno_ref, seqno_ref.wrapping_add(1))
142 };
143
144 EncodingBlock::new(self, stream_id, seqno)
145 }
146
147 #[inline]
153 pub fn ratio(&self) -> f32 {
154 self.inner.as_ref().ratio()
155 }
156
157 #[inline]
158 fn inner_mut(&mut self) -> Pin<&mut InnerEncoder> {
159 self.inner.as_mut()
160 }
161}
162
163impl Default for Encoder {
164 fn default() -> Self {
165 Self::new()
166 }
167}
168
169#[derive(Debug)]
174pub struct SDTCInstruction(Box<[u8]>);
175
176impl SDTCInstruction {
177 #[inline]
179 pub fn data(&self) -> &[u8] {
180 &self.0
181 }
182
183 #[inline]
185 pub fn take(self) -> Box<[u8]> {
186 self.0
187 }
188}
189
190impl AsRef<[u8]> for SDTCInstruction {
191 #[inline]
192 fn as_ref(&self) -> &[u8] {
193 self.data()
194 }
195}
196
197impl From<SDTCInstruction> for Box<[u8]> {
198 fn from(sdtc_instruction: SDTCInstruction) -> Self {
199 sdtc_instruction.0
200 }
201}
202
203pub struct EncodingBlock<'a>(&'a mut Encoder);
207
208impl<'a> EncodingBlock<'a> {
209 fn new(encoder: &'a mut Encoder, stream_id: StreamId, seqno: u32) -> Self {
210 encoder
211 .inner_mut()
212 .start_header_block(stream_id, seqno)
213 .map(|()| Self(encoder))
214 .unwrap() }
216
217 pub fn append<H>(&mut self, header: H) -> Result<&mut Self, EncoderError>
219 where
220 H: TryIntoHeader,
221 {
222 self.0.inner_mut().encode(header).map(|()| self)
223 }
224
225 pub fn encode(self) -> Result<BuffersEncoded, EncoderError> {
227 self.0
228 .inner_mut()
229 .end_header_block()
230 .map(|(header, stream)| BuffersEncoded {
231 header: header.into_boxed_slice(),
232 stream: stream.into_boxed_slice(),
233 })
234 }
235}
236
237pub struct BuffersEncoded {
241 header: Box<[u8]>,
242 stream: Box<[u8]>,
243}
244
245impl BuffersEncoded {
246 pub fn header(&self) -> &[u8] {
248 &self.header
249 }
250
251 pub fn stream(&self) -> &[u8] {
253 &self.stream
254 }
255
256 pub fn take(self) -> (Box<[u8]>, Box<[u8]>) {
257 self.into()
258 }
259}
260
261impl From<BuffersEncoded> for (Box<[u8]>, Box<[u8]>) {
262 fn from(buffers_encoded: BuffersEncoded) -> Self {
263 (buffers_encoded.header, buffers_encoded.stream)
264 }
265}
266
267struct InnerEncoder {
268 encoder: ls_qpack_sys::lsqpack_enc,
269 enc_buffer: Vec<u8>,
270 hdr_buffer: Vec<u8>,
271 _marker: PhantomPinned,
272}
273
274impl InnerEncoder {
275 fn new() -> Pin<Box<Self>> {
276 let mut this = Box::new(Self {
277 encoder: ls_qpack_sys::lsqpack_enc::default(),
278 enc_buffer: Vec::new(),
279 hdr_buffer: Vec::new(),
280 _marker: PhantomPinned,
281 });
282
283 unsafe {
284 ls_qpack_sys::lsqpack_enc_preinit(&mut this.encoder, std::ptr::null_mut());
285 }
286
287 Box::into_pin(this)
288 }
289
290 fn init(
291 self: Pin<&mut Self>,
292 max_table_size: u32,
293 dyn_table_size: u32,
294 max_blocked_streams: u32,
295 ) -> Result<Box<[u8]>, EncoderError> {
296 let this = unsafe { self.get_unchecked_mut() };
297
298 let mut buffer = vec![0; ls_qpack_sys::LSQPACK_LONGEST_SDTC as usize];
299 let mut sdtc_buffer_size = buffer.len();
300
301 let result = unsafe {
302 ls_qpack_sys::lsqpack_enc_init(
303 &mut this.encoder,
304 std::ptr::null_mut(),
305 max_table_size,
306 dyn_table_size,
307 max_blocked_streams,
308 ls_qpack_sys::lsqpack_enc_opts_LSQPACK_ENC_OPT_STAGE_2,
309 buffer.as_mut_ptr(),
310 &mut sdtc_buffer_size,
311 )
312 };
313
314 if result == 0 {
315 buffer.truncate(sdtc_buffer_size);
316 Ok(buffer.into_boxed_slice())
317 } else {
318 Err(EncoderError)
319 }
320 }
321
322 fn start_header_block(
324 self: Pin<&mut Self>,
325 stream_id: StreamId,
326 seqno: u32,
327 ) -> Result<(), EncoderError> {
328 let this = unsafe { self.get_unchecked_mut() };
329
330 let result = unsafe {
331 ls_qpack_sys::lsqpack_enc_start_header(&mut this.encoder, stream_id.value(), seqno)
332 };
333
334 if result == 0 {
335 this.enc_buffer.clear();
336 this.hdr_buffer.clear();
337
338 Ok(())
339 } else {
340 Err(EncoderError)
341 }
342 }
343
344 fn encode<H>(self: Pin<&mut Self>, header: H) -> Result<(), EncoderError>
345 where
346 H: TryIntoHeader,
347 {
348 const BUFFER_SIZE: usize = 1024;
349
350 let mut header = header.try_into_header().map_err(|_| EncoderError)?;
351
352 let this = unsafe { self.get_unchecked_mut() };
353
354 let enc_buffer_offset = this.enc_buffer.len();
358 this.enc_buffer.resize(enc_buffer_offset + BUFFER_SIZE, 0);
359
360 let hdr_buffer_offset = this.hdr_buffer.len();
361 this.hdr_buffer.resize(hdr_buffer_offset + BUFFER_SIZE, 0);
362
363 let mut enc_buffer_size = this.enc_buffer.len() - enc_buffer_offset;
364 let mut hdr_buffer_size = this.hdr_buffer.len() - hdr_buffer_offset;
365
366 let result = unsafe {
367 ls_qpack_sys::lsqpack_enc_encode(
368 &mut this.encoder,
369 this.enc_buffer.as_mut_ptr().add(enc_buffer_offset),
370 &mut enc_buffer_size,
371 this.hdr_buffer.as_mut_ptr().add(hdr_buffer_offset),
372 &mut hdr_buffer_size,
373 header.build_lsxpack_header().as_ref(),
374 0,
375 )
376 };
377
378 if result == ls_qpack_sys::lsqpack_enc_status_LQES_OK {
379 this.enc_buffer
380 .truncate(enc_buffer_offset + enc_buffer_size);
381 this.hdr_buffer
382 .truncate(hdr_buffer_offset + hdr_buffer_size);
383
384 Ok(())
385 } else {
386 this.enc_buffer.truncate(enc_buffer_offset);
387 this.hdr_buffer.truncate(hdr_buffer_offset);
388
389 Err(EncoderError)
390 }
391 }
392
393 fn end_header_block(self: Pin<&mut Self>) -> Result<(Vec<u8>, Vec<u8>), EncoderError> {
400 let this = unsafe { self.get_unchecked_mut() };
401
402 let max_prefix_len =
403 unsafe { ls_qpack_sys::lsqpack_enc_header_block_prefix_size(&this.encoder) };
404
405 let mut hdr_block = vec![0; max_prefix_len + this.hdr_buffer.len()];
406
407 let hdr_prefix_len = unsafe {
408 ls_qpack_sys::lsqpack_enc_end_header(
409 &mut this.encoder,
410 hdr_block.as_mut_ptr(),
411 max_prefix_len,
412 std::ptr::null_mut(),
413 )
414 };
415
416 if hdr_prefix_len > 0 {
417 hdr_block.truncate(hdr_prefix_len as usize);
418 hdr_block.extend_from_slice(&this.hdr_buffer);
419
420 Ok((hdr_block, std::mem::take(&mut this.enc_buffer)))
421 } else {
422 Err(EncoderError)
423 }
424 }
425
426 fn ratio(self: Pin<&Self>) -> f32 {
427 unsafe { ls_qpack_sys::lsqpack_enc_ratio(&self.encoder) }
428 }
429}
430
431impl Drop for InnerEncoder {
432 fn drop(&mut self) {
433 unsafe { ls_qpack_sys::lsqpack_enc_cleanup(&mut self.encoder) }
434 }
435}
436
437impl Debug for EncoderError {
438 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
439 f.debug_struct("EncoderError").finish()
440 }
441}
442
443impl Display for EncoderError {
444 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
445 Debug::fmt(self, f)
446 }
447}
448
449impl std::error::Error for EncoderError {}
450
451#[cfg(test)]
452mod tests {
453 use super::Encoder;
454 use super::StreamId;
455
456 #[test]
457 fn test_encoder_determinism_static() {
458 let mut encoder = Encoder::new();
459
460 let results = (0..1024)
461 .map(|_| {
462 encoder
463 .encode_all(StreamId::new(0), utilities::HEADERS_LIST_1)
464 .unwrap()
465 })
466 .collect::<Vec<_>>();
467
468 assert!(results.iter().all(|b| b.header() == results[0].header()));
469 assert!(results.iter().all(|b| b.stream().is_empty()));
470 }
471
472 mod utilities {
473 pub(super) const HEADERS_LIST_1: [(&str, &str); 2] =
474 [(":status", "404"), (":method", "connect")];
475 }
476}