1 // Copyright (c) 2018 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 #include "quiche/quic/core/qpack/qpack_header_table.h"
6
7 #include "absl/strings/string_view.h"
8 #include "quiche/quic/core/qpack/qpack_static_table.h"
9 #include "quiche/quic/platform/api/quic_logging.h"
10 #include "quiche/common/platform/api/quiche_logging.h"
11
12 namespace quic {
13
QpackEncoderHeaderTable()14 QpackEncoderHeaderTable::QpackEncoderHeaderTable()
15 : static_index_(ObtainQpackStaticTable().GetStaticIndex()),
16 static_name_index_(ObtainQpackStaticTable().GetStaticNameIndex()) {}
17
InsertEntry(absl::string_view name,absl::string_view value)18 uint64_t QpackEncoderHeaderTable::InsertEntry(absl::string_view name,
19 absl::string_view value) {
20 const uint64_t index =
21 QpackHeaderTableBase<QpackEncoderDynamicTable>::InsertEntry(name, value);
22
23 // Make name and value point to the new entry.
24 name = dynamic_entries().back()->name();
25 value = dynamic_entries().back()->value();
26
27 auto index_result = dynamic_index_.insert(
28 std::make_pair(QpackLookupEntry{name, value}, index));
29 if (!index_result.second) {
30 // An entry with the same name and value already exists. It needs to be
31 // replaced, because |dynamic_index_| tracks the most recent entry for a
32 // given name and value.
33 QUICHE_DCHECK_GT(index, index_result.first->second);
34 dynamic_index_.erase(index_result.first);
35 auto result = dynamic_index_.insert(
36 std::make_pair(QpackLookupEntry{name, value}, index));
37 QUICHE_CHECK(result.second);
38 }
39
40 auto name_result = dynamic_name_index_.insert({name, index});
41 if (!name_result.second) {
42 // An entry with the same name already exists. It needs to be replaced,
43 // because |dynamic_name_index_| tracks the most recent entry for a given
44 // name.
45 QUICHE_DCHECK_GT(index, name_result.first->second);
46 dynamic_name_index_.erase(name_result.first);
47 auto result = dynamic_name_index_.insert({name, index});
48 QUICHE_CHECK(result.second);
49 }
50
51 return index;
52 }
53
FindHeaderField(absl::string_view name,absl::string_view value,bool * is_static,uint64_t * index) const54 QpackEncoderHeaderTable::MatchType QpackEncoderHeaderTable::FindHeaderField(
55 absl::string_view name, absl::string_view value, bool* is_static,
56 uint64_t* index) const {
57 QpackLookupEntry query{name, value};
58
59 // Look for exact match in static table.
60 auto index_it = static_index_.find(query);
61 if (index_it != static_index_.end()) {
62 *index = index_it->second;
63 *is_static = true;
64 return MatchType::kNameAndValue;
65 }
66
67 // Look for exact match in dynamic table.
68 index_it = dynamic_index_.find(query);
69 if (index_it != dynamic_index_.end()) {
70 *index = index_it->second;
71 *is_static = false;
72 return MatchType::kNameAndValue;
73 }
74
75 // Look for name match in static table.
76 auto name_index_it = static_name_index_.find(name);
77 if (name_index_it != static_name_index_.end()) {
78 *index = name_index_it->second;
79 *is_static = true;
80 return MatchType::kName;
81 }
82
83 // Look for name match in dynamic table.
84 name_index_it = dynamic_name_index_.find(name);
85 if (name_index_it != dynamic_name_index_.end()) {
86 *index = name_index_it->second;
87 *is_static = false;
88 return MatchType::kName;
89 }
90
91 return MatchType::kNoMatch;
92 }
93
MaxInsertSizeWithoutEvictingGivenEntry(uint64_t index) const94 uint64_t QpackEncoderHeaderTable::MaxInsertSizeWithoutEvictingGivenEntry(
95 uint64_t index) const {
96 QUICHE_DCHECK_LE(dropped_entry_count(), index);
97
98 if (index > inserted_entry_count()) {
99 // All entries are allowed to be evicted.
100 return dynamic_table_capacity();
101 }
102
103 // Initialize to current available capacity.
104 uint64_t max_insert_size = dynamic_table_capacity() - dynamic_table_size();
105
106 uint64_t entry_index = dropped_entry_count();
107 for (const auto& entry : dynamic_entries()) {
108 if (entry_index >= index) {
109 break;
110 }
111 ++entry_index;
112 max_insert_size += entry->Size();
113 }
114
115 return max_insert_size;
116 }
117
draining_index(float draining_fraction) const118 uint64_t QpackEncoderHeaderTable::draining_index(
119 float draining_fraction) const {
120 QUICHE_DCHECK_LE(0.0, draining_fraction);
121 QUICHE_DCHECK_LE(draining_fraction, 1.0);
122
123 const uint64_t required_space = draining_fraction * dynamic_table_capacity();
124 uint64_t space_above_draining_index =
125 dynamic_table_capacity() - dynamic_table_size();
126
127 if (dynamic_entries().empty() ||
128 space_above_draining_index >= required_space) {
129 return dropped_entry_count();
130 }
131
132 auto it = dynamic_entries().begin();
133 uint64_t entry_index = dropped_entry_count();
134 while (space_above_draining_index < required_space) {
135 space_above_draining_index += (*it)->Size();
136 ++it;
137 ++entry_index;
138 if (it == dynamic_entries().end()) {
139 return inserted_entry_count();
140 }
141 }
142
143 return entry_index;
144 }
145
RemoveEntryFromEnd()146 void QpackEncoderHeaderTable::RemoveEntryFromEnd() {
147 const QpackEntry* const entry = dynamic_entries().front().get();
148 const uint64_t index = dropped_entry_count();
149
150 auto index_it = dynamic_index_.find({entry->name(), entry->value()});
151 // Remove |dynamic_index_| entry only if it points to the same
152 // QpackEntry in dynamic_entries().
153 if (index_it != dynamic_index_.end() && index_it->second == index) {
154 dynamic_index_.erase(index_it);
155 }
156
157 auto name_it = dynamic_name_index_.find(entry->name());
158 // Remove |dynamic_name_index_| entry only if it points to the same
159 // QpackEntry in dynamic_entries().
160 if (name_it != dynamic_name_index_.end() && name_it->second == index) {
161 dynamic_name_index_.erase(name_it);
162 }
163
164 QpackHeaderTableBase<QpackEncoderDynamicTable>::RemoveEntryFromEnd();
165 }
166
QpackDecoderHeaderTable()167 QpackDecoderHeaderTable::QpackDecoderHeaderTable()
168 : static_entries_(ObtainQpackStaticTable().GetStaticEntries()) {}
169
~QpackDecoderHeaderTable()170 QpackDecoderHeaderTable::~QpackDecoderHeaderTable() {
171 for (auto& entry : observers_) {
172 entry.second->Cancel();
173 }
174 }
175
InsertEntry(absl::string_view name,absl::string_view value)176 uint64_t QpackDecoderHeaderTable::InsertEntry(absl::string_view name,
177 absl::string_view value) {
178 const uint64_t index =
179 QpackHeaderTableBase<QpackDecoderDynamicTable>::InsertEntry(name, value);
180
181 // Notify and deregister observers whose threshold is met, if any.
182 while (!observers_.empty()) {
183 auto it = observers_.begin();
184 if (it->first > inserted_entry_count()) {
185 break;
186 }
187 Observer* observer = it->second;
188 observers_.erase(it);
189 observer->OnInsertCountReachedThreshold();
190 }
191
192 return index;
193 }
194
LookupEntry(bool is_static,uint64_t index) const195 const QpackEntry* QpackDecoderHeaderTable::LookupEntry(bool is_static,
196 uint64_t index) const {
197 if (is_static) {
198 if (index >= static_entries_.size()) {
199 return nullptr;
200 }
201
202 return &static_entries_[index];
203 }
204
205 if (index < dropped_entry_count()) {
206 return nullptr;
207 }
208
209 index -= dropped_entry_count();
210
211 if (index >= dynamic_entries().size()) {
212 return nullptr;
213 }
214
215 return &dynamic_entries()[index];
216 }
217
RegisterObserver(uint64_t required_insert_count,Observer * observer)218 void QpackDecoderHeaderTable::RegisterObserver(uint64_t required_insert_count,
219 Observer* observer) {
220 QUICHE_DCHECK_GT(required_insert_count, 0u);
221 observers_.insert({required_insert_count, observer});
222 }
223
UnregisterObserver(uint64_t required_insert_count,Observer * observer)224 void QpackDecoderHeaderTable::UnregisterObserver(uint64_t required_insert_count,
225 Observer* observer) {
226 auto it = observers_.lower_bound(required_insert_count);
227 while (it != observers_.end() && it->first == required_insert_count) {
228 if (it->second == observer) {
229 observers_.erase(it);
230 return;
231 }
232 ++it;
233 }
234
235 // |observer| must have been registered.
236 QUICHE_NOTREACHED();
237 }
238
239 } // namespace quic
240