OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 // |
| 5 // Decoder for strings encoded using the HPACK Huffman Code (see |
| 6 // https://httpwg.github.io/specs/rfc7541.html#huffman.code). |
| 7 // |
| 8 // This implementation is inspired by the One-Shift algorithm described in |
| 9 // "On the Implementation of Minimum Redundancy Prefix Codes", by Alistair |
| 10 // Moffat and Andrew Turpin, 1997. |
| 11 // See also https://en.wikipedia.org/wiki/Canonical_Huffman_code for background |
| 12 // on canonical Huffman codes. |
| 13 // |
| 14 // This decoder differs from that in .../spdy/hpack/hpack_huffman_table.cc |
| 15 // as follows: |
| 16 // 1) It decodes only the code described in RFC7541, where as the older |
| 17 // implementation supported any canonical Huffman code provided at run |
| 18 // time. |
| 19 // 2) It uses a fixed amount of memory allocated at build time; it doesn't |
| 20 // construct a tree of of decoding tables based on an encoding |
| 21 // table provided at run time. |
| 22 // 3) In benchmarks it runs from 10% to 70% faster, based on the length |
| 23 // of the strings (faster for longer strings). Some of the improvements |
| 24 // could be back ported, but others are fundamental to the approach. |
| 25 |
| 26 #include "net/spdy/hpack/hpack_huffman_decoder.h" |
| 27 |
| 28 #include <bitset> |
| 29 #include <limits> |
| 30 #include <utility> |
| 31 |
| 32 #include "base/logging.h" |
| 33 #include "net/spdy/hpack/hpack_input_stream.h" |
| 34 |
| 35 namespace net { |
| 36 namespace { |
| 37 |
| 38 typedef HpackHuffmanDecoder::HuffmanWord HuffmanWord; |
| 39 typedef HpackHuffmanDecoder::HuffmanCodeLength HuffmanCodeLength; |
| 40 |
| 41 const HuffmanCodeLength kHuffmanWordLength = |
| 42 std::numeric_limits<HuffmanWord>::digits; |
| 43 |
| 44 const HuffmanCodeLength kMinCodeLength = 5; |
| 45 const HuffmanCodeLength kMaxCodeLength = 30; |
| 46 |
| 47 const HuffmanWord kInvalidLJCode = ~static_cast<HuffmanWord>(0); |
| 48 // Length of a code in bits to the first code with that length, left-justified. |
| 49 // Note that this can be computed from kLengthToFirstCanonical. |
| 50 const HuffmanWord kLengthToFirstLJCode[] = { |
| 51 kInvalidLJCode, // There are no codes of length 0. |
| 52 kInvalidLJCode, // There are no codes of length 1. |
| 53 kInvalidLJCode, // There are no codes of length 2. |
| 54 kInvalidLJCode, // There are no codes of length 3. |
| 55 kInvalidLJCode, // There are no codes of length 4. |
| 56 0x00000000, // Length 5. |
| 57 0x50000000, // Length 6. |
| 58 0xb8000000, // Length 7. |
| 59 0xf8000000, // Length 8. |
| 60 kInvalidLJCode, // There are no codes of length 9. |
| 61 0xfe000000, // Length 10. |
| 62 0xff400000, // Length 11. |
| 63 0xffa00000, // Length 12. |
| 64 0xffc00000, // Length 13. |
| 65 0xfff00000, // Length 14. |
| 66 0xfff80000, // Length 15. |
| 67 kInvalidLJCode, // There are no codes of length 16. |
| 68 kInvalidLJCode, // There are no codes of length 17. |
| 69 kInvalidLJCode, // There are no codes of length 18. |
| 70 0xfffe0000, // Length 19. |
| 71 0xfffe6000, // Length 20. |
| 72 0xfffee000, // Length 21. |
| 73 0xffff4800, // Length 22. |
| 74 0xffffb000, // Length 23. |
| 75 0xffffea00, // Length 24. |
| 76 0xfffff600, // Length 25. |
| 77 0xfffff800, // Length 26. |
| 78 0xfffffbc0, // Length 27. |
| 79 0xfffffe20, // Length 28. |
| 80 kInvalidLJCode, // There are no codes of length 29. |
| 81 0xfffffff0, // Length 30. |
| 82 }; |
| 83 |
| 84 // TODO(jamessynge): Determine the performance impact of different types for |
| 85 // the elements of this array (i.e. a larger type uses more cache, yet might |
| 86 // better on some architectures). |
| 87 const uint8_t kInvalidCanonical = 255; |
| 88 // Maps from length of a code to the first 'canonical symbol' with that length. |
| 89 const uint8_t kLengthToFirstCanonical[] = { |
| 90 kInvalidCanonical, // Length 0, 0 codes. |
| 91 kInvalidCanonical, // Length 1, 0 codes. |
| 92 kInvalidCanonical, // Length 2, 0 codes. |
| 93 kInvalidCanonical, // Length 3, 0 codes. |
| 94 kInvalidCanonical, // Length 4, 0 codes. |
| 95 0, // Length 5, 10 codes. |
| 96 10, // Length 6, 26 codes. |
| 97 36, // Length 7, 32 codes. |
| 98 68, // Length 8, 6 codes. |
| 99 kInvalidCanonical, // Length 9, 0 codes. |
| 100 74, // Length 10, 5 codes. |
| 101 79, // Length 11, 3 codes. |
| 102 82, // Length 12, 2 codes. |
| 103 84, // Length 13, 6 codes. |
| 104 90, // Length 14, 2 codes. |
| 105 92, // Length 15, 3 codes. |
| 106 kInvalidCanonical, // Length 16, 0 codes. |
| 107 kInvalidCanonical, // Length 17, 0 codes. |
| 108 kInvalidCanonical, // Length 18, 0 codes. |
| 109 95, // Length 19, 3 codes. |
| 110 98, // Length 20, 8 codes. |
| 111 106, // Length 21, 13 codes. |
| 112 119, // Length 22, 26 codes. |
| 113 145, // Length 23, 29 codes. |
| 114 174, // Length 24, 12 codes. |
| 115 186, // Length 25, 4 codes. |
| 116 190, // Length 26, 15 codes. |
| 117 205, // Length 27, 19 codes. |
| 118 224, // Length 28, 29 codes. |
| 119 kInvalidCanonical, // Length 29, 0 codes. |
| 120 253, // Length 30, 4 codes. |
| 121 }; |
| 122 |
| 123 // Mapping from canonical symbol (0 to 255) to actual symbol. |
| 124 // clang-format off |
| 125 const uint8_t kCanonicalToSymbol[] = { |
| 126 '0', '1', '2', 'a', 'c', 'e', 'i', 'o', |
| 127 's', 't', 0x20, '%', '-', '.', '/', '3', |
| 128 '4', '5', '6', '7', '8', '9', '=', 'A', |
| 129 '_', 'b', 'd', 'f', 'g', 'h', 'l', 'm', |
| 130 'n', 'p', 'r', 'u', ':', 'B', 'C', 'D', |
| 131 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', |
| 132 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', |
| 133 'U', 'V', 'W', 'Y', 'j', 'k', 'q', 'v', |
| 134 'w', 'x', 'y', 'z', '&', '*', ',', ';', |
| 135 'X', 'Z', '!', '\"', '(', ')', '?', '\'', |
| 136 '+', '|', '#', '>', 0x00, '$', '@', '[', |
| 137 ']', '~', '^', '}', '<', '`', '{', '\\', |
| 138 0xc3, 0xd0, 0x80, 0x82, 0x83, 0xa2, 0xb8, 0xc2, |
| 139 0xe0, 0xe2, 0x99, 0xa1, 0xa7, 0xac, 0xb0, 0xb1, |
| 140 0xb3, 0xd1, 0xd8, 0xd9, 0xe3, 0xe5, 0xe6, 0x81, |
| 141 0x84, 0x85, 0x86, 0x88, 0x92, 0x9a, 0x9c, 0xa0, |
| 142 0xa3, 0xa4, 0xa9, 0xaa, 0xad, 0xb2, 0xb5, 0xb9, |
| 143 0xba, 0xbb, 0xbd, 0xbe, 0xc4, 0xc6, 0xe4, 0xe8, |
| 144 0xe9, 0x01, 0x87, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, |
| 145 0x8f, 0x93, 0x95, 0x96, 0x97, 0x98, 0x9b, 0x9d, |
| 146 0x9e, 0xa5, 0xa6, 0xa8, 0xae, 0xaf, 0xb4, 0xb6, |
| 147 0xb7, 0xbc, 0xbf, 0xc5, 0xe7, 0xef, 0x09, 0x8e, |
| 148 0x90, 0x91, 0x94, 0x9f, 0xab, 0xce, 0xd7, 0xe1, |
| 149 0xec, 0xed, 0xc7, 0xcf, 0xea, 0xeb, 0xc0, 0xc1, |
| 150 0xc8, 0xc9, 0xca, 0xcd, 0xd2, 0xd5, 0xda, 0xdb, |
| 151 0xee, 0xf0, 0xf2, 0xf3, 0xff, 0xcb, 0xcc, 0xd3, |
| 152 0xd4, 0xd6, 0xdd, 0xde, 0xdf, 0xf1, 0xf4, 0xf5, |
| 153 0xf6, 0xf7, 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, |
| 154 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0b, |
| 155 0x0c, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, |
| 156 0x15, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, |
| 157 0x1e, 0x1f, 0x7f, 0xdc, 0xf9, 0x0a, 0x0d, 0x16, |
| 158 }; |
| 159 // clang-format on |
| 160 |
| 161 #ifndef NDEBUG |
| 162 |
| 163 // Only used in DLOG. |
| 164 bool IsEOSPrefix(HuffmanWord bits, HuffmanCodeLength bits_available) { |
| 165 if (bits_available == 0) { |
| 166 return true; |
| 167 } |
| 168 // We expect all the bits below the high order |bits_available| bits |
| 169 // to be cleared. |
| 170 HuffmanWord expected = HuffmanWord(0xffffffff) << (32 - bits_available); |
| 171 return bits == expected; |
| 172 } |
| 173 |
| 174 #endif // NDEBUG |
| 175 |
| 176 } // namespace |
| 177 |
| 178 // TODO(jamessynge): Should we read these magic numbers from |
| 179 // kLengthToFirstLJCode? Would that reduce cache consumption? Slow decoding? |
| 180 // TODO(jamessynge): Is this being inlined by the compiler? Should we inline |
| 181 // into DecodeString the tests for code lengths 5 through 8 (> 99% of codes |
| 182 // according to the HPACK spec)? |
| 183 HpackHuffmanDecoder::HuffmanCodeLength HpackHuffmanDecoder::CodeLengthOfPrefix( |
| 184 HpackHuffmanDecoder::HuffmanWord value) { |
| 185 HuffmanCodeLength length; |
| 186 if (value < 0xb8000000) { |
| 187 if (value < 0x50000000) { |
| 188 length = 5; |
| 189 } else { |
| 190 length = 6; |
| 191 } |
| 192 } else { |
| 193 if (value < 0xfe000000) { |
| 194 if (value < 0xf8000000) { |
| 195 length = 7; |
| 196 } else { |
| 197 length = 8; |
| 198 } |
| 199 } else { |
| 200 if (value < 0xffc00000) { |
| 201 if (value < 0xffa00000) { |
| 202 if (value < 0xff400000) { |
| 203 length = 10; |
| 204 } else { |
| 205 length = 11; |
| 206 } |
| 207 } else { |
| 208 length = 12; |
| 209 } |
| 210 } else { |
| 211 if (value < 0xfffe0000) { |
| 212 if (value < 0xfff80000) { |
| 213 if (value < 0xfff00000) { |
| 214 length = 13; |
| 215 } else { |
| 216 length = 14; |
| 217 } |
| 218 } else { |
| 219 length = 15; |
| 220 } |
| 221 } else { |
| 222 if (value < 0xffff4800) { |
| 223 if (value < 0xfffee000) { |
| 224 if (value < 0xfffe6000) { |
| 225 length = 19; |
| 226 } else { |
| 227 length = 20; |
| 228 } |
| 229 } else { |
| 230 length = 21; |
| 231 } |
| 232 } else { |
| 233 if (value < 0xffffea00) { |
| 234 if (value < 0xffffb000) { |
| 235 length = 22; |
| 236 } else { |
| 237 length = 23; |
| 238 } |
| 239 } else { |
| 240 if (value < 0xfffffbc0) { |
| 241 if (value < 0xfffff800) { |
| 242 if (value < 0xfffff600) { |
| 243 length = 24; |
| 244 } else { |
| 245 length = 25; |
| 246 } |
| 247 } else { |
| 248 length = 26; |
| 249 } |
| 250 } else { |
| 251 if (value < 0xfffffff0) { |
| 252 if (value < 0xfffffe20) { |
| 253 length = 27; |
| 254 } else { |
| 255 length = 28; |
| 256 } |
| 257 } else { |
| 258 length = 30; |
| 259 } |
| 260 } |
| 261 } |
| 262 } |
| 263 } |
| 264 } |
| 265 } |
| 266 } |
| 267 return length; |
| 268 } |
| 269 |
| 270 HuffmanWord HpackHuffmanDecoder::DecodeToCanonical( |
| 271 HuffmanCodeLength code_length, |
| 272 HuffmanWord bits) { |
| 273 DCHECK_LE(kMinCodeLength, code_length); |
| 274 DCHECK_LE(code_length, kMaxCodeLength); |
| 275 |
| 276 // What is the first left-justified code of length |code_length|? |
| 277 HuffmanWord first_lj_code = kLengthToFirstLJCode[code_length]; |
| 278 DCHECK_NE(kInvalidLJCode, first_lj_code); |
| 279 |
| 280 // Which canonical symbol corresponds to the high order |code_length| |
| 281 // bits of |first_lj_code|? |
| 282 HuffmanWord first_canonical = kLengthToFirstCanonical[code_length]; |
| 283 DCHECK_NE(kInvalidCanonical, first_canonical); |
| 284 |
| 285 // What is the position of the canonical symbol being decoded within |
| 286 // the canonical symbols of length |code_length|? |
| 287 HuffmanWord ordinal_in_length = |
| 288 ((bits - first_lj_code) >> (kHuffmanWordLength - code_length)); |
| 289 |
| 290 // Combined these two to produce the position of the canonical symbol |
| 291 // being decoded within all of the canonical symbols. |
| 292 return first_canonical + ordinal_in_length; |
| 293 } |
| 294 |
| 295 char HpackHuffmanDecoder::CanonicalToSource(HuffmanWord canonical) { |
| 296 DCHECK_LT(canonical, 256u); |
| 297 return static_cast<char>(kCanonicalToSymbol[canonical]); |
| 298 } |
| 299 |
| 300 // TODO(jamessynge): Maybe further refactorings, including just passing in a |
| 301 // StringPiece instead of an HpackInputStream, thus avoiding the PeekBits calls, |
| 302 // and also allowing us to separate the code into portions dealing with long |
| 303 // strings, and a later portion dealing with the last few bytes of strings. |
| 304 // TODO(jamessynge): Determine if that is worth it by adding some counters to |
| 305 // measure the distribution of string sizes seen in practice. |
| 306 bool HpackHuffmanDecoder::DecodeString(HpackInputStream* in, |
| 307 size_t out_capacity, |
| 308 std::string* out) { |
| 309 out->clear(); |
| 310 |
| 311 // Load |bits| with the leading bits of the input stream, left justified |
| 312 // (i.e. the bits of the first byte are the high-order bits of |bits|, |
| 313 // and the bits of the fourth byte are the low-order bits of |bits|). |
| 314 // |peeked_success| if there are more bits in |*in| (i.e. the encoding |
| 315 // of the string to be decoded is more than 4 bytes). |
| 316 |
| 317 auto bits_available_and_bits = in->InitializePeekBits(); |
| 318 HuffmanCodeLength bits_available = bits_available_and_bits.first; |
| 319 HuffmanWord bits = bits_available_and_bits.second; |
| 320 |
| 321 // |peeked_success| tracks whether the previous PeekBits call was able to |
| 322 // store any new bits into |bits|. For the first pass through the loop below |
| 323 // the value false is appropriate: |
| 324 // If we have 32 bits (i.e. the input has at least 4 bytes), then: |
| 325 // |peeked_sucess| is not examined because |code_length| is |
| 326 // at most 30 in the HPACK Huffman Code. |
| 327 // If we have at most 24 bits (i.e. the input has at most 3 bytes), then: |
| 328 // It is possible that the very first |code_length| is greater than |
| 329 // |bits_available|, in which case we need to read peeked_success to |
| 330 // determine whether we should try to read more input, or have already |
| 331 // loaded |bits| with the final bits of the input. |
| 332 // After the first loop |peeked_success| has been set by a call to PeekBits. |
| 333 bool peeked_success = false; |
| 334 |
| 335 while (true) { |
| 336 const HuffmanCodeLength code_length = CodeLengthOfPrefix(bits); |
| 337 DCHECK_LE(kMinCodeLength, code_length); |
| 338 DCHECK_LE(code_length, kMaxCodeLength); |
| 339 DVLOG(1) << "bits: 0b" << std::bitset<32>(bits) |
| 340 << " (avail=" << bits_available << ")" |
| 341 << " prefix length: " << code_length |
| 342 << (code_length > bits_available ? " *****" : ""); |
| 343 if (code_length > bits_available) { |
| 344 if (!peeked_success) { |
| 345 // Unable to read enough input for a match. If only a portion of |
| 346 // the last byte remains, this is a successful EOS condition. |
| 347 // Note that this does NOT check whether the available bits are all |
| 348 // set to 1, which the encoder is required to set at EOS, and the |
| 349 // decoder is required to check. |
| 350 // TODO(jamessynge): Discuss whether we should enforce this check, |
| 351 // as required by the RFC, presumably flag guarded so that we can |
| 352 // disable it should it occur a lot. From my testing it appears that |
| 353 // our encoder may be doing this wrong. Sigh. |
| 354 // TODO(jamessynge): Add a counter for how often the remaining bits |
| 355 // are non-zero. |
| 356 in->ConsumeByteRemainder(); |
| 357 DLOG_IF(WARNING, |
| 358 (in->HasMoreData() || !IsEOSPrefix(bits, bits_available))) |
| 359 << "bits: 0b" << std::bitset<32>(bits) |
| 360 << " (avail=" << bits_available << ")" |
| 361 << " prefix length: " << code_length |
| 362 << " HasMoreData: " << in->HasMoreData(); |
| 363 return !in->HasMoreData(); |
| 364 } |
| 365 // We're dealing with a long code. It *might* be useful to add a special |
| 366 // method to HpackInputStream for getting more than "at most 8" bits |
| 367 // at a time. |
| 368 do { |
| 369 peeked_success = in->PeekBits(&bits_available, &bits); |
| 370 } while (peeked_success && bits_available < 32); |
| 371 } else { |
| 372 if (out->size() == out_capacity) { |
| 373 // This code would cause us to overflow |out_capacity|. |
| 374 // TODO(jamessynge) Avoid this case by pre-allocating out based on |
| 375 // scaling up the encoded size by 8/5 (shortest codes are 5 bits). |
| 376 DLOG(WARNING) << "Output size too large: " << out_capacity; |
| 377 return false; |
| 378 } |
| 379 |
| 380 // Convert from the prefix code of length |code_length| to the |
| 381 // canonical symbol (i.e. where the input symbols (bytes) are ordered by |
| 382 // increasing code length and then by their increasing uint8 value). |
| 383 HuffmanWord canonical = DecodeToCanonical(code_length, bits); |
| 384 |
| 385 bits = bits << code_length; |
| 386 bits_available -= code_length; |
| 387 in->ConsumeBits(code_length); |
| 388 |
| 389 if (canonical < 256) { |
| 390 out->push_back(CanonicalToSource(canonical)); |
| 391 } else { |
| 392 // Encoder is not supposed to explicity encode the EOS symbol (30 |
| 393 // 1-bits). |
| 394 // TODO(jamessynge): Discuss returning false here, as required by HPACK. |
| 395 DCHECK(false) << "EOS explicitly encoded!\n" |
| 396 << "bits: 0b" << std::bitset<32>(bits) |
| 397 << " (avail=" << bits_available << ")" |
| 398 << " prefix length: " << code_length |
| 399 << " canonical: " << canonical; |
| 400 } |
| 401 // Get some more bits for decoding (up to 8). |peeked_success| is true |
| 402 // if we got any bits. |
| 403 peeked_success = in->PeekBits(&bits_available, &bits); |
| 404 } |
| 405 DLOG_IF(WARNING, (VLOG_IS_ON(1) && bits_available < 32 && !peeked_success)) |
| 406 << "no more peeking possible"; |
| 407 } |
| 408 } |
| 409 |
| 410 } // namespace net |
OLD | NEW |