Don't allocate on each call to huffman decode

This commit is contained in:
Carl Lerche
2017-06-13 14:39:39 -07:00
parent ab8eac6c05
commit c12a9a86ae
3 changed files with 45 additions and 39 deletions

View File

@@ -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]
} }

View File

@@ -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()
} }
} }

View File

@@ -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());