diff options
Diffstat (limited to 'third_party/rust/neqo-qpack/src/qpack_send_buf.rs')
-rw-r--r-- | third_party/rust/neqo-qpack/src/qpack_send_buf.rs | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/third_party/rust/neqo-qpack/src/qpack_send_buf.rs b/third_party/rust/neqo-qpack/src/qpack_send_buf.rs new file mode 100644 index 0000000000..4fbdbf12bd --- /dev/null +++ b/third_party/rust/neqo-qpack/src/qpack_send_buf.rs @@ -0,0 +1,161 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::huffman::encode_huffman; +use crate::prefix::Prefix; +use neqo_common::Encoder; +use std::convert::TryFrom; +use std::ops::Deref; + +#[derive(Default, Debug, PartialEq)] +pub(crate) struct QpackData { + buf: Vec<u8>, +} + +impl QpackData { + pub fn len(&self) -> usize { + self.buf.len() + } + + fn write_byte(&mut self, b: u8) { + self.buf.push(b); + } + + pub fn encode_varint(&mut self, i: u64) { + let mut enc = Encoder::default(); + enc.encode_varint(i); + self.buf.append(&mut enc.into()); + } + + pub(crate) fn encode_prefixed_encoded_int(&mut self, prefix: Prefix, mut val: u64) -> usize { + let first_byte_max: u8 = if prefix.len() == 0 { + 0xff + } else { + (1 << (8 - prefix.len())) - 1 + }; + + if val < u64::from(first_byte_max) { + let v = u8::try_from(val).unwrap(); + self.write_byte((prefix.prefix() & !first_byte_max) | v); + return 1; + } + + self.write_byte(prefix.prefix() | first_byte_max); + val -= u64::from(first_byte_max); + + let mut written = 1; + let mut done = false; + while !done { + let mut b = u8::try_from(val & 0x7f).unwrap(); + val >>= 7; + if val > 0 { + b |= 0x80; + } else { + done = true; + } + + self.write_byte(b); + written += 1; + } + written + } + + pub fn encode_literal(&mut self, use_huffman: bool, prefix: Prefix, value: &[u8]) { + let real_prefix = Prefix::new( + if use_huffman { + prefix.prefix() | (0x80 >> prefix.len()) + } else { + prefix.prefix() + }, + prefix.len() + 1, + ); + + if use_huffman { + let encoded = encode_huffman(value); + self.encode_prefixed_encoded_int(real_prefix, u64::try_from(encoded.len()).unwrap()); + self.write_bytes(&encoded); + } else { + self.encode_prefixed_encoded_int(real_prefix, u64::try_from(value.len()).unwrap()); + self.write_bytes(value); + } + } + + pub fn write_bytes(&mut self, buf: &[u8]) { + self.buf.extend_from_slice(buf); + } + + pub fn read(&mut self, r: usize) { + assert!( + r <= self.buf.len(), + "want to set more bytes read than remain in the buffer." + ); + self.buf = self.buf.split_off(r); + } +} + +impl Deref for QpackData { + type Target = [u8]; + fn deref(&self) -> &Self::Target { + &self.buf + } +} + +#[cfg(test)] +mod tests { + use super::{Prefix, QpackData}; + + #[test] + fn test_encode_prefixed_encoded_int_1() { + let mut d = QpackData::default(); + d.encode_prefixed_encoded_int(Prefix::new(0xC0, 2), 5); + assert_eq!(d[..], [0xc5]); + } + + #[test] + fn test_encode_prefixed_encoded_int_2() { + let mut d = QpackData::default(); + d.encode_prefixed_encoded_int(Prefix::new(0xC0, 2), 65); + assert_eq!(d[..], [0xff, 0x02]); + } + + #[test] + fn test_encode_prefixed_encoded_int_3() { + let mut d = QpackData::default(); + d.encode_prefixed_encoded_int(Prefix::new(0xC0, 2), 100_000); + assert_eq!(d[..], [0xff, 0xe1, 0x8c, 0x06]); + } + + #[test] + fn max_int() { + let mut d = QpackData::default(); + d.encode_prefixed_encoded_int(Prefix::new(0x80, 1), u64::MAX); + assert_eq!( + d[..], + [0xff, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01] + ); + } + + const VALUE: &[u8] = b"custom-key"; + + const LITERAL: &[u8] = &[ + 0xca, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, + ]; + const LITERAL_HUFFMAN: &[u8] = &[0xe8, 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f]; + + #[test] + fn test_encode_literal() { + let mut d = QpackData::default(); + d.encode_literal(false, Prefix::new(0xC0, 2), VALUE); + assert_eq!(&&d[..], &LITERAL); + } + + #[test] + fn test_encode_literal_huffman() { + let mut d = QpackData::default(); + d.encode_literal(true, Prefix::new(0xC0, 2), VALUE); + assert_eq!(&&d[..], &LITERAL_HUFFMAN); + } +} |