From 9eb031ce837883233a30b19317f32acb920be62b Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Tue, 7 Jan 2014 21:44:56 +0900 Subject: [PATCH] nghttp2_hd: Avoid nghttp2_hd_huff_decode_count Huffman decoding is costly. It is faster to do geometric realloc than calling nghttp2_hd_huff_decode_count to know the length in advance. --- lib/nghttp2_hd.c | 26 +++----------------------- lib/nghttp2_hd.h | 21 +++++++++++++-------- lib/nghttp2_hd_huffman.c | 29 ++++++++++++++++++++++++----- 3 files changed, 40 insertions(+), 36 deletions(-) diff --git a/lib/nghttp2_hd.c b/lib/nghttp2_hd.c index 00fa1fee..2c9b733a 100644 --- a/lib/nghttp2_hd.c +++ b/lib/nghttp2_hd.c @@ -1237,26 +1237,6 @@ static int inflater_post_process_hd_entry(nghttp2_hd_context *inflater, return 0; } -static ssize_t inflate_decode(uint8_t **dest_ptr, uint8_t *in, size_t inlen, - nghttp2_hd_side side) -{ - ssize_t declen; - if(inlen == 0) { - *dest_ptr = NULL; - return 0; - } - declen = nghttp2_hd_huff_decode_count(in, inlen, side); - if(declen == -1) { - return NGHTTP2_ERR_HEADER_COMP; - } - *dest_ptr = malloc(declen); - if(*dest_ptr == NULL) { - return NGHTTP2_ERR_HEADER_COMP; - } - nghttp2_hd_huff_decode(*dest_ptr, declen, in, inlen, side); - return declen; -} - ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater, nghttp2_nv **nva_ptr, uint8_t *in, size_t inlen) @@ -1341,7 +1321,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater, goto fail; } if(name_huffman) { - rv = inflate_decode(&nv.name, in, namelen, inflater->side); + rv = nghttp2_hd_huff_decode(&nv.name, in, namelen, inflater->side); if(rv < 0) { DEBUGF(fprintf(stderr, "Name huffman decoding failed\n")); goto fail; @@ -1369,7 +1349,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater, goto fail; } if(value_huffman) { - rv = inflate_decode(&nv.value, in, valuelen, inflater->side); + rv = nghttp2_hd_huff_decode(&nv.value, in, valuelen, inflater->side); if(rv < 0) { DEBUGF(fprintf(stderr, "Value huffman decoding failed\n")); free(decoded_huffman_name); @@ -1453,7 +1433,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater, goto fail; } if(value_huffman) { - rv = inflate_decode(&value, in, valuelen, inflater->side); + rv = nghttp2_hd_huff_decode(&value, in, valuelen, inflater->side); if(rv < 0) { DEBUGF(fprintf(stderr, "Value huffman decoding failed\n")); goto fail; diff --git a/lib/nghttp2_hd.h b/lib/nghttp2_hd.h index ccf53076..50d57dd4 100644 --- a/lib/nghttp2_hd.h +++ b/lib/nghttp2_hd.h @@ -381,12 +381,11 @@ ssize_t nghttp2_hd_huff_decode_count(const uint8_t *src, size_t srclen, nghttp2_hd_side side); /* - * Decodes the given data |src| with length |srclen| to the given - * memory location pointed by |dest|, allocated at lest |destlen| - * bytes. The given input must be padded with the prefix of terminal - * code. The caller is responsible to specify |destlen| at least the - * length that nghttp2_hd_huff_decode_count() returns. If |side| is - * NGHTTP2_HD_SIDE_REQUEST, the request huffman code table is + * Decodes the given data |src| with length |srclen|. This function + * allocates memory to store the result and assigns the its pointer to + * |*dest_ptr| on success. The caller is responsible to release the + * memory pointed by |*dest_ptr| if this function succeeds. If |side| + * is NGHTTP2_HD_SIDE_REQUEST, the request huffman code table is * used. Otherwise, the response code table is used. * * This function returns the number of written bytes. This return @@ -394,9 +393,15 @@ ssize_t nghttp2_hd_huff_decode_count(const uint8_t *src, size_t srclen, * nghttp2_hd_huff_decode_count() if it is given with the same |src|, * |srclen|, and |side|. * - * This function returns -1 if it fails. + * If this function fails, it returns one of the following negative + * return codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_HEADER_COMP + * Decoding process has failed. */ -ssize_t nghttp2_hd_huff_decode(uint8_t *dest, size_t destlen, +ssize_t nghttp2_hd_huff_decode(uint8_t **dest_ptr, const uint8_t *src, size_t srclen, nghttp2_hd_side side); diff --git a/lib/nghttp2_hd_huffman.c b/lib/nghttp2_hd_huffman.c index 8e875e1a..33f13158 100644 --- a/lib/nghttp2_hd_huffman.c +++ b/lib/nghttp2_hd_huffman.c @@ -213,7 +213,7 @@ ssize_t nghttp2_hd_huff_decode_count(const uint8_t *src, size_t srclen, return j; } -ssize_t nghttp2_hd_huff_decode(uint8_t *dest, size_t destlen, +ssize_t nghttp2_hd_huff_decode(uint8_t **dest_ptr, const uint8_t *src, size_t srclen, nghttp2_hd_side side) { @@ -221,6 +221,9 @@ ssize_t nghttp2_hd_huff_decode(uint8_t *dest, size_t destlen, size_t i, j; const nghttp2_huff_sym *huff_sym_table; const huff_decode_table_type *huff_decode_table; + uint8_t *dest = NULL; + size_t destlen = 0; + int rv; if(side == NGHTTP2_HD_SIDE_REQUEST) { huff_sym_table = req_huff_sym_table; @@ -231,23 +234,39 @@ ssize_t nghttp2_hd_huff_decode(uint8_t *dest, size_t destlen, } j = 0; for(i = 0; i < srclen;) { - int rv = huff_decode(src + i, srclen - i, bitoff, - huff_sym_table, huff_decode_table); + rv = huff_decode(src + i, srclen - i, bitoff, + huff_sym_table, huff_decode_table); if(rv == -1) { if(check_last_byte(src, srclen, i, bitoff)) { break; } - return -1; + rv = NGHTTP2_ERR_HEADER_COMP; + goto fail; } if(rv == 256) { /* 256 is special terminal symbol and it should not encoded in byte string. */ - return -1; + rv = NGHTTP2_ERR_HEADER_COMP; + goto fail; + } + if(j == destlen) { + size_t new_len = j == 0 ? 32 : j * 2; + uint8_t *new_dest = realloc(dest, new_len); + if(new_dest == NULL) { + rv = NGHTTP2_ERR_NOMEM; + goto fail; + } + dest = new_dest; + destlen = new_len; } dest[j++] = rv; bitoff += huff_sym_table[rv].nbits; i += bitoff / 8; bitoff &= 0x7; } + *dest_ptr = dest; return j; + fail: + free(dest); + return rv; }