Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions neqo-common/src/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,34 @@ impl Encoder<Vec<u8>> {
Self::default()
}

/// Create a new [`Encoder`] that takes ownership of the provided [`Vec<u8>`].
///
/// The encoder will treat any existing data in the vector as "pre-existing"
/// and will append new encoded data after it. The start position is set to
/// the current length of the vector, so only newly encoded data will be
/// considered part of the encoder's content.
///
/// This is useful when you want to preserve existing buffer contents while
/// continuing to encode additional data.
#[must_use]
pub fn new_with_vec(buf: Vec<u8>) -> Self {
Comment thread
larseggert marked this conversation as resolved.
Outdated
let start = buf.len();
Self { buf, start }
}
Comment thread
martinthomson marked this conversation as resolved.
Outdated

/// Drain the first `n` bytes from the encoder buffer, returning an iterator
/// over the drained elements. This follows standard Rust drain semantics.
///
/// # Panics
///
/// Panics if `n` is greater than the current length of the encoder.
pub fn drain(&mut self, n: usize) -> std::vec::Drain<'_, u8> {
assert!(n <= self.len(), "Cannot drain beyond buffer length");
let drain_iter = self.buf.drain(self.start..self.start + n);
self.start = self.start.saturating_sub(n);
drain_iter
}
Comment thread
larseggert marked this conversation as resolved.
Comment thread
larseggert marked this conversation as resolved.
Comment on lines +428 to +437
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation for skip() should clarify that this method is only available for Encoder<Vec<u8>>, not for generic Encoder<B> where B: Buffer. This is important because the method is defined in an impl Encoder<Vec<u8>> block, not impl<B: Buffer> Encoder<B>.

Copilot uses AI. Check for mistakes.

/// Static helper function for previewing the results of encoding without doing it.
///
/// # Panics
Expand Down
16 changes: 10 additions & 6 deletions neqo-qpack/src/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@

use std::fmt::{self, Display, Formatter};

use neqo_common::{qdebug, Header};
use neqo_common::{qdebug, Encoder, Header};
use neqo_transport::{Connection, StreamId};

use crate::{
decoder_instructions::DecoderInstruction,
encoder_instructions::{DecodedEncoderInstruction, EncoderInstructionReader},
header_block::{HeaderDecoder, HeaderDecoderResult},
qpack_send_buf::Data,
reader::ReceiverConnWrapper,
stats::Stats,
table::HeaderTable,
Expand All @@ -28,7 +27,7 @@ pub struct Decoder {
table: HeaderTable,
acked_inserts: u64,
max_entries: u64,
send_buf: Data,
send_buf: Encoder,
local_stream_id: Option<StreamId>,
max_table_size: u64,
max_blocked_streams: usize,
Expand All @@ -43,7 +42,7 @@ impl Decoder {
#[must_use]
pub fn new(qpack_settings: &Settings) -> Self {
qdebug!("Decoder: creating a new qpack decoder");
let mut send_buf = Data::default();
let mut send_buf = Encoder::default();
send_buf.encode_varint(QPACK_UNI_STREAM_TYPE_DECODER);
let max_blocked_streams = usize::from(qpack_settings.max_blocked_streams);
Self {
Expand Down Expand Up @@ -175,11 +174,16 @@ impl Decoder {
let r = conn
.stream_send(
self.local_stream_id.ok_or(Error::Internal)?,
&self.send_buf[..],
self.send_buf.as_ref(),
)
.map_err(|_| Error::DecoderStream)?;
qdebug!("[{self}] {r} bytes sent");
self.send_buf.read(r);
if r < self.send_buf.len() {
// Efficiently remove the sent bytes from the buffer and update internal state
self.send_buf.remove_prefix(r);
} else {
self.send_buf = Encoder::default();
}
}
Ok(())
}
Expand Down
17 changes: 9 additions & 8 deletions neqo-qpack/src/decoder_instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use neqo_transport::StreamId;

use crate::{
prefix::{DECODER_HEADER_ACK, DECODER_INSERT_COUNT_INCREMENT, DECODER_STREAM_CANCELLATION},
qpack_send_buf::Data,
qpack_send_buf::QpackEncoder,
reader::{IntReader, ReadByte},
Res,
};
Expand Down Expand Up @@ -44,7 +44,7 @@ impl DecoderInstruction {
}
}

pub(crate) fn marshal(&self, enc: &mut Data) {
pub(crate) fn marshal<T: QpackEncoder>(&self, enc: &mut T) {
match self {
Self::InsertCountIncrement { increment } => {
enc.encode_prefixed_encoded_int(DECODER_INSERT_COUNT_INCREMENT, *increment);
Expand Down Expand Up @@ -144,16 +144,17 @@ impl DecoderInstructionReader {
#[cfg_attr(coverage_nightly, coverage(off))]
mod test {

use neqo_common::Encoder;
use neqo_transport::StreamId;

use super::{Data, DecoderInstruction, DecoderInstructionReader};
use super::{DecoderInstruction, DecoderInstructionReader};
use crate::{reader::test_receiver::TestReceiver, Error};

fn test_encoding_decoding(instruction: DecoderInstruction) {
let mut buf = Data::default();
let mut buf = Encoder::default();
instruction.marshal(&mut buf);
let mut test_receiver: TestReceiver = TestReceiver::default();
test_receiver.write(&buf);
test_receiver.write(buf.as_ref());
let mut decoder = DecoderInstructionReader::new();
assert_eq!(
decoder.read_instructions(&mut test_receiver).unwrap(),
Expand Down Expand Up @@ -182,18 +183,18 @@ mod test {
}

fn test_encoding_decoding_slow_reader(instruction: DecoderInstruction) {
let mut buf = Data::default();
let mut buf = Encoder::default();
instruction.marshal(&mut buf);
let mut test_receiver: TestReceiver = TestReceiver::default();
let mut decoder = DecoderInstructionReader::new();
for i in 0..buf.len() - 1 {
test_receiver.write(&buf[i..=i]);
test_receiver.write(&buf.as_ref()[i..=i]);
assert_eq!(
decoder.read_instructions(&mut test_receiver),
Err(Error::NeedMoreData)
);
}
test_receiver.write(&buf[buf.len() - 1..buf.len()]);
test_receiver.write(&buf.as_ref()[buf.len() - 1..buf.len()]);
assert_eq!(
decoder.read_instructions(&mut test_receiver).unwrap(),
instruction
Expand Down
21 changes: 10 additions & 11 deletions neqo-qpack/src/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ use crate::{
encoder_instructions::EncoderInstruction,
header_block::HeaderEncoder,
qlog,
qpack_send_buf::Data,
reader::ReceiverConnWrapper,
stats::Stats,
table::{HeaderTable, LookupResult, ADDITIONAL_TABLE_ENTRY_SIZE},
Expand Down Expand Up @@ -269,14 +268,14 @@ impl Encoder {
return Err(Error::DynamicTableFull);
}

let mut buf = Data::default();
let mut buf = neqo_common::Encoder::default();
EncoderInstruction::InsertWithNameLiteral { name, value }
.marshal(&mut buf, self.use_huffman);

let stream_id = self.local_stream.stream_id().ok_or(Error::Internal)?;

let sent = conn
.stream_send_atomic(stream_id, &buf)
.stream_send_atomic(stream_id, buf.as_ref())
.map_err(|e| map_stream_send_atomic_error(&e))?;
if !sent {
return Err(Error::EncoderStreamBlocked);
Expand Down Expand Up @@ -309,9 +308,9 @@ impl Encoder {
if cap < self.table.capacity() && !self.table.can_evict_to(cap) {
return Err(Error::DynamicTableFull);
}
let mut buf = Data::default();
let mut buf = neqo_common::Encoder::default();
EncoderInstruction::Capacity { value: cap }.marshal(&mut buf, self.use_huffman);
if !conn.stream_send_atomic(stream_id, &buf)? {
if !conn.stream_send_atomic(stream_id, buf.as_ref())? {
return Err(Error::EncoderStreamBlocked);
}
if self.table.set_capacity(cap).is_err() {
Expand Down Expand Up @@ -339,9 +338,9 @@ impl Encoder {
Ok(())
}
LocalStreamState::Uninitialized(stream_id) => {
let mut buf = Data::default();
let mut buf = neqo_common::Encoder::default();
buf.encode_varint(QPACK_UNI_STREAM_TYPE_ENCODER);
if !conn.stream_send_atomic(stream_id, &buf[..])? {
if !conn.stream_send_atomic(stream_id, buf.as_ref())? {
return Err(Error::EncoderStreamBlocked);
}
self.local_stream = LocalStreamState::Initialized(stream_id);
Expand Down Expand Up @@ -580,7 +579,7 @@ mod tests {
let buf = self
.encoder
.encode_header_block(&mut self.conn, headers, stream_id);
assert_eq!(&buf[..], expected_encoding);
assert_eq!(buf.as_ref(), expected_encoding);
self.send_instructions(inst);
}

Expand Down Expand Up @@ -806,7 +805,7 @@ mod tests {
let buf = encoder
.encoder
.encode_header_block(&mut encoder.conn, &t.headers, STREAM_1);
assert_eq!(&buf[..], t.header_block);
assert_eq!(buf.as_ref(), t.header_block);
encoder.send_instructions(t.encoder_inst);
}
}
Expand Down Expand Up @@ -880,7 +879,7 @@ mod tests {
let buf = encoder
.encoder
.encode_header_block(&mut encoder.conn, &t.headers, STREAM_1);
assert_eq!(&buf[..], t.header_block);
assert_eq!(buf.as_ref(), t.header_block);
encoder.send_instructions(t.encoder_inst);
}
}
Expand Down Expand Up @@ -952,7 +951,7 @@ mod tests {
&[Header::new("content-length", "1234")],
STREAM_1,
);
assert_eq!(&buf[..], ENCODE_INDEXED_REF_DYNAMIC);
assert_eq!(buf.as_ref(), ENCODE_INDEXED_REF_DYNAMIC);
encoder.send_instructions(&[]);

// insert "content-length: 12345 which will fail because the entry in the table cannot be
Expand Down
16 changes: 8 additions & 8 deletions neqo-qpack/src/encoder_instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::{
ENCODER_CAPACITY, ENCODER_DUPLICATE, ENCODER_INSERT_WITH_NAME_LITERAL,
ENCODER_INSERT_WITH_NAME_REF_DYNAMIC, ENCODER_INSERT_WITH_NAME_REF_STATIC, NO_PREFIX,
},
qpack_send_buf::Data,
qpack_send_buf::QpackEncoder,
reader::{IntReader, LiteralReader, ReadByte, Reader},
Res,
};
Expand Down Expand Up @@ -52,7 +52,7 @@ pub enum EncoderInstruction<'a> {
}

impl EncoderInstruction<'_> {
pub(crate) fn marshal(&self, enc: &mut Data, use_huffman: bool) {
pub(crate) fn marshal<T: QpackEncoder>(&self, enc: &mut T, use_huffman: bool) {
match self {
Self::Capacity { value } => {
enc.encode_prefixed_encoded_int(ENCODER_CAPACITY, *value);
Expand Down Expand Up @@ -297,14 +297,14 @@ impl EncoderInstructionReader {
#[cfg_attr(coverage_nightly, coverage(off))]
mod test {

use super::{Data, EncoderInstruction, EncoderInstructionReader};
use super::{EncoderInstruction, EncoderInstructionReader};
use crate::{reader::test_receiver::TestReceiver, Error};

fn test_encoding_decoding(instruction: &EncoderInstruction, use_huffman: bool) {
let mut buf = Data::default();
let mut buf = neqo_common::Encoder::default();
instruction.marshal(&mut buf, use_huffman);
let mut test_receiver: TestReceiver = TestReceiver::default();
test_receiver.write(&buf);
test_receiver.write(buf.as_ref());
let mut reader = EncoderInstructionReader::new();
assert_eq!(
reader.read_instructions(&mut test_receiver).unwrap(),
Expand Down Expand Up @@ -395,18 +395,18 @@ mod test {
}

fn test_encoding_decoding_slow_reader(instruction: &EncoderInstruction, use_huffman: bool) {
let mut buf = Data::default();
let mut buf = neqo_common::Encoder::default();
instruction.marshal(&mut buf, use_huffman);
let mut test_receiver: TestReceiver = TestReceiver::default();
let mut decoder = EncoderInstructionReader::new();
for i in 0..buf.len() - 1 {
test_receiver.write(&buf[i..=i]);
test_receiver.write(&buf.as_ref()[i..=i]);
assert_eq!(
decoder.read_instructions(&mut test_receiver),
Err(Error::NeedMoreData)
);
}
test_receiver.write(&buf[buf.len() - 1..buf.len()]);
test_receiver.write(&buf.as_ref()[buf.len() - 1..buf.len()]);
assert_eq!(
decoder.read_instructions(&mut test_receiver).unwrap(),
instruction.into()
Expand Down
10 changes: 5 additions & 5 deletions neqo-qpack/src/header_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ use crate::{
HEADER_FIELD_LITERAL_NAME_REF_DYNAMIC_POST, HEADER_FIELD_LITERAL_NAME_REF_STATIC,
NO_PREFIX,
},
qpack_send_buf::Data,
qpack_send_buf::QpackEncoder as _,
reader::{parse_utf8, ReceiverBufferWrapper},
table::HeaderTable,
Error, Res,
};

#[derive(Default, Debug, PartialEq, Eq)]
pub struct HeaderEncoder {
buf: Data,
buf: neqo_common::Encoder,
base: u64,
use_huffman: bool,
max_entries: u64,
Expand All @@ -44,7 +44,7 @@ impl Display for HeaderEncoder {
impl HeaderEncoder {
pub fn new(base: u64, use_huffman: bool, max_entries: u64) -> Self {
Self {
buf: Data::default(),
buf: neqo_common::Encoder::default(),
base,
use_huffman,
max_entries,
Expand Down Expand Up @@ -139,14 +139,14 @@ impl HeaderEncoder {
.encode_prefixed_encoded_int(NO_PREFIX, enc_insert_cnt);
self.buf.encode_prefixed_encoded_int(prefix, delta);

self.buf.write_bytes(&tmp);
self.buf.encode(tmp.as_ref());
Comment thread
larseggert marked this conversation as resolved.
Outdated
}
}

impl Deref for HeaderEncoder {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.buf
self.buf.as_ref()
}
}

Expand Down
2 changes: 1 addition & 1 deletion neqo-qpack/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ mod table;
pub use stats::Stats;
use thiserror::Error;

pub use crate::{decoder::Decoder, encoder::Encoder};
pub use crate::{decoder::Decoder, encoder::Encoder, qpack_send_buf::QpackEncoder};

type Res<T> = Result<T, Error>;

Expand Down
Loading
Loading