# HG changeset patch # User Valentin Bartenev # Date 1455194136 -10800 # Node ID ba3c2ca21aa574d0901cf835f040c41da97dc3bc # Parent 5fe617f3822293dcc564df37f02b80a1abfa58b1 HTTP/2: implemented HPACK Huffman encoding for response headers. This reduces the size of headers by over 30% on average. Based on the patch by Vlad Krasnov: http://mailman.nginx.org/pipermail/nginx-devel/2015-December/007682.html diff --git a/auto/cc/conf b/auto/cc/conf --- a/auto/cc/conf +++ b/auto/cc/conf @@ -225,6 +225,16 @@ if [ "$NGX_PLATFORM" != win32 ]; then . auto/feature + ngx_feature="gcc builtin 64 bit byteswap" + ngx_feature_name="NGX_HAVE_GCC_BSWAP64" + ngx_feature_run=no + ngx_feature_incs= + ngx_feature_path= + ngx_feature_libs= + ngx_feature_test="__builtin_bswap64(0)" + . auto/feature + + # ngx_feature="inline" # ngx_feature_name= # ngx_feature_run=no diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h --- a/src/http/v2/ngx_http_v2.h +++ b/src/http/v2/ngx_http_v2.h @@ -274,6 +274,8 @@ ngx_int_t ngx_http_v2_table_size(ngx_htt ngx_int_t ngx_http_v2_huff_decode(u_char *state, u_char *src, size_t len, u_char **dst, ngx_uint_t last, ngx_log_t *log); +size_t ngx_http_v2_huff_encode(u_char *src, size_t len, u_char *dst, + ngx_uint_t lower); #define ngx_http_v2_prefix(bits) ((1 << (bits)) - 1) diff --git a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c --- a/src/http/v2/ngx_http_v2_filter_module.c +++ b/src/http/v2/ngx_http_v2_filter_module.c @@ -25,6 +25,11 @@ #define ngx_http_v2_indexed(i) (128 + (i)) #define ngx_http_v2_inc_indexed(i) (64 + (i)) +#define ngx_http_v2_write_name(dst, src, len, tmp) \ + ngx_http_v2_string_encode(dst, src, len, tmp, 1) +#define ngx_http_v2_write_value(dst, src, len, tmp) \ + ngx_http_v2_string_encode(dst, src, len, tmp, 0) + #define NGX_HTTP_V2_ENCODE_RAW 0 #define NGX_HTTP_V2_ENCODE_HUFF 0x80 @@ -46,6 +51,8 @@ #define NGX_HTTP_V2_VARY_INDEX 59 +static u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, + u_char *tmp, ngx_uint_t lower); static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value); static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame( @@ -119,8 +126,8 @@ static ngx_http_output_header_filter_pt static ngx_int_t ngx_http_v2_header_filter(ngx_http_request_t *r) { - u_char status, *pos, *start, *p; - size_t len; + u_char status, *pos, *start, *p, *tmp; + size_t len, tmp_len; ngx_str_t host, location; ngx_uint_t i, port; ngx_list_part_t *part; @@ -136,6 +143,14 @@ ngx_http_v2_header_filter(ngx_http_reque #endif u_char addr[NGX_SOCKADDR_STRLEN]; + static const u_char nginx[5] = "\x84\xaa\x63\x55\xe7"; +#if (NGX_HTTP_GZIP) + static const u_char accept_encoding[12] = + "\x8b\x84\x84\x2d\x69\x5b\x05\x44\x3c\x86\xaa\x6f"; +#endif + + static size_t nginx_ver_len = ngx_http_v2_literal_size(NGINX_VER); + static u_char nginx_ver[ngx_http_v2_literal_size(NGINX_VER)]; if (!r->stream) { return ngx_http_next_header_filter(r); @@ -215,8 +230,7 @@ ngx_http_v2_header_filter(ngx_http_reque clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (r->headers_out.server == NULL) { - len += 1 + (clcf->server_tokens ? ngx_http_v2_literal_size(NGINX_VER) - : ngx_http_v2_literal_size("nginx")); + len += 1 + (clcf->server_tokens ? nginx_ver_len : sizeof(nginx)); } if (r->headers_out.date == NULL) { @@ -340,10 +354,12 @@ ngx_http_v2_header_filter(ngx_http_reque len += 1 + NGX_HTTP_V2_INT_OCTETS + r->headers_out.location->value.len; } + tmp_len = len; + #if (NGX_HTTP_GZIP) if (r->gzip_vary) { if (clcf->gzip_vary) { - len += 1 + ngx_http_v2_literal_size("Accept-Encoding"); + len += 1 + sizeof(accept_encoding); } else { r->gzip_vary = 0; @@ -386,10 +402,20 @@ ngx_http_v2_header_filter(ngx_http_reque len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; + + if (header[i].key.len > tmp_len) { + tmp_len = header[i].key.len; + } + + if (header[i].value.len > tmp_len) { + tmp_len = header[i].value.len; + } } - pos = ngx_palloc(r->pool, len); - if (pos == NULL) { + tmp = ngx_palloc(r->pool, tmp_len); + pos = ngx_pnalloc(r->pool, len); + + if (pos == NULL || tmp == NULL) { return NGX_ERROR; } @@ -408,21 +434,23 @@ ngx_http_v2_header_filter(ngx_http_reque *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX); if (clcf->server_tokens) { - *pos++ = NGX_HTTP_V2_ENCODE_RAW | (sizeof(NGINX_VER) - 1); - pos = ngx_cpymem(pos, NGINX_VER, sizeof(NGINX_VER) - 1); + if (nginx_ver[0] == '\0') { + p = ngx_http_v2_write_value(nginx_ver, (u_char *) NGINX_VER, + sizeof(NGINX_VER) - 1, tmp); + nginx_ver_len = p - nginx_ver; + } + + pos = ngx_cpymem(pos, nginx_ver, nginx_ver_len); } else { - *pos++ = NGX_HTTP_V2_ENCODE_RAW | (sizeof("nginx") - 1); - pos = ngx_cpymem(pos, "nginx", sizeof("nginx") - 1); + pos = ngx_cpymem(pos, nginx, sizeof(nginx)); } } if (r->headers_out.date == NULL) { *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX); - *pos++ = (u_char) ngx_cached_http_time.len; - - pos = ngx_cpymem(pos, ngx_cached_http_time.data, - ngx_cached_http_time.len); + pos = ngx_http_v2_write_value(pos, ngx_cached_http_time.data, + ngx_cached_http_time.len, tmp); } if (r->headers_out.content_type.len) { @@ -431,34 +459,30 @@ ngx_http_v2_header_filter(ngx_http_reque if (r->headers_out.content_type_len == r->headers_out.content_type.len && r->headers_out.charset.len) { - *pos = NGX_HTTP_V2_ENCODE_RAW; - pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), - r->headers_out.content_type.len - + sizeof("; charset=") - 1 - + r->headers_out.charset.len); + len = r->headers_out.content_type.len + sizeof("; charset=") - 1 + + r->headers_out.charset.len; - p = pos; + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } - pos = ngx_cpymem(pos, r->headers_out.content_type.data, - r->headers_out.content_type.len); - - pos = ngx_cpymem(pos, "; charset=", sizeof("; charset=") - 1); + p = ngx_cpymem(p, r->headers_out.content_type.data, + r->headers_out.content_type.len); - pos = ngx_cpymem(pos, r->headers_out.charset.data, - r->headers_out.charset.len); + p = ngx_cpymem(p, "; charset=", sizeof("; charset=") - 1); - /* update r->headers_out.content_type for possible logging */ + p = ngx_cpymem(p, r->headers_out.charset.data, + r->headers_out.charset.len); - r->headers_out.content_type.len = pos - p; - r->headers_out.content_type.data = p; + /* updated r->headers_out.content_type is also needed for logging */ - } else { - *pos = NGX_HTTP_V2_ENCODE_RAW; - pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), - r->headers_out.content_type.len); - pos = ngx_cpymem(pos, r->headers_out.content_type.data, - r->headers_out.content_type.len); + r->headers_out.content_type.len = len; + r->headers_out.content_type.data = p - len; } + + pos = ngx_http_v2_write_value(pos, r->headers_out.content_type.data, + r->headers_out.content_type.len, tmp); } if (r->headers_out.content_length == NULL @@ -476,26 +500,26 @@ ngx_http_v2_header_filter(ngx_http_reque { *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX); - *pos++ = NGX_HTTP_V2_ENCODE_RAW - | (sizeof("Wed, 31 Dec 1986 18:00:00 GMT") - 1); - pos = ngx_http_time(pos, r->headers_out.last_modified_time); + ngx_http_time(pos, r->headers_out.last_modified_time); + len = sizeof("Wed, 31 Dec 1986 18:00:00 GMT") - 1; + + /* + * Date will always be encoded using huffman in the temporary buffer, + * so it's safe here to use src and dst pointing to the same address. + */ + pos = ngx_http_v2_write_value(pos, pos, len, tmp); } if (r->headers_out.location && r->headers_out.location->value.len) { *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX); - - *pos = NGX_HTTP_V2_ENCODE_RAW; - pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), - r->headers_out.location->value.len); - pos = ngx_cpymem(pos, r->headers_out.location->value.data, - r->headers_out.location->value.len); + pos = ngx_http_v2_write_value(pos, r->headers_out.location->value.data, + r->headers_out.location->value.len, tmp); } #if (NGX_HTTP_GZIP) if (r->gzip_vary) { *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX); - *pos++ = NGX_HTTP_V2_ENCODE_RAW | (sizeof("Accept-Encoding") - 1); - pos = ngx_cpymem(pos, "Accept-Encoding", sizeof("Accept-Encoding") - 1); + pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding)); } #endif @@ -520,16 +544,11 @@ ngx_http_v2_header_filter(ngx_http_reque *pos++ = 0; - *pos = NGX_HTTP_V2_ENCODE_RAW; - pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), - header[i].key.len); - ngx_strlow(pos, header[i].key.data, header[i].key.len); - pos += header[i].key.len; + pos = ngx_http_v2_write_name(pos, header[i].key.data, + header[i].key.len, tmp); - *pos = NGX_HTTP_V2_ENCODE_RAW; - pos = ngx_http_v2_write_int(pos, ngx_http_v2_prefix(7), - header[i].value.len); - pos = ngx_cpymem(pos, header[i].value.data, header[i].value.len); + pos = ngx_http_v2_write_value(pos, header[i].value.data, + header[i].value.len, tmp); } frame = ngx_http_v2_create_headers_frame(r, start, pos); @@ -557,6 +576,32 @@ ngx_http_v2_header_filter(ngx_http_reque static u_char * +ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp, + ngx_uint_t lower) +{ + size_t hlen; + + hlen = ngx_http_v2_huff_encode(src, len, tmp, lower); + + if (hlen > 0) { + *dst = NGX_HTTP_V2_ENCODE_HUFF; + dst = ngx_http_v2_write_int(dst, ngx_http_v2_prefix(7), hlen); + return ngx_cpymem(dst, tmp, hlen); + } + + *dst = NGX_HTTP_V2_ENCODE_RAW; + dst = ngx_http_v2_write_int(dst, ngx_http_v2_prefix(7), len); + + if (lower) { + ngx_strlow(dst, src, len); + return dst + len; + } + + return ngx_cpymem(dst, src, len); +} + + +static u_char * ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value) { if (value < prefix) { diff --git a/src/http/v2/ngx_http_v2_huff_encode.c b/src/http/v2/ngx_http_v2_huff_encode.c --- a/src/http/v2/ngx_http_v2_huff_encode.c +++ b/src/http/v2/ngx_http_v2_huff_encode.c @@ -2,9 +2,249 @@ /* * Copyright (C) Nginx, Inc. * Copyright (C) Valentin V. Bartenev + * Copyright (C) 2015 Vlad Krasnov */ #include #include #include + + +typedef struct { + uint32_t code; + uint32_t len; +} ngx_http_v2_huff_encode_code_t; + + +static ngx_http_v2_huff_encode_code_t ngx_http_v2_huff_encode_table[256] = +{ + {0x00001ff8, 13}, {0x007fffd8, 23}, {0x0fffffe2, 28}, {0x0fffffe3, 28}, + {0x0fffffe4, 28}, {0x0fffffe5, 28}, {0x0fffffe6, 28}, {0x0fffffe7, 28}, + {0x0fffffe8, 28}, {0x00ffffea, 24}, {0x3ffffffc, 30}, {0x0fffffe9, 28}, + {0x0fffffea, 28}, {0x3ffffffd, 30}, {0x0fffffeb, 28}, {0x0fffffec, 28}, + {0x0fffffed, 28}, {0x0fffffee, 28}, {0x0fffffef, 28}, {0x0ffffff0, 28}, + {0x0ffffff1, 28}, {0x0ffffff2, 28}, {0x3ffffffe, 30}, {0x0ffffff3, 28}, + {0x0ffffff4, 28}, {0x0ffffff5, 28}, {0x0ffffff6, 28}, {0x0ffffff7, 28}, + {0x0ffffff8, 28}, {0x0ffffff9, 28}, {0x0ffffffa, 28}, {0x0ffffffb, 28}, + {0x00000014, 6}, {0x000003f8, 10}, {0x000003f9, 10}, {0x00000ffa, 12}, + {0x00001ff9, 13}, {0x00000015, 6}, {0x000000f8, 8}, {0x000007fa, 11}, + {0x000003fa, 10}, {0x000003fb, 10}, {0x000000f9, 8}, {0x000007fb, 11}, + {0x000000fa, 8}, {0x00000016, 6}, {0x00000017, 6}, {0x00000018, 6}, + {0x00000000, 5}, {0x00000001, 5}, {0x00000002, 5}, {0x00000019, 6}, + {0x0000001a, 6}, {0x0000001b, 6}, {0x0000001c, 6}, {0x0000001d, 6}, + {0x0000001e, 6}, {0x0000001f, 6}, {0x0000005c, 7}, {0x000000fb, 8}, + {0x00007ffc, 15}, {0x00000020, 6}, {0x00000ffb, 12}, {0x000003fc, 10}, + {0x00001ffa, 13}, {0x00000021, 6}, {0x0000005d, 7}, {0x0000005e, 7}, + {0x0000005f, 7}, {0x00000060, 7}, {0x00000061, 7}, {0x00000062, 7}, + {0x00000063, 7}, {0x00000064, 7}, {0x00000065, 7}, {0x00000066, 7}, + {0x00000067, 7}, {0x00000068, 7}, {0x00000069, 7}, {0x0000006a, 7}, + {0x0000006b, 7}, {0x0000006c, 7}, {0x0000006d, 7}, {0x0000006e, 7}, + {0x0000006f, 7}, {0x00000070, 7}, {0x00000071, 7}, {0x00000072, 7}, + {0x000000fc, 8}, {0x00000073, 7}, {0x000000fd, 8}, {0x00001ffb, 13}, + {0x0007fff0, 19}, {0x00001ffc, 13}, {0x00003ffc, 14}, {0x00000022, 6}, + {0x00007ffd, 15}, {0x00000003, 5}, {0x00000023, 6}, {0x00000004, 5}, + {0x00000024, 6}, {0x00000005, 5}, {0x00000025, 6}, {0x00000026, 6}, + {0x00000027, 6}, {0x00000006, 5}, {0x00000074, 7}, {0x00000075, 7}, + {0x00000028, 6}, {0x00000029, 6}, {0x0000002a, 6}, {0x00000007, 5}, + {0x0000002b, 6}, {0x00000076, 7}, {0x0000002c, 6}, {0x00000008, 5}, + {0x00000009, 5}, {0x0000002d, 6}, {0x00000077, 7}, {0x00000078, 7}, + {0x00000079, 7}, {0x0000007a, 7}, {0x0000007b, 7}, {0x00007ffe, 15}, + {0x000007fc, 11}, {0x00003ffd, 14}, {0x00001ffd, 13}, {0x0ffffffc, 28}, + {0x000fffe6, 20}, {0x003fffd2, 22}, {0x000fffe7, 20}, {0x000fffe8, 20}, + {0x003fffd3, 22}, {0x003fffd4, 22}, {0x003fffd5, 22}, {0x007fffd9, 23}, + {0x003fffd6, 22}, {0x007fffda, 23}, {0x007fffdb, 23}, {0x007fffdc, 23}, + {0x007fffdd, 23}, {0x007fffde, 23}, {0x00ffffeb, 24}, {0x007fffdf, 23}, + {0x00ffffec, 24}, {0x00ffffed, 24}, {0x003fffd7, 22}, {0x007fffe0, 23}, + {0x00ffffee, 24}, {0x007fffe1, 23}, {0x007fffe2, 23}, {0x007fffe3, 23}, + {0x007fffe4, 23}, {0x001fffdc, 21}, {0x003fffd8, 22}, {0x007fffe5, 23}, + {0x003fffd9, 22}, {0x007fffe6, 23}, {0x007fffe7, 23}, {0x00ffffef, 24}, + {0x003fffda, 22}, {0x001fffdd, 21}, {0x000fffe9, 20}, {0x003fffdb, 22}, + {0x003fffdc, 22}, {0x007fffe8, 23}, {0x007fffe9, 23}, {0x001fffde, 21}, + {0x007fffea, 23}, {0x003fffdd, 22}, {0x003fffde, 22}, {0x00fffff0, 24}, + {0x001fffdf, 21}, {0x003fffdf, 22}, {0x007fffeb, 23}, {0x007fffec, 23}, + {0x001fffe0, 21}, {0x001fffe1, 21}, {0x003fffe0, 22}, {0x001fffe2, 21}, + {0x007fffed, 23}, {0x003fffe1, 22}, {0x007fffee, 23}, {0x007fffef, 23}, + {0x000fffea, 20}, {0x003fffe2, 22}, {0x003fffe3, 22}, {0x003fffe4, 22}, + {0x007ffff0, 23}, {0x003fffe5, 22}, {0x003fffe6, 22}, {0x007ffff1, 23}, + {0x03ffffe0, 26}, {0x03ffffe1, 26}, {0x000fffeb, 20}, {0x0007fff1, 19}, + {0x003fffe7, 22}, {0x007ffff2, 23}, {0x003fffe8, 22}, {0x01ffffec, 25}, + {0x03ffffe2, 26}, {0x03ffffe3, 26}, {0x03ffffe4, 26}, {0x07ffffde, 27}, + {0x07ffffdf, 27}, {0x03ffffe5, 26}, {0x00fffff1, 24}, {0x01ffffed, 25}, + {0x0007fff2, 19}, {0x001fffe3, 21}, {0x03ffffe6, 26}, {0x07ffffe0, 27}, + {0x07ffffe1, 27}, {0x03ffffe7, 26}, {0x07ffffe2, 27}, {0x00fffff2, 24}, + {0x001fffe4, 21}, {0x001fffe5, 21}, {0x03ffffe8, 26}, {0x03ffffe9, 26}, + {0x0ffffffd, 28}, {0x07ffffe3, 27}, {0x07ffffe4, 27}, {0x07ffffe5, 27}, + {0x000fffec, 20}, {0x00fffff3, 24}, {0x000fffed, 20}, {0x001fffe6, 21}, + {0x003fffe9, 22}, {0x001fffe7, 21}, {0x001fffe8, 21}, {0x007ffff3, 23}, + {0x003fffea, 22}, {0x003fffeb, 22}, {0x01ffffee, 25}, {0x01ffffef, 25}, + {0x00fffff4, 24}, {0x00fffff5, 24}, {0x03ffffea, 26}, {0x007ffff4, 23}, + {0x03ffffeb, 26}, {0x07ffffe6, 27}, {0x03ffffec, 26}, {0x03ffffed, 26}, + {0x07ffffe7, 27}, {0x07ffffe8, 27}, {0x07ffffe9, 27}, {0x07ffffea, 27}, + {0x07ffffeb, 27}, {0x0ffffffe, 28}, {0x07ffffec, 27}, {0x07ffffed, 27}, + {0x07ffffee, 27}, {0x07ffffef, 27}, {0x07fffff0, 27}, {0x03ffffee, 26} +}; + + +/* same as above, but embeds lowercase transformation */ +static ngx_http_v2_huff_encode_code_t ngx_http_v2_huff_encode_table_lc[256] = +{ + {0x00001ff8, 13}, {0x007fffd8, 23}, {0x0fffffe2, 28}, {0x0fffffe3, 28}, + {0x0fffffe4, 28}, {0x0fffffe5, 28}, {0x0fffffe6, 28}, {0x0fffffe7, 28}, + {0x0fffffe8, 28}, {0x00ffffea, 24}, {0x3ffffffc, 30}, {0x0fffffe9, 28}, + {0x0fffffea, 28}, {0x3ffffffd, 30}, {0x0fffffeb, 28}, {0x0fffffec, 28}, + {0x0fffffed, 28}, {0x0fffffee, 28}, {0x0fffffef, 28}, {0x0ffffff0, 28}, + {0x0ffffff1, 28}, {0x0ffffff2, 28}, {0x3ffffffe, 30}, {0x0ffffff3, 28}, + {0x0ffffff4, 28}, {0x0ffffff5, 28}, {0x0ffffff6, 28}, {0x0ffffff7, 28}, + {0x0ffffff8, 28}, {0x0ffffff9, 28}, {0x0ffffffa, 28}, {0x0ffffffb, 28}, + {0x00000014, 6}, {0x000003f8, 10}, {0x000003f9, 10}, {0x00000ffa, 12}, + {0x00001ff9, 13}, {0x00000015, 6}, {0x000000f8, 8}, {0x000007fa, 11}, + {0x000003fa, 10}, {0x000003fb, 10}, {0x000000f9, 8}, {0x000007fb, 11}, + {0x000000fa, 8}, {0x00000016, 6}, {0x00000017, 6}, {0x00000018, 6}, + {0x00000000, 5}, {0x00000001, 5}, {0x00000002, 5}, {0x00000019, 6}, + {0x0000001a, 6}, {0x0000001b, 6}, {0x0000001c, 6}, {0x0000001d, 6}, + {0x0000001e, 6}, {0x0000001f, 6}, {0x0000005c, 7}, {0x000000fb, 8}, + {0x00007ffc, 15}, {0x00000020, 6}, {0x00000ffb, 12}, {0x000003fc, 10}, + {0x00001ffa, 13}, {0x00000003, 5}, {0x00000023, 6}, {0x00000004, 5}, + {0x00000024, 6}, {0x00000005, 5}, {0x00000025, 6}, {0x00000026, 6}, + {0x00000027, 6}, {0x00000006, 5}, {0x00000074, 7}, {0x00000075, 7}, + {0x00000028, 6}, {0x00000029, 6}, {0x0000002a, 6}, {0x00000007, 5}, + {0x0000002b, 6}, {0x00000076, 7}, {0x0000002c, 6}, {0x00000008, 5}, + {0x00000009, 5}, {0x0000002d, 6}, {0x00000077, 7}, {0x00000078, 7}, + {0x00000079, 7}, {0x0000007a, 7}, {0x0000007b, 7}, {0x00001ffb, 13}, + {0x0007fff0, 19}, {0x00001ffc, 13}, {0x00003ffc, 14}, {0x00000022, 6}, + {0x00007ffd, 15}, {0x00000003, 5}, {0x00000023, 6}, {0x00000004, 5}, + {0x00000024, 6}, {0x00000005, 5}, {0x00000025, 6}, {0x00000026, 6}, + {0x00000027, 6}, {0x00000006, 5}, {0x00000074, 7}, {0x00000075, 7}, + {0x00000028, 6}, {0x00000029, 6}, {0x0000002a, 6}, {0x00000007, 5}, + {0x0000002b, 6}, {0x00000076, 7}, {0x0000002c, 6}, {0x00000008, 5}, + {0x00000009, 5}, {0x0000002d, 6}, {0x00000077, 7}, {0x00000078, 7}, + {0x00000079, 7}, {0x0000007a, 7}, {0x0000007b, 7}, {0x00007ffe, 15}, + {0x000007fc, 11}, {0x00003ffd, 14}, {0x00001ffd, 13}, {0x0ffffffc, 28}, + {0x000fffe6, 20}, {0x003fffd2, 22}, {0x000fffe7, 20}, {0x000fffe8, 20}, + {0x003fffd3, 22}, {0x003fffd4, 22}, {0x003fffd5, 22}, {0x007fffd9, 23}, + {0x003fffd6, 22}, {0x007fffda, 23}, {0x007fffdb, 23}, {0x007fffdc, 23}, + {0x007fffdd, 23}, {0x007fffde, 23}, {0x00ffffeb, 24}, {0x007fffdf, 23}, + {0x00ffffec, 24}, {0x00ffffed, 24}, {0x003fffd7, 22}, {0x007fffe0, 23}, + {0x00ffffee, 24}, {0x007fffe1, 23}, {0x007fffe2, 23}, {0x007fffe3, 23}, + {0x007fffe4, 23}, {0x001fffdc, 21}, {0x003fffd8, 22}, {0x007fffe5, 23}, + {0x003fffd9, 22}, {0x007fffe6, 23}, {0x007fffe7, 23}, {0x00ffffef, 24}, + {0x003fffda, 22}, {0x001fffdd, 21}, {0x000fffe9, 20}, {0x003fffdb, 22}, + {0x003fffdc, 22}, {0x007fffe8, 23}, {0x007fffe9, 23}, {0x001fffde, 21}, + {0x007fffea, 23}, {0x003fffdd, 22}, {0x003fffde, 22}, {0x00fffff0, 24}, + {0x001fffdf, 21}, {0x003fffdf, 22}, {0x007fffeb, 23}, {0x007fffec, 23}, + {0x001fffe0, 21}, {0x001fffe1, 21}, {0x003fffe0, 22}, {0x001fffe2, 21}, + {0x007fffed, 23}, {0x003fffe1, 22}, {0x007fffee, 23}, {0x007fffef, 23}, + {0x000fffea, 20}, {0x003fffe2, 22}, {0x003fffe3, 22}, {0x003fffe4, 22}, + {0x007ffff0, 23}, {0x003fffe5, 22}, {0x003fffe6, 22}, {0x007ffff1, 23}, + {0x03ffffe0, 26}, {0x03ffffe1, 26}, {0x000fffeb, 20}, {0x0007fff1, 19}, + {0x003fffe7, 22}, {0x007ffff2, 23}, {0x003fffe8, 22}, {0x01ffffec, 25}, + {0x03ffffe2, 26}, {0x03ffffe3, 26}, {0x03ffffe4, 26}, {0x07ffffde, 27}, + {0x07ffffdf, 27}, {0x03ffffe5, 26}, {0x00fffff1, 24}, {0x01ffffed, 25}, + {0x0007fff2, 19}, {0x001fffe3, 21}, {0x03ffffe6, 26}, {0x07ffffe0, 27}, + {0x07ffffe1, 27}, {0x03ffffe7, 26}, {0x07ffffe2, 27}, {0x00fffff2, 24}, + {0x001fffe4, 21}, {0x001fffe5, 21}, {0x03ffffe8, 26}, {0x03ffffe9, 26}, + {0x0ffffffd, 28}, {0x07ffffe3, 27}, {0x07ffffe4, 27}, {0x07ffffe5, 27}, + {0x000fffec, 20}, {0x00fffff3, 24}, {0x000fffed, 20}, {0x001fffe6, 21}, + {0x003fffe9, 22}, {0x001fffe7, 21}, {0x001fffe8, 21}, {0x007ffff3, 23}, + {0x003fffea, 22}, {0x003fffeb, 22}, {0x01ffffee, 25}, {0x01ffffef, 25}, + {0x00fffff4, 24}, {0x00fffff5, 24}, {0x03ffffea, 26}, {0x007ffff4, 23}, + {0x03ffffeb, 26}, {0x07ffffe6, 27}, {0x03ffffec, 26}, {0x03ffffed, 26}, + {0x07ffffe7, 27}, {0x07ffffe8, 27}, {0x07ffffe9, 27}, {0x07ffffea, 27}, + {0x07ffffeb, 27}, {0x0ffffffe, 28}, {0x07ffffec, 27}, {0x07ffffed, 27}, + {0x07ffffee, 27}, {0x07ffffef, 27}, {0x07fffff0, 27}, {0x03ffffee, 26} +}; + + +#if (NGX_PTR_SIZE == 8) + +#if (NGX_HAVE_LITTLE_ENDIAN) + +#if (NGX_HAVE_GCC_BSWAP64) +#define ngx_http_v2_huff_encode_buf(dst, buf) \ + (*(uint64_t *) (dst) = __builtin_bswap64(buf)) +#else +#define ngx_http_v2_huff_encode_buf(dst, buf) \ + ((dst)[0] = (u_char) ((buf) >> 56), \ + (dst)[1] = (u_char) ((buf) >> 48), \ + (dst)[2] = (u_char) ((buf) >> 40), \ + (dst)[3] = (u_char) ((buf) >> 32), \ + (dst)[4] = (u_char) ((buf) >> 24), \ + (dst)[5] = (u_char) ((buf) >> 16), \ + (dst)[6] = (u_char) ((buf) >> 8), \ + (dst)[7] = (u_char) (buf)) +#endif + +#else /* !NGX_HAVE_LITTLE_ENDIAN */ +#define ngx_http_v2_huff_encode_buf(dst, buf) \ + (*(uint64_t *) (dst) = (buf)) +#endif + +#else /* NGX_PTR_SIZE == 4 */ + +#define ngx_http_v2_huff_encode_buf(dst, buf) \ + (*(uint32_t *) (dst) = htonl(buf)) + +#endif + + +size_t +ngx_http_v2_huff_encode(u_char *src, size_t len, u_char *dst, ngx_uint_t lower) +{ + u_char *end; + size_t hlen; + ngx_uint_t buf, pending, code; + ngx_http_v2_huff_encode_code_t *table, *next; + + table = lower ? ngx_http_v2_huff_encode_table_lc + : ngx_http_v2_huff_encode_table; + hlen = 0; + buf = 0; + pending = 0; + + end = src + len; + + while (src != end) { + next = &table[*src++]; + + code = next->code; + pending += next->len; + + /* accumulate bits */ + if (pending < sizeof(buf) * 8) { + buf |= code << (sizeof(buf) * 8 - pending); + continue; + } + + if (hlen + sizeof(buf) >= len) { + return 0; + } + + pending -= sizeof(buf) * 8; + + buf |= code >> pending; + + ngx_http_v2_huff_encode_buf(&dst[hlen], buf); + + hlen += sizeof(buf); + + buf = pending ? code << (sizeof(buf) * 8 - pending) : 0; + } + + buf |= (ngx_uint_t) -1 >> pending; + + pending = ngx_align(pending, 8); + + if (hlen + pending / 8 >= len) { + return 0; + } + + buf >>= sizeof(buf) * 8 - pending; + + while (pending) { + pending -= 8; + dst[hlen++] = (u_char) (buf >> pending); + } + + return hlen; +}