[nginx] HTTP/2: implemented HPACK Huffman encoding for response ...

Valentin Bartenev vbart at nginx.com
Thu Feb 11 16:30:23 UTC 2016


details:   http://hg.nginx.org/nginx/rev/ba3c2ca21aa5
branches:  
changeset: 6395:ba3c2ca21aa5
user:      Valentin Bartenev <vbart at nginx.com>
date:      Thu Feb 11 15:35:36 2016 +0300
description:
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

diffstat:

 auto/cc/conf                            |   10 +
 src/http/v2/ngx_http_v2.h               |    2 +
 src/http/v2/ngx_http_v2_filter_module.c |  157 +++++++++++++-------
 src/http/v2/ngx_http_v2_huff_encode.c   |  240 ++++++++++++++++++++++++++++++++
 4 files changed, 353 insertions(+), 56 deletions(-)

diffs (558 lines):

diff -r 5fe617f38222 -r ba3c2ca21aa5 auto/cc/conf
--- a/auto/cc/conf	Thu Feb 11 18:46:46 2016 +0300
+++ b/auto/cc/conf	Thu Feb 11 15:35:36 2016 +0300
@@ -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 -r 5fe617f38222 -r ba3c2ca21aa5 src/http/v2/ngx_http_v2.h
--- a/src/http/v2/ngx_http_v2.h	Thu Feb 11 18:46:46 2016 +0300
+++ b/src/http/v2/ngx_http_v2.h	Thu Feb 11 15:35:36 2016 +0300
@@ -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 -r 5fe617f38222 -r ba3c2ca21aa5 src/http/v2/ngx_http_v2_filter_module.c
--- a/src/http/v2/ngx_http_v2_filter_module.c	Thu Feb 11 18:46:46 2016 +0300
+++ b/src/http/v2/ngx_http_v2_filter_module.c	Thu Feb 11 15:35:36 2016 +0300
@@ -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);
+            p = ngx_cpymem(p, r->headers_out.content_type.data,
+                           r->headers_out.content_type.len);
 
-            pos = ngx_cpymem(pos, "; charset=", sizeof("; charset=") - 1);
+            p = ngx_cpymem(p, "; charset=", sizeof("; charset=") - 1);
 
-            pos = ngx_cpymem(pos, r->headers_out.charset.data,
-                             r->headers_out.charset.len);
+            p = ngx_cpymem(p, r->headers_out.charset.data,
+                           r->headers_out.charset.len);
 
-            /* update r->headers_out.content_type for possible logging */
+            /* updated r->headers_out.content_type is also needed for logging */
 
-            r->headers_out.content_type.len = pos - p;
-            r->headers_out.content_type.data = p;
+            r->headers_out.content_type.len = len;
+            r->headers_out.content_type.data = p - len;
+        }
 
-        } 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);
-        }
+        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 -r 5fe617f38222 -r ba3c2ca21aa5 src/http/v2/ngx_http_v2_huff_encode.c
--- a/src/http/v2/ngx_http_v2_huff_encode.c	Thu Feb 11 18:46:46 2016 +0300
+++ b/src/http/v2/ngx_http_v2_huff_encode.c	Thu Feb 11 15:35:36 2016 +0300
@@ -2,9 +2,249 @@
 /*
  * Copyright (C) Nginx, Inc.
  * Copyright (C) Valentin V. Bartenev
+ * Copyright (C) 2015 Vlad Krasnov
  */
 
 
 #include <ngx_config.h>
 #include <ngx_core.h>
 #include <ngx_http.h>
+
+
+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;
+}



More information about the nginx-devel mailing list