blob: 50faefd9c5b68ed727dabd9dbfebb3d658827bab [file] [log] [blame]
QUICHE team82dee2f2019-01-18 12:35:12 -05001// Copyright 2014 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
QUICHE team5be974e2020-12-29 18:35:24 -05005#include "spdy/core/hpack/hpack_huffman_table.h"
QUICHE team82dee2f2019-01-18 12:35:12 -05006
7#include <algorithm>
8#include <cmath>
9#include <limits>
10#include <memory>
11
QUICHE team5be974e2020-12-29 18:35:24 -050012#include "spdy/core/hpack/hpack_output_stream.h"
13#include "spdy/platform/api/spdy_estimate_memory_usage.h"
14#include "spdy/platform/api/spdy_logging.h"
QUICHE team82dee2f2019-01-18 12:35:12 -050015
16namespace spdy {
17
18namespace {
19
20bool SymbolLengthAndIdCompare(const HpackHuffmanSymbol& a,
21 const HpackHuffmanSymbol& b) {
22 if (a.length == b.length) {
23 return a.id < b.id;
24 }
25 return a.length < b.length;
26}
27bool SymbolIdCompare(const HpackHuffmanSymbol& a, const HpackHuffmanSymbol& b) {
28 return a.id < b.id;
29}
30
31} // namespace
32
33HpackHuffmanTable::HpackHuffmanTable() : pad_bits_(0), failed_symbol_id_(0) {}
34
35HpackHuffmanTable::~HpackHuffmanTable() = default;
36
37bool HpackHuffmanTable::Initialize(const HpackHuffmanSymbol* input_symbols,
38 size_t symbol_count) {
39 CHECK(!IsInitialized());
40 DCHECK_LE(symbol_count, std::numeric_limits<uint16_t>::max());
41
42 std::vector<Symbol> symbols(symbol_count);
43 // Validate symbol id sequence, and copy into |symbols|.
44 for (uint16_t i = 0; i < symbol_count; i++) {
45 if (i != input_symbols[i].id) {
46 failed_symbol_id_ = i;
47 return false;
48 }
49 symbols[i] = input_symbols[i];
50 }
51 // Order on length and ID ascending, to verify symbol codes are canonical.
52 std::sort(symbols.begin(), symbols.end(), SymbolLengthAndIdCompare);
53 if (symbols[0].code != 0) {
54 failed_symbol_id_ = 0;
55 return false;
56 }
57 for (size_t i = 1; i != symbols.size(); i++) {
58 unsigned code_shift = 32 - symbols[i - 1].length;
59 uint32_t code = symbols[i - 1].code + (1 << code_shift);
60
61 if (code != symbols[i].code) {
62 failed_symbol_id_ = symbols[i].id;
63 return false;
64 }
65 if (code < symbols[i - 1].code) {
66 // An integer overflow occurred. This implies the input
67 // lengths do not represent a valid Huffman code.
68 failed_symbol_id_ = symbols[i].id;
69 return false;
70 }
71 }
72 if (symbols.back().length < 8) {
73 // At least one code (such as an EOS symbol) must be 8 bits or longer.
74 // Without this, some inputs will not be encodable in a whole number
75 // of bytes.
76 return false;
77 }
78 pad_bits_ = static_cast<uint8_t>(symbols.back().code >> 24);
79
80 // Order on symbol ID ascending.
81 std::sort(symbols.begin(), symbols.end(), SymbolIdCompare);
82 BuildEncodeTable(symbols);
83 return true;
84}
85
86void HpackHuffmanTable::BuildEncodeTable(const std::vector<Symbol>& symbols) {
87 for (size_t i = 0; i != symbols.size(); i++) {
88 const Symbol& symbol = symbols[i];
89 CHECK_EQ(i, symbol.id);
90 code_by_id_.push_back(symbol.code);
91 length_by_id_.push_back(symbol.length);
92 }
93}
94
95bool HpackHuffmanTable::IsInitialized() const {
96 return !code_by_id_.empty();
97}
98
vasilvva89e7fa2020-10-12 21:35:05 -070099void HpackHuffmanTable::EncodeString(absl::string_view in,
QUICHE team82dee2f2019-01-18 12:35:12 -0500100 HpackOutputStream* out) const {
101 size_t bit_remnant = 0;
102 for (size_t i = 0; i != in.size(); i++) {
103 uint16_t symbol_id = static_cast<uint8_t>(in[i]);
104 CHECK_GT(code_by_id_.size(), symbol_id);
105
106 // Load, and shift code to low bits.
107 unsigned length = length_by_id_[symbol_id];
108 uint32_t code = code_by_id_[symbol_id] >> (32 - length);
109
110 bit_remnant = (bit_remnant + length) % 8;
111
112 if (length > 24) {
113 out->AppendBits(static_cast<uint8_t>(code >> 24), length - 24);
114 length = 24;
115 }
116 if (length > 16) {
117 out->AppendBits(static_cast<uint8_t>(code >> 16), length - 16);
118 length = 16;
119 }
120 if (length > 8) {
121 out->AppendBits(static_cast<uint8_t>(code >> 8), length - 8);
122 length = 8;
123 }
124 out->AppendBits(static_cast<uint8_t>(code), length);
125 }
126 if (bit_remnant != 0) {
127 // Pad current byte as required.
128 out->AppendBits(pad_bits_ >> bit_remnant, 8 - bit_remnant);
129 }
130}
131
vasilvva89e7fa2020-10-12 21:35:05 -0700132size_t HpackHuffmanTable::EncodedSize(absl::string_view in) const {
QUICHE team82dee2f2019-01-18 12:35:12 -0500133 size_t bit_count = 0;
134 for (size_t i = 0; i != in.size(); i++) {
135 uint16_t symbol_id = static_cast<uint8_t>(in[i]);
136 CHECK_GT(code_by_id_.size(), symbol_id);
137
138 bit_count += length_by_id_[symbol_id];
139 }
140 if (bit_count % 8 != 0) {
141 bit_count += 8 - bit_count % 8;
142 }
143 return bit_count / 8;
144}
145
146size_t HpackHuffmanTable::EstimateMemoryUsage() const {
147 return SpdyEstimateMemoryUsage(code_by_id_) +
148 SpdyEstimateMemoryUsage(length_by_id_);
149}
150
151} // namespace spdy