Commit 5d6964cf authored by Tatsuhiro Tsujikawa's avatar Tatsuhiro Tsujikawa

Faster huffman decoding

parent 0d855bfc
...@@ -104,41 +104,37 @@ int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, ...@@ -104,41 +104,37 @@ int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src,
} }
void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) { void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) {
ctx->state = 0; ctx->fstate = NGHTTP2_HUFF_ACCEPTED;
ctx->accept = 1;
} }
ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
nghttp2_buf *buf, const uint8_t *src, nghttp2_buf *buf, const uint8_t *src,
size_t srclen, int final) { size_t srclen, int final) {
size_t i; const uint8_t *end = src + srclen;
nghttp2_huff_decode node = {ctx->fstate, 0};
const nghttp2_huff_decode *t = &node;
uint8_t c;
/* We use the decoding algorithm described in /* We use the decoding algorithm described in
http://graphics.ics.uci.edu/pub/Prefix.pdf */ http://graphics.ics.uci.edu/pub/Prefix.pdf */
for (i = 0; i < srclen; ++i) { for (; src != end;) {
const nghttp2_huff_decode *t; c = *src++;
t = &huff_decode_table[t->fstate & 0x1ff][c >> 4];
t = &huff_decode_table[ctx->state][src[i] >> 4]; if (t->fstate & NGHTTP2_HUFF_SYM) {
if (t->flags & NGHTTP2_HUFF_FAIL) {
return NGHTTP2_ERR_HEADER_COMP;
}
if (t->flags & NGHTTP2_HUFF_SYM) {
*buf->last++ = t->sym; *buf->last++ = t->sym;
} }
t = &huff_decode_table[t->state][src[i] & 0xf]; t = &huff_decode_table[t->fstate & 0x1ff][c & 0xf];
if (t->flags & NGHTTP2_HUFF_FAIL) { if (t->fstate & NGHTTP2_HUFF_SYM) {
return NGHTTP2_ERR_HEADER_COMP;
}
if (t->flags & NGHTTP2_HUFF_SYM) {
*buf->last++ = t->sym; *buf->last++ = t->sym;
} }
ctx->state = t->state;
ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0;
} }
if (final && !ctx->accept) {
ctx->fstate = t->fstate;
if (final && !(ctx->fstate & NGHTTP2_HUFF_ACCEPTED)) {
return NGHTTP2_ERR_HEADER_COMP; return NGHTTP2_ERR_HEADER_COMP;
} }
return (ssize_t)i;
return (ssize_t)srclen;
} }
...@@ -34,21 +34,20 @@ ...@@ -34,21 +34,20 @@
typedef enum { typedef enum {
/* FSA accepts this state as the end of huffman encoding /* FSA accepts this state as the end of huffman encoding
sequence. */ sequence. */
NGHTTP2_HUFF_ACCEPTED = 1, NGHTTP2_HUFF_ACCEPTED = 1 << 14,
/* This state emits symbol */ /* This state emits symbol */
NGHTTP2_HUFF_SYM = (1 << 1), NGHTTP2_HUFF_SYM = 1 << 15,
/* If state machine reaches this state, decoding fails. */
NGHTTP2_HUFF_FAIL = (1 << 2)
} nghttp2_huff_decode_flag; } nghttp2_huff_decode_flag;
typedef struct { typedef struct {
/* huffman decoding state, which is actually the node ID of internal /* fstate is the current huffman decoding state, which is actually
huffman tree. We have 257 leaf nodes, but they are identical to the node ID of internal huffman tree with
root node other than emitting a symbol, so we have 256 internal nghttp2_huff_decode_flag OR-ed. We have 257 leaf nodes, but they
nodes [1..255], inclusive. */ are identical to root node other than emitting a symbol, so we
uint8_t state; have 256 internal nodes [1..255], inclusive. The node ID 256 is
/* bitwise OR of zero or more of the nghttp2_huff_decode_flag */ a special node and it is a terminal state that means decoding
uint8_t flags; failed. */
uint16_t fstate;
/* symbol if NGHTTP2_HUFF_SYM flag set */ /* symbol if NGHTTP2_HUFF_SYM flag set */
uint8_t sym; uint8_t sym;
} nghttp2_huff_decode; } nghttp2_huff_decode;
...@@ -56,12 +55,8 @@ typedef struct { ...@@ -56,12 +55,8 @@ typedef struct {
typedef nghttp2_huff_decode huff_decode_table_type[16]; typedef nghttp2_huff_decode huff_decode_table_type[16];
typedef struct { typedef struct {
/* Current huffman decoding state. We stripped leaf nodes, so the /* fstate is the current huffman decoding state. */
value range is [0..255], inclusive. */ uint16_t fstate;
uint8_t state;
/* nonzero if we can say that the decoding process succeeds at this
state */
uint8_t accept;
} nghttp2_hd_huff_decode_context; } nghttp2_hd_huff_decode_context;
typedef struct { typedef struct {
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -357,9 +357,8 @@ def _build_transition_table(ctx, node): ...@@ -357,9 +357,8 @@ def _build_transition_table(ctx, node):
def huffman_tree_build_transition_table(ctx): def huffman_tree_build_transition_table(ctx):
_build_transition_table(ctx, ctx.root) _build_transition_table(ctx, ctx.root)
NGHTTP2_HUFF_ACCEPTED = 1 NGHTTP2_HUFF_ACCEPTED = 1 << 14
NGHTTP2_HUFF_SYM = 1 << 1 NGHTTP2_HUFF_SYM = 1 << 15
NGHTTP2_HUFF_FAIL = 1 << 2
def _print_transition_table(node): def _print_transition_table(node):
if node.term is not None: if node.term is not None:
...@@ -374,8 +373,7 @@ def _print_transition_table(node): ...@@ -374,8 +373,7 @@ def _print_transition_table(node):
out = sym out = sym
flags |= NGHTTP2_HUFF_SYM flags |= NGHTTP2_HUFF_SYM
if nd is None: if nd is None:
id = 0 id = 256
flags |= NGHTTP2_HUFF_FAIL
else: else:
id = nd.id id = nd.id
if id is None: if id is None:
...@@ -384,13 +382,32 @@ def _print_transition_table(node): ...@@ -384,13 +382,32 @@ def _print_transition_table(node):
flags |= NGHTTP2_HUFF_ACCEPTED flags |= NGHTTP2_HUFF_ACCEPTED
elif nd.accept: elif nd.accept:
flags |= NGHTTP2_HUFF_ACCEPTED flags |= NGHTTP2_HUFF_ACCEPTED
print ' {{{}, 0x{:02x}, {}}},'.format(id, flags, out) print ' {{0x{:02x}, {}}},'.format(id | flags, out)
print '},' print '},'
_print_transition_table(node.left) _print_transition_table(node.left)
_print_transition_table(node.right) _print_transition_table(node.right)
def huffman_tree_print_transition_table(ctx): def huffman_tree_print_transition_table(ctx):
_print_transition_table(ctx.root) _print_transition_table(ctx.root)
print '/* 256 */'
print '{'
print ' {0x100, 0},'
print ' {0x100, 0},'
print ' {0x100, 0},'
print ' {0x100, 0},'
print ' {0x100, 0},'
print ' {0x100, 0},'
print ' {0x100, 0},'
print ' {0x100, 0},'
print ' {0x100, 0},'
print ' {0x100, 0},'
print ' {0x100, 0},'
print ' {0x100, 0},'
print ' {0x100, 0},'
print ' {0x100, 0},'
print ' {0x100, 0},'
print ' {0x100, 0},'
print '},'
if __name__ == '__main__': if __name__ == '__main__':
ctx = Context() ctx = Context()
...@@ -436,14 +453,12 @@ const nghttp2_huff_sym huff_sym_table[] = {''' ...@@ -436,14 +453,12 @@ const nghttp2_huff_sym huff_sym_table[] = {'''
enum {{ enum {{
NGHTTP2_HUFF_ACCEPTED = {}, NGHTTP2_HUFF_ACCEPTED = {},
NGHTTP2_HUFF_SYM = {}, NGHTTP2_HUFF_SYM = {},
NGHTTP2_HUFF_FAIL = {},
}} nghttp2_huff_decode_flag; }} nghttp2_huff_decode_flag;
'''.format(NGHTTP2_HUFF_ACCEPTED, NGHTTP2_HUFF_SYM, NGHTTP2_HUFF_FAIL) '''.format(NGHTTP2_HUFF_ACCEPTED, NGHTTP2_HUFF_SYM)
print '''\ print '''\
typedef struct { typedef struct {
uint8_t state; uint16_t fstate;
uint8_t flags;
uint8_t sym; uint8_t sym;
} nghttp2_huff_decode; } nghttp2_huff_decode;
''' '''
......
...@@ -402,6 +402,7 @@ int main() { ...@@ -402,6 +402,7 @@ int main() {
test_nghttp2_hd_deflate_hd_vec) || test_nghttp2_hd_deflate_hd_vec) ||
!CU_add_test(pSuite, "hd_decode_length", test_nghttp2_hd_decode_length) || !CU_add_test(pSuite, "hd_decode_length", test_nghttp2_hd_decode_length) ||
!CU_add_test(pSuite, "hd_huff_encode", test_nghttp2_hd_huff_encode) || !CU_add_test(pSuite, "hd_huff_encode", test_nghttp2_hd_huff_encode) ||
!CU_add_test(pSuite, "hd_huff_decode", test_nghttp2_hd_huff_decode) ||
!CU_add_test(pSuite, "adjust_local_window_size", !CU_add_test(pSuite, "adjust_local_window_size",
test_nghttp2_adjust_local_window_size) || test_nghttp2_adjust_local_window_size) ||
!CU_add_test(pSuite, "check_header_name", !CU_add_test(pSuite, "check_header_name",
......
...@@ -1538,3 +1538,32 @@ void test_nghttp2_hd_huff_encode(void) { ...@@ -1538,3 +1538,32 @@ void test_nghttp2_hd_huff_encode(void) {
nghttp2_bufs_free(&bufs); nghttp2_bufs_free(&bufs);
} }
void test_nghttp2_hd_huff_decode(void) {
const uint8_t e[] = {0x1f, 0xff, 0xff, 0xff, 0xff, 0xff};
nghttp2_hd_huff_decode_context ctx;
nghttp2_buf outbuf;
uint8_t b[256];
ssize_t len;
nghttp2_buf_wrap_init(&outbuf, b, sizeof(b));
nghttp2_hd_huff_decode_context_init(&ctx);
len = nghttp2_hd_huff_decode(&ctx, &outbuf, e, 1, 1);
CU_ASSERT(1 == len);
CU_ASSERT(0 == memcmp("a", outbuf.pos, 1));
/* Premature sequence must elicit decoding error */
nghttp2_buf_wrap_init(&outbuf, b, sizeof(b));
nghttp2_hd_huff_decode_context_init(&ctx);
len = nghttp2_hd_huff_decode(&ctx, &outbuf, e, 2, 1);
CU_ASSERT(NGHTTP2_ERR_HEADER_COMP == len);
/* Fully decoding EOS is error */
nghttp2_buf_wrap_init(&outbuf, b, sizeof(b));
nghttp2_hd_huff_decode_context_init(&ctx);
len = nghttp2_hd_huff_decode(&ctx, &outbuf, e, 2, 6);
CU_ASSERT(NGHTTP2_ERR_HEADER_COMP == len);
}
...@@ -50,5 +50,6 @@ void test_nghttp2_hd_public_api(void); ...@@ -50,5 +50,6 @@ void test_nghttp2_hd_public_api(void);
void test_nghttp2_hd_deflate_hd_vec(void); void test_nghttp2_hd_deflate_hd_vec(void);
void test_nghttp2_hd_decode_length(void); void test_nghttp2_hd_decode_length(void);
void test_nghttp2_hd_huff_encode(void); void test_nghttp2_hd_huff_encode(void);
void test_nghttp2_hd_huff_decode(void);
#endif /* NGHTTP2_HD_TEST_H */ #endif /* NGHTTP2_HD_TEST_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment