xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/core/qpack/qpack_header_table.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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