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 /*
34  * Encodes huffman code |sym| into |*dest_ptr|, whose least |rembits|
35  * bits are not filled yet.  The |rembits| must be in range [1, 8],
36  * inclusive.  At the end of the process, the |*dest_ptr| is updated
37  * and points where next output should be placed. The number of
38  * unfilled bits in the pointed location is returned.
39  */
huff_encode_sym(nghttp2_bufs * bufs,size_t * avail_ptr,size_t rembits,const nghttp2_huff_sym * sym)40 static ssize_t huff_encode_sym(nghttp2_bufs *bufs, size_t *avail_ptr,
41                                size_t rembits, const nghttp2_huff_sym *sym)
42 {
43     int rv;
44     size_t nbits = sym->nbits;
45     uint32_t code = sym->code;
46 
47     /* We assume that sym->nbits <= 32 */
48     if (rembits > nbits) {
49         nghttp2_bufs_fast_orb_hold(bufs, (uint8_t)(code << (rembits - nbits)));
50         return (ssize_t)(rembits - nbits);
51     }
52 
53     if (rembits == nbits) {
54         nghttp2_bufs_fast_orb(bufs, (uint8_t)code);
55         --*avail_ptr;
56         return 8;
57     }
58 
59     nghttp2_bufs_fast_orb(bufs, (uint8_t)(code >> (nbits - rembits)));
60     --*avail_ptr;
61 
62     nbits -= rembits;
63     if (nbits & 0x7) {
64         /* align code to MSB byte boundary */
65         code <<= 8 - (nbits & 0x7);
66     }
67 
68     if (*avail_ptr < (nbits + 7) / 8) {
69         /* slow path */
70         if (nbits > 24) {
71             rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 24));
72             if (rv != 0) {
73                 return rv;
74             }
75             nbits -= 8;
76         }
77         if (nbits > 16) {
78             rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 16));
79             if (rv != 0) {
80                 return rv;
81             }
82             nbits -= 8;
83         }
84         if (nbits > 8) {
85             rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 8));
86             if (rv != 0) {
87                 return rv;
88             }
89             nbits -= 8;
90         }
91         if (nbits == 8) {
92             rv = nghttp2_bufs_addb(bufs, (uint8_t)code);
93             if (rv != 0) {
94                 return rv;
95             }
96             *avail_ptr = nghttp2_bufs_cur_avail(bufs);
97             return 8;
98         }
99 
100         rv = nghttp2_bufs_addb_hold(bufs, (uint8_t)code);
101         if (rv != 0) {
102             return rv;
103         }
104         *avail_ptr = nghttp2_bufs_cur_avail(bufs);
105         return (ssize_t)(8 - nbits);
106     }
107 
108     /* fast path, since most code is less than 8 */
109     if (nbits < 8) {
110         nghttp2_bufs_fast_addb_hold(bufs, (uint8_t)code);
111         *avail_ptr = nghttp2_bufs_cur_avail(bufs);
112         return (ssize_t)(8 - nbits);
113     }
114 
115     /* handle longer code path */
116     if (nbits > 24) {
117         nghttp2_bufs_fast_addb(bufs, (uint8_t)(code >> 24));
118         nbits -= 8;
119     }
120 
121     if (nbits > 16) {
122         nghttp2_bufs_fast_addb(bufs, (uint8_t)(code >> 16));
123         nbits -= 8;
124     }
125 
126     if (nbits > 8) {
127         nghttp2_bufs_fast_addb(bufs, (uint8_t)(code >> 8));
128         nbits -= 8;
129     }
130 
131     if (nbits == 8) {
132         nghttp2_bufs_fast_addb(bufs, (uint8_t)code);
133         *avail_ptr = nghttp2_bufs_cur_avail(bufs);
134         return 8;
135     }
136 
137     nghttp2_bufs_fast_addb_hold(bufs, (uint8_t)code);
138     *avail_ptr = nghttp2_bufs_cur_avail(bufs);
139     return (ssize_t)(8 - nbits);
140 }
141 
nghttp2_hd_huff_encode_count(const uint8_t * src,size_t len)142 size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len)
143 {
144     size_t i;
145     size_t nbits = 0;
146 
147     for (i = 0; i < len; ++i) {
148         nbits += huff_sym_table[src[i]].nbits;
149     }
150     /* pad the prefix of EOS (256) */
151     return (nbits + 7) / 8;
152 }
153 
nghttp2_hd_huff_encode(nghttp2_bufs * bufs,const uint8_t * src,size_t srclen)154 int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src,
155                            size_t srclen)
156 {
157     int rv;
158     ssize_t rembits = 8;
159     size_t i;
160     size_t avail;
161 
162     avail = nghttp2_bufs_cur_avail(bufs);
163 
164     for (i = 0; i < srclen; ++i) {
165         const nghttp2_huff_sym *sym = &huff_sym_table[src[i]];
166         if (rembits == 8) {
167             if (avail) {
168                 nghttp2_bufs_fast_addb_hold(bufs, 0);
169             } else {
170                 rv = nghttp2_bufs_addb_hold(bufs, 0);
171                 if (rv != 0) {
172                     return rv;
173                 }
174                 avail = nghttp2_bufs_cur_avail(bufs);
175             }
176         }
177         rembits = huff_encode_sym(bufs, &avail, (size_t)rembits, sym);
178         if (rembits < 0) {
179             return (int)rembits;
180         }
181     }
182     /* 256 is special terminal symbol, pad with its prefix */
183     if (rembits < 8) {
184         /* if rembits < 8, we should have at least 1 buffer space
185            available */
186         const nghttp2_huff_sym *sym = &huff_sym_table[256];
187         assert(avail);
188         /* Caution we no longer adjust avail here */
189         nghttp2_bufs_fast_orb(
190             bufs, (uint8_t)(sym->code >> (sym->nbits - (size_t)rembits)));
191     }
192 
193     return 0;
194 }
195 
nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context * ctx)196 void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx)
197 {
198     ctx->state = 0;
199     ctx->accept = 1;
200 }
201 
nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context * ctx,nghttp2_buf * buf,const uint8_t * src,size_t srclen,int final)202 ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
203                                nghttp2_buf *buf, const uint8_t *src,
204                                size_t srclen, int final)
205 {
206     size_t i;
207 
208     /* We use the decoding algorithm described in
209        http://graphics.ics.uci.edu/pub/Prefix.pdf */
210     for (i = 0; i < srclen; ++i) {
211         const nghttp2_huff_decode *t;
212 
213         t = &huff_decode_table[ctx->state][src[i] >> 4];
214         if (t->flags & NGHTTP2_HUFF_FAIL) {
215             return NGHTTP2_ERR_HEADER_COMP;
216         }
217         if (t->flags & NGHTTP2_HUFF_SYM) {
218             *buf->last++ = t->sym;
219         }
220 
221         t = &huff_decode_table[t->state][src[i] & 0xf];
222         if (t->flags & NGHTTP2_HUFF_FAIL) {
223             return NGHTTP2_ERR_HEADER_COMP;
224         }
225         if (t->flags & NGHTTP2_HUFF_SYM) {
226             *buf->last++ = t->sym;
227         }
228 
229         ctx->state = t->state;
230         ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0;
231     }
232     if (final && !ctx->accept) {
233         return NGHTTP2_ERR_HEADER_COMP;
234     }
235     return (ssize_t)i;
236 }
237