summaryrefslogtreecommitdiffstats
path: root/third_party/rust/neqo-qpack/src/qpack_send_buf.rs
diff options
context:
space:
mode:
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.rs161
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..a443859081
--- /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 std::{convert::TryFrom, ops::Deref};
+
+use neqo_common::Encoder;
+
+use crate::{huffman::encode_huffman, prefix::Prefix};
+
+#[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);
+ }
+}