|  | // Copyright (c) 2018 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder.h" | 
|  |  | 
|  | #include "net/third_party/quiche/src/http2/hpack/huffman/huffman_spec_tables.h" | 
|  | #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" | 
|  |  | 
|  | // TODO(jamessynge): Remove use of binary literals, that is a C++ 14 feature. | 
|  |  | 
|  | namespace http2 { | 
|  |  | 
|  | size_t ExactHuffmanSize(Http2StringPiece plain) { | 
|  | size_t bits = 0; | 
|  | for (const uint8_t c : plain) { | 
|  | bits += HuffmanSpecTables::kCodeLengths[c]; | 
|  | } | 
|  | return (bits + 7) / 8; | 
|  | } | 
|  |  | 
|  | size_t BoundedHuffmanSize(Http2StringPiece plain) { | 
|  | // TODO(jamessynge): Determine whether we should set the min size for Huffman | 
|  | // encoding much higher (i.e. if less than N, then the savings isn't worth | 
|  | // the cost of encoding and decoding). Of course, we need to decide on a | 
|  | // value function, which might be throughput on a full load test, or a | 
|  | // microbenchmark of the time to encode and then decode a HEADERS frame, | 
|  | // possibly with the cost of crypto included (i.e. crypto is going to have | 
|  | // a fairly constant per-byte cost, so reducing the number of bytes in-transit | 
|  | // reduces the number that must be encrypted and later decrypted). | 
|  | if (plain.size() < 3) { | 
|  | // Huffman encoded string can't be smaller than the plain size for very | 
|  | // short strings. | 
|  | return plain.size(); | 
|  | } | 
|  | // TODO(jamessynge): Measure whether this can be done more efficiently with | 
|  | // nested loops (e.g. make exact measurement of 8 bytes, then check if min | 
|  | // remaining is too long). | 
|  | // Compute the number of bits in an encoding that is shorter than the plain | 
|  | // string (i.e. the number of bits in a string 1 byte shorter than plain), | 
|  | // and use this as the limit of the size of the encoding. | 
|  | const size_t limit_bits = (plain.size() - 1) * 8; | 
|  | // The shortest code length in the Huffman table of the HPACK spec has 5 bits | 
|  | // (e.g. for 0, 1, a and e). | 
|  | const size_t min_code_length = 5; | 
|  | // We can therefore say that all plain text bytes whose code length we've not | 
|  | // yet looked up will take at least 5 bits. | 
|  | size_t min_bits_remaining = plain.size() * min_code_length; | 
|  | size_t bits = 0; | 
|  | for (const uint8_t c : plain) { | 
|  | bits += HuffmanSpecTables::kCodeLengths[c]; | 
|  | min_bits_remaining -= min_code_length; | 
|  | // If our minimum estimate of the total number of bits won't yield an | 
|  | // encoding shorter the plain text, let's bail. | 
|  | const size_t minimum_bits_total = bits + min_bits_remaining; | 
|  | if (minimum_bits_total > limit_bits) { | 
|  | bits += min_bits_remaining; | 
|  | break; | 
|  | } | 
|  | } | 
|  | return (bits + 7) / 8; | 
|  | } | 
|  |  | 
|  | void HuffmanEncode(Http2StringPiece plain, std::string* huffman) { | 
|  | DCHECK(huffman != nullptr); | 
|  | huffman->clear();         // Note that this doesn't release memory. | 
|  | uint64_t bit_buffer = 0;  // High-bit is next bit to output. Not clear if that | 
|  | // is more performant than having the low-bit be the | 
|  | // last to be output. | 
|  | size_t bits_unused = 64;  // Number of bits available for the next code. | 
|  | for (uint8_t c : plain) { | 
|  | size_t code_length = HuffmanSpecTables::kCodeLengths[c]; | 
|  | if (bits_unused < code_length) { | 
|  | // There isn't enough room in bit_buffer for the code of c. | 
|  | // Flush until bits_unused > 56 (i.e. 64 - 8). | 
|  | do { | 
|  | char h = static_cast<char>(bit_buffer >> 56); | 
|  | bit_buffer <<= 8; | 
|  | bits_unused += 8; | 
|  | // Perhaps would be more efficient if we populated an array of chars, | 
|  | // so we don't have to call push_back each time. Reconsider if used | 
|  | // for production. | 
|  | huffman->push_back(h); | 
|  | } while (bits_unused <= 56); | 
|  | } | 
|  | uint64_t code = HuffmanSpecTables::kRightCodes[c]; | 
|  | size_t shift_by = bits_unused - code_length; | 
|  | bit_buffer |= (code << shift_by); | 
|  | bits_unused -= code_length; | 
|  | } | 
|  | // bit_buffer contains (64-bits_unused) bits that still need to be flushed. | 
|  | // Output whole bytes until we don't have any whole bytes left. | 
|  | size_t bits_used = 64 - bits_unused; | 
|  | while (bits_used >= 8) { | 
|  | char h = static_cast<char>(bit_buffer >> 56); | 
|  | bit_buffer <<= 8; | 
|  | bits_used -= 8; | 
|  | huffman->push_back(h); | 
|  | } | 
|  | if (bits_used > 0) { | 
|  | // We have less than a byte left to output. The spec calls for padding out | 
|  | // the final byte with the leading bits of the EOS symbol (30 1-bits). | 
|  | constexpr uint64_t leading_eos_bits = 0b11111111; | 
|  | bit_buffer |= (leading_eos_bits << (56 - bits_used)); | 
|  | char h = static_cast<char>(bit_buffer >> 56); | 
|  | huffman->push_back(h); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace http2 |