4df1cd04251b046f4470ace0b0122eb60b582f8a
[platform/upstream/nghttp2.git] / lib / nghttp2_hd_huffman.c
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2013 Tatsuhiro Tsujikawa
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 #include "nghttp2_hd_huffman.h"
26
27 #include <string.h>
28 #include <assert.h>
29 #include <stdio.h>
30
31 #include "nghttp2_hd.h"
32
33 extern const nghttp2_huff_sym huff_sym_table[];
34 extern const nghttp2_huff_decode huff_decode_table[][16];
35
36 /*
37  * Encodes huffman code |sym| into |*dest_ptr|, whose least |rembits|
38  * bits are not filled yet.  The |rembits| must be in range [1, 8],
39  * inclusive.  At the end of the process, the |*dest_ptr| is updated
40  * and points where next output should be placed. The number of
41  * unfilled bits in the pointed location is returned.
42  */
43 static ssize_t huff_encode_sym(nghttp2_bufs *bufs, size_t *avail_ptr,
44                                size_t rembits, const nghttp2_huff_sym *sym) {
45   int rv;
46   size_t nbits = sym->nbits;
47   uint32_t code = sym->code;
48
49   /* We assume that sym->nbits <= 32 */
50   if (rembits > nbits) {
51     nghttp2_bufs_fast_orb_hold(bufs, code << (rembits - nbits));
52     return (ssize_t)(rembits - nbits);
53   }
54
55   if (rembits == nbits) {
56     nghttp2_bufs_fast_orb(bufs, code);
57     --*avail_ptr;
58     return 8;
59   }
60
61   nghttp2_bufs_fast_orb(bufs, code >> (nbits - rembits));
62   --*avail_ptr;
63
64   nbits -= rembits;
65   if (nbits & 0x7) {
66     /* align code to MSB byte boundary */
67     code <<= 8 - (nbits & 0x7);
68   }
69
70   /* we lose at most 3 bytes, but it is not critical in practice */
71   if (*avail_ptr < (nbits + 7) / 8) {
72     rv = nghttp2_bufs_advance(bufs);
73     if (rv != 0) {
74       return rv;
75     }
76     *avail_ptr = nghttp2_bufs_cur_avail(bufs);
77     /* we assume that we at least 3 buffer space available */
78     assert(*avail_ptr >= 3);
79   }
80
81   /* fast path, since most code is less than 8 */
82   if (nbits < 8) {
83     nghttp2_bufs_fast_addb_hold(bufs, code);
84     *avail_ptr = nghttp2_bufs_cur_avail(bufs);
85     return (ssize_t)(8 - nbits);
86   }
87
88   /* handle longer code path */
89   if (nbits > 24) {
90     nghttp2_bufs_fast_addb(bufs, code >> 24);
91     nbits -= 8;
92   }
93
94   if (nbits > 16) {
95     nghttp2_bufs_fast_addb(bufs, code >> 16);
96     nbits -= 8;
97   }
98
99   if (nbits > 8) {
100     nghttp2_bufs_fast_addb(bufs, code >> 8);
101     nbits -= 8;
102   }
103
104   if (nbits == 8) {
105     nghttp2_bufs_fast_addb(bufs, code);
106     *avail_ptr = nghttp2_bufs_cur_avail(bufs);
107     return 8;
108   }
109
110   nghttp2_bufs_fast_addb_hold(bufs, code);
111   *avail_ptr = nghttp2_bufs_cur_avail(bufs);
112   return (ssize_t)(8 - nbits);
113 }
114
115 size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len) {
116   size_t i;
117   size_t nbits = 0;
118
119   for (i = 0; i < len; ++i) {
120     nbits += huff_sym_table[src[i]].nbits;
121   }
122   /* pad the prefix of EOS (256) */
123   return (nbits + 7) / 8;
124 }
125
126 int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src,
127                            size_t srclen) {
128   int rv;
129   ssize_t rembits = 8;
130   size_t i;
131   size_t avail;
132
133   avail = nghttp2_bufs_cur_avail(bufs);
134
135   for (i = 0; i < srclen; ++i) {
136     const nghttp2_huff_sym *sym = &huff_sym_table[src[i]];
137     if (rembits == 8) {
138       if (avail) {
139         nghttp2_bufs_fast_addb_hold(bufs, 0);
140       } else {
141         rv = nghttp2_bufs_addb_hold(bufs, 0);
142         if (rv != 0) {
143           return rv;
144         }
145         avail = nghttp2_bufs_cur_avail(bufs);
146       }
147     }
148     rembits = huff_encode_sym(bufs, &avail, rembits, sym);
149     if (rembits < 0) {
150       return (int)rembits;
151     }
152   }
153   /* 256 is special terminal symbol, pad with its prefix */
154   if (rembits < 8) {
155     /* if rembits < 8, we should have at least 1 buffer space
156        available */
157     const nghttp2_huff_sym *sym = &huff_sym_table[256];
158     assert(avail);
159     /* Caution we no longer adjust avail here */
160     nghttp2_bufs_fast_orb(bufs, sym->code >> (sym->nbits - rembits));
161   }
162
163   return 0;
164 }
165
166 void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) {
167   ctx->state = 0;
168   ctx->accept = 1;
169 }
170
171 /* Use macro to make the code simpler..., but error case is tricky.
172    We spent most of the CPU in decoding, so we are doing this
173    thing. */
174 #define hd_huff_decode_sym_emit(bufs, sym, avail)                              \
175   do {                                                                         \
176     if ((avail)) {                                                             \
177       nghttp2_bufs_fast_addb((bufs), (sym));                                   \
178       --(avail);                                                               \
179     } else {                                                                   \
180       rv = nghttp2_bufs_addb((bufs), (sym));                                   \
181       if (rv != 0) {                                                           \
182         return rv;                                                             \
183       }                                                                        \
184       (avail) = nghttp2_bufs_cur_avail((bufs));                                \
185     }                                                                          \
186   } while (0)
187
188 ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
189                                nghttp2_bufs *bufs, const uint8_t *src,
190                                size_t srclen, int final) {
191   size_t i;
192   int rv;
193   size_t avail;
194
195   avail = nghttp2_bufs_cur_avail(bufs);
196
197   /* We use the decoding algorithm described in
198      http://graphics.ics.uci.edu/pub/Prefix.pdf */
199   for (i = 0; i < srclen; ++i) {
200     const nghttp2_huff_decode *t;
201
202     t = &huff_decode_table[ctx->state][src[i] >> 4];
203     if (t->flags & NGHTTP2_HUFF_FAIL) {
204       return NGHTTP2_ERR_HEADER_COMP;
205     }
206     if (t->flags & NGHTTP2_HUFF_SYM) {
207       /* this is macro, and may return from this function on error */
208       hd_huff_decode_sym_emit(bufs, t->sym, avail);
209     }
210
211     t = &huff_decode_table[t->state][src[i] & 0xf];
212     if (t->flags & NGHTTP2_HUFF_FAIL) {
213       return NGHTTP2_ERR_HEADER_COMP;
214     }
215     if (t->flags & NGHTTP2_HUFF_SYM) {
216       /* this is macro, and may return from this function on error */
217       hd_huff_decode_sym_emit(bufs, t->sym, avail);
218     }
219
220     ctx->state = t->state;
221     ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0;
222   }
223   if (final && !ctx->accept) {
224     return NGHTTP2_ERR_HEADER_COMP;
225   }
226   return (ssize_t)i;
227 }