Don't allocate on each call to huffman decode
This commit is contained in:
@@ -2,7 +2,7 @@ use super::{huffman, header as h2_header, Header};
|
|||||||
use util::byte_str::FromUtf8Error;
|
use util::byte_str::FromUtf8Error;
|
||||||
|
|
||||||
use http::{method, header, status, StatusCode, Method};
|
use http::{method, header, status, StatusCode, Method};
|
||||||
use bytes::{Buf, Bytes};
|
use bytes::{Buf, Bytes, BytesMut};
|
||||||
|
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
@@ -14,6 +14,7 @@ pub struct Decoder {
|
|||||||
max_size_update: Option<usize>,
|
max_size_update: Option<usize>,
|
||||||
last_max_update: usize,
|
last_max_update: usize,
|
||||||
table: Table,
|
table: Table,
|
||||||
|
buffer: BytesMut,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents all errors that can be encountered while performing the decoding
|
/// Represents all errors that can be encountered while performing the decoding
|
||||||
@@ -139,6 +140,7 @@ impl Decoder {
|
|||||||
max_size_update: None,
|
max_size_update: None,
|
||||||
last_max_update: size,
|
last_max_update: size,
|
||||||
table: Table::new(size),
|
table: Table::new(size),
|
||||||
|
buffer: BytesMut::with_capacity(4096),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,7 +236,7 @@ impl Decoder {
|
|||||||
self.table.get(index)
|
self.table.get(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_literal(&self, buf: &mut Cursor<&Bytes>, index: bool)
|
fn decode_literal(&mut self, buf: &mut Cursor<&Bytes>, index: bool)
|
||||||
-> Result<Header, DecoderError>
|
-> Result<Header, DecoderError>
|
||||||
{
|
{
|
||||||
let prefix = if index {
|
let prefix = if index {
|
||||||
@@ -249,17 +251,43 @@ impl Decoder {
|
|||||||
// First, read the header name
|
// First, read the header name
|
||||||
if table_idx == 0 {
|
if table_idx == 0 {
|
||||||
// Read the name as a literal
|
// Read the name as a literal
|
||||||
let name = try!(decode_string(buf));
|
let name = try!(self.decode_string(buf));
|
||||||
let value = try!(decode_string(buf));
|
let value = try!(self.decode_string(buf));
|
||||||
|
|
||||||
Header::new(name, value)
|
Header::new(name, value)
|
||||||
} else {
|
} else {
|
||||||
let e = try!(self.table.get(table_idx));
|
let e = try!(self.table.get(table_idx));
|
||||||
let value = try!(decode_string(buf));
|
let value = try!(self.decode_string(buf));
|
||||||
|
|
||||||
e.name().into_entry(value)
|
e.name().into_entry(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn decode_string(&mut self, buf: &mut Cursor<&Bytes>) -> Result<Bytes, DecoderError> {
|
||||||
|
const HUFF_FLAG: u8 = 0b10000000;
|
||||||
|
|
||||||
|
// The first bit in the first byte contains the huffman encoded flag.
|
||||||
|
let huff = peek_u8(buf) & HUFF_FLAG == HUFF_FLAG;
|
||||||
|
|
||||||
|
// Decode the string length using 7 bit prefix
|
||||||
|
let len = try!(decode_int(buf, 7));
|
||||||
|
|
||||||
|
if len > buf.remaining() {
|
||||||
|
return Err(DecoderError::StringUnderflow);
|
||||||
|
}
|
||||||
|
|
||||||
|
if huff {
|
||||||
|
let ret = {
|
||||||
|
let raw = &buf.bytes()[..len];
|
||||||
|
huffman::decode(raw, &mut self.buffer).map(Into::into)
|
||||||
|
};
|
||||||
|
|
||||||
|
buf.advance(len);
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
Ok(take(buf, len))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Decoder {
|
impl Default for Decoder {
|
||||||
@@ -356,32 +384,6 @@ fn decode_int<B: Buf>(buf: &mut B, prefix_size: u8) -> Result<usize, DecoderErro
|
|||||||
Err(DecoderError::IntegerUnderflow)
|
Err(DecoderError::IntegerUnderflow)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_string(buf: &mut Cursor<&Bytes>) -> Result<Bytes, DecoderError> {
|
|
||||||
const HUFF_FLAG: u8 = 0b10000000;
|
|
||||||
|
|
||||||
// The first bit in the first byte contains the huffman encoded flag.
|
|
||||||
let huff = peek_u8(buf) & HUFF_FLAG == HUFF_FLAG;
|
|
||||||
|
|
||||||
// Decode the string length using 7 bit prefix
|
|
||||||
let len = try!(decode_int(buf, 7));
|
|
||||||
|
|
||||||
if len > buf.remaining() {
|
|
||||||
return Err(DecoderError::StringUnderflow);
|
|
||||||
}
|
|
||||||
|
|
||||||
if huff {
|
|
||||||
let ret = {
|
|
||||||
let raw = &buf.bytes()[..len];
|
|
||||||
huffman::decode(raw).map(Into::into)
|
|
||||||
};
|
|
||||||
|
|
||||||
buf.advance(len);
|
|
||||||
return ret;
|
|
||||||
} else {
|
|
||||||
Ok(take(buf, len))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn peek_u8<B: Buf>(buf: &mut B) -> u8 {
|
fn peek_u8<B: Buf>(buf: &mut B) -> u8 {
|
||||||
buf.bytes()[0]
|
buf.bytes()[0]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -664,6 +664,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn huff_decode(src: &[u8]) -> BytesMut {
|
fn huff_decode(src: &[u8]) -> BytesMut {
|
||||||
huffman::decode(src).unwrap()
|
let mut buf = BytesMut::new();
|
||||||
|
huffman::decode(src, &mut buf).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,21 +17,19 @@ const MAYBE_EOS: u8 = 1;
|
|||||||
const DECODED: u8 = 2;
|
const DECODED: u8 = 2;
|
||||||
const ERROR: u8 = 4;
|
const ERROR: u8 = 4;
|
||||||
|
|
||||||
pub fn decode(src: &[u8]) -> Result<BytesMut, DecoderError> {
|
pub fn decode(src: &[u8], buf: &mut BytesMut) -> Result<BytesMut, DecoderError> {
|
||||||
// TODO: This should not allocate and instead take a dst
|
|
||||||
|
|
||||||
let mut decoder = Decoder::new();
|
let mut decoder = Decoder::new();
|
||||||
|
|
||||||
// Max compression ratio is >= 0.5
|
// Max compression ratio is >= 0.5
|
||||||
let mut dst = BytesMut::with_capacity(src.len() << 1);
|
buf.reserve(src.len() << 1);
|
||||||
|
|
||||||
for b in src {
|
for b in src {
|
||||||
if let Some(b) = try!(decoder.decode4(b >> 4)) {
|
if let Some(b) = try!(decoder.decode4(b >> 4)) {
|
||||||
dst.put_u8(b);
|
buf.put_u8(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(b) = try!(decoder.decode4(b & 0xf)) {
|
if let Some(b) = try!(decoder.decode4(b & 0xf)) {
|
||||||
dst.put_u8(b);
|
buf.put_u8(b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +37,7 @@ pub fn decode(src: &[u8]) -> Result<BytesMut, DecoderError> {
|
|||||||
return Err(DecoderError::InvalidHuffmanCode);
|
return Err(DecoderError::InvalidHuffmanCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(dst)
|
Ok(buf.take())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: return error when there is not enough room to encode the value
|
// TODO: return error when there is not enough room to encode the value
|
||||||
@@ -119,6 +117,11 @@ impl Decoder {
|
|||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
fn decode(src: &[u8]) -> Result<BytesMut, DecoderError> {
|
||||||
|
let mut buf = BytesMut::new();
|
||||||
|
super::decode(src, &mut buf)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn decode_single_byte() {
|
fn decode_single_byte() {
|
||||||
assert_eq!("o", decode(&[0b00111111]).unwrap());
|
assert_eq!("o", decode(&[0b00111111]).unwrap());
|
||||||
|
|||||||
Reference in New Issue
Block a user