// Licensed under the Apache License, Version 2.0 or the MIT license // , 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, } 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); } }