blob: 24edc98869177a54fe057ab4d673478c4dda078c [file] [log] [blame]
QUICHE teama6ef0a62019-03-07 20:34:33 -05001// 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
QUICHE team5be974e2020-12-29 18:35:24 -05005#include "quic/core/qpack/qpack_header_table.h"
QUICHE teama6ef0a62019-03-07 20:34:33 -05006
vasilvv7cac7b02020-10-08 12:32:10 -07007#include "absl/strings/string_view.h"
QUICHE team5be974e2020-12-29 18:35:24 -05008#include "quic/core/qpack/qpack_static_table.h"
9#include "quic/platform/api/quic_logging.h"
QUICHE teama6ef0a62019-03-07 20:34:33 -050010
11namespace quic {
12
QUICHE teama6ef0a62019-03-07 20:34:33 -050013QpackHeaderTable::QpackHeaderTable()
14 : static_entries_(ObtainQpackStaticTable().GetStaticEntries()),
15 static_index_(ObtainQpackStaticTable().GetStaticIndex()),
16 static_name_index_(ObtainQpackStaticTable().GetStaticNameIndex()),
17 dynamic_table_size_(0),
18 dynamic_table_capacity_(0),
19 maximum_dynamic_table_capacity_(0),
20 max_entries_(0),
bnc20df1af2019-11-12 10:46:20 -080021 dropped_entry_count_(0),
22 dynamic_table_entry_referenced_(false) {}
QUICHE teama6ef0a62019-03-07 20:34:33 -050023
bnc45a573a2019-10-04 09:30:02 -070024QpackHeaderTable::~QpackHeaderTable() {
25 for (auto& entry : observers_) {
26 entry.second->Cancel();
27 }
28}
QUICHE teama6ef0a62019-03-07 20:34:33 -050029
30const QpackEntry* QpackHeaderTable::LookupEntry(bool is_static,
31 uint64_t index) const {
32 if (is_static) {
33 if (index >= static_entries_.size()) {
34 return nullptr;
35 }
36
37 return &static_entries_[index];
38 }
39
40 if (index < dropped_entry_count_) {
41 return nullptr;
42 }
43
44 index -= dropped_entry_count_;
45
46 if (index >= dynamic_entries_.size()) {
47 return nullptr;
48 }
49
50 return &dynamic_entries_[index];
51}
52
53QpackHeaderTable::MatchType QpackHeaderTable::FindHeaderField(
vasilvv7cac7b02020-10-08 12:32:10 -070054 absl::string_view name,
55 absl::string_view value,
QUICHE teama6ef0a62019-03-07 20:34:33 -050056 bool* is_static,
57 uint64_t* index) const {
58 QpackEntry query(name, value);
59
60 // Look for exact match in static table.
61 auto index_it = static_index_.find(&query);
62 if (index_it != static_index_.end()) {
63 DCHECK((*index_it)->IsStatic());
64 *index = (*index_it)->InsertionIndex();
65 *is_static = true;
66 return MatchType::kNameAndValue;
67 }
68
69 // Look for exact match in dynamic table.
70 index_it = dynamic_index_.find(&query);
71 if (index_it != dynamic_index_.end()) {
72 DCHECK(!(*index_it)->IsStatic());
73 *index = (*index_it)->InsertionIndex();
74 *is_static = false;
75 return MatchType::kNameAndValue;
76 }
77
78 // Look for name match in static table.
79 auto name_index_it = static_name_index_.find(name);
80 if (name_index_it != static_name_index_.end()) {
81 DCHECK(name_index_it->second->IsStatic());
82 *index = name_index_it->second->InsertionIndex();
83 *is_static = true;
84 return MatchType::kName;
85 }
86
87 // Look for name match in dynamic table.
88 name_index_it = dynamic_name_index_.find(name);
89 if (name_index_it != dynamic_name_index_.end()) {
90 DCHECK(!name_index_it->second->IsStatic());
91 *index = name_index_it->second->InsertionIndex();
92 *is_static = false;
93 return MatchType::kName;
94 }
95
96 return MatchType::kNoMatch;
97}
98
vasilvv7cac7b02020-10-08 12:32:10 -070099const QpackEntry* QpackHeaderTable::InsertEntry(absl::string_view name,
100 absl::string_view value) {
bncb4025172019-07-12 18:09:59 -0700101 const uint64_t entry_size = QpackEntry::Size(name, value);
QUICHE teama6ef0a62019-03-07 20:34:33 -0500102 if (entry_size > dynamic_table_capacity_) {
103 return nullptr;
104 }
105
106 const uint64_t index = dropped_entry_count_ + dynamic_entries_.size();
107 dynamic_entries_.push_back({name, value, /* is_static = */ false, index});
108 QpackEntry* const new_entry = &dynamic_entries_.back();
109
110 // Evict entries after inserting the new entry instead of before
111 // in order to avoid invalidating |name| and |value|.
112 dynamic_table_size_ += entry_size;
113 EvictDownToCurrentCapacity();
114
115 auto index_result = dynamic_index_.insert(new_entry);
116 if (!index_result.second) {
117 // An entry with the same name and value already exists. It needs to be
118 // replaced, because |dynamic_index_| tracks the most recent entry for a
119 // given name and value.
120 DCHECK_GT(new_entry->InsertionIndex(),
121 (*index_result.first)->InsertionIndex());
122 dynamic_index_.erase(index_result.first);
123 auto result = dynamic_index_.insert(new_entry);
124 CHECK(result.second);
125 }
126
127 auto name_result = dynamic_name_index_.insert({new_entry->name(), new_entry});
128 if (!name_result.second) {
129 // An entry with the same name already exists. It needs to be replaced,
130 // because |dynamic_name_index_| tracks the most recent entry for a given
131 // name.
132 DCHECK_GT(new_entry->InsertionIndex(),
133 name_result.first->second->InsertionIndex());
134 dynamic_name_index_.erase(name_result.first);
135 auto result = dynamic_name_index_.insert({new_entry->name(), new_entry});
136 CHECK(result.second);
137 }
138
bnc7a1c21c2019-07-08 19:09:50 -0700139 // Notify and deregister observers whose threshold is met, if any.
bncbb40c7f2019-10-02 17:46:57 -0700140 while (!observers_.empty()) {
141 auto it = observers_.begin();
142 if (it->first > inserted_entry_count()) {
143 break;
144 }
145 Observer* observer = it->second;
146 observers_.erase(it);
bnc7a1c21c2019-07-08 19:09:50 -0700147 observer->OnInsertCountReachedThreshold();
148 }
149
QUICHE teama6ef0a62019-03-07 20:34:33 -0500150 return new_entry;
151}
152
bnc687c6e72019-07-29 10:57:40 -0700153uint64_t QpackHeaderTable::MaxInsertSizeWithoutEvictingGivenEntry(
154 uint64_t index) const {
155 DCHECK_LE(dropped_entry_count_, index);
156
157 if (index > inserted_entry_count()) {
158 // All entries are allowed to be evicted.
159 return dynamic_table_capacity_;
160 }
161
162 // Initialize to current available capacity.
163 uint64_t max_insert_size = dynamic_table_capacity_ - dynamic_table_size_;
164
165 for (const auto& entry : dynamic_entries_) {
166 if (entry.InsertionIndex() >= index) {
167 break;
168 }
169 max_insert_size += entry.Size();
170 }
171
172 return max_insert_size;
173}
174
QUICHE teama6ef0a62019-03-07 20:34:33 -0500175bool QpackHeaderTable::SetDynamicTableCapacity(uint64_t capacity) {
176 if (capacity > maximum_dynamic_table_capacity_) {
177 return false;
178 }
179
180 dynamic_table_capacity_ = capacity;
181 EvictDownToCurrentCapacity();
182
183 DCHECK_LE(dynamic_table_size_, dynamic_table_capacity_);
184
185 return true;
186}
187
renjietang033ca772020-06-12 10:28:22 -0700188bool QpackHeaderTable::SetMaximumDynamicTableCapacity(
QUICHE teama6ef0a62019-03-07 20:34:33 -0500189 uint64_t maximum_dynamic_table_capacity) {
renjietang033ca772020-06-12 10:28:22 -0700190 if (maximum_dynamic_table_capacity_ == 0) {
191 maximum_dynamic_table_capacity_ = maximum_dynamic_table_capacity;
192 max_entries_ = maximum_dynamic_table_capacity / 32;
193 return true;
194 }
195 // If the value is already set, it should not be changed.
196 return maximum_dynamic_table_capacity == maximum_dynamic_table_capacity_;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500197}
198
bncbb40c7f2019-10-02 17:46:57 -0700199void QpackHeaderTable::RegisterObserver(uint64_t required_insert_count,
200 Observer* observer) {
bnc7a1c21c2019-07-08 19:09:50 -0700201 DCHECK_GT(required_insert_count, 0u);
bncbb40c7f2019-10-02 17:46:57 -0700202 observers_.insert({required_insert_count, observer});
bnc7a1c21c2019-07-08 19:09:50 -0700203}
204
bnc2f2b7422019-10-02 18:10:43 -0700205void QpackHeaderTable::UnregisterObserver(uint64_t required_insert_count,
206 Observer* observer) {
207 auto it = observers_.lower_bound(required_insert_count);
208 while (it != observers_.end() && it->first == required_insert_count) {
209 if (it->second == observer) {
210 observers_.erase(it);
211 return;
212 }
213 ++it;
214 }
215
216 // |observer| must have been registered.
217 QUIC_NOTREACHED();
218}
219
bncb34a7ec2019-07-25 08:53:44 -0700220uint64_t QpackHeaderTable::draining_index(float draining_fraction) const {
221 DCHECK_LE(0.0, draining_fraction);
222 DCHECK_LE(draining_fraction, 1.0);
223
224 const uint64_t required_space = draining_fraction * dynamic_table_capacity_;
225 uint64_t space_above_draining_index =
226 dynamic_table_capacity_ - dynamic_table_size_;
227
228 if (dynamic_entries_.empty() ||
229 space_above_draining_index >= required_space) {
230 return dropped_entry_count_;
231 }
232
233 auto it = dynamic_entries_.begin();
234 while (space_above_draining_index < required_space) {
235 space_above_draining_index += it->Size();
236 ++it;
237 if (it == dynamic_entries_.end()) {
238 return inserted_entry_count();
239 }
240 }
241
242 return it->InsertionIndex();
243}
244
QUICHE teama6ef0a62019-03-07 20:34:33 -0500245void QpackHeaderTable::EvictDownToCurrentCapacity() {
246 while (dynamic_table_size_ > dynamic_table_capacity_) {
247 DCHECK(!dynamic_entries_.empty());
248
249 QpackEntry* const entry = &dynamic_entries_.front();
bncb4025172019-07-12 18:09:59 -0700250 const uint64_t entry_size = entry->Size();
QUICHE teama6ef0a62019-03-07 20:34:33 -0500251
252 DCHECK_GE(dynamic_table_size_, entry_size);
253 dynamic_table_size_ -= entry_size;
254
255 auto index_it = dynamic_index_.find(entry);
256 // Remove |dynamic_index_| entry only if it points to the same
257 // QpackEntry in |dynamic_entries_|. Note that |dynamic_index_| has a
258 // comparison function that only considers name and value, not actual
259 // QpackEntry* address, so find() can return a different entry if name and
260 // value match.
261 if (index_it != dynamic_index_.end() && *index_it == entry) {
262 dynamic_index_.erase(index_it);
263 }
264
265 auto name_it = dynamic_name_index_.find(entry->name());
266 // Remove |dynamic_name_index_| entry only if it points to the same
267 // QpackEntry in |dynamic_entries_|.
268 if (name_it != dynamic_name_index_.end() && name_it->second == entry) {
269 dynamic_name_index_.erase(name_it);
270 }
271
272 dynamic_entries_.pop_front();
273 ++dropped_entry_count_;
274 }
275}
276
277} // namespace quic