OLD | NEW |
| (Empty) |
1 // 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 | |
5 #include "net/spdy/hpack/hpack_header_table.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "base/logging.h" | |
10 #include "net/spdy/hpack/hpack_constants.h" | |
11 #include "net/spdy/hpack/hpack_static_table.h" | |
12 #include "net/spdy/platform/api/spdy_estimate_memory_usage.h" | |
13 #include "net/spdy/spdy_flags.h" | |
14 | |
15 namespace net { | |
16 | |
17 size_t HpackHeaderTable::EntryHasher::operator()( | |
18 const HpackEntry* entry) const { | |
19 return base::StringPieceHash()(entry->name()) ^ | |
20 base::StringPieceHash()(entry->value()); | |
21 } | |
22 | |
23 bool HpackHeaderTable::EntriesEq::operator()(const HpackEntry* lhs, | |
24 const HpackEntry* rhs) const { | |
25 if (lhs == nullptr) { | |
26 return rhs == nullptr; | |
27 } | |
28 if (rhs == nullptr) { | |
29 return false; | |
30 } | |
31 return lhs->name() == rhs->name() && lhs->value() == rhs->value(); | |
32 } | |
33 | |
34 HpackHeaderTable::HpackHeaderTable() | |
35 : static_entries_(ObtainHpackStaticTable().GetStaticEntries()), | |
36 static_index_(ObtainHpackStaticTable().GetStaticIndex()), | |
37 static_name_index_(ObtainHpackStaticTable().GetStaticNameIndex()), | |
38 settings_size_bound_(kDefaultHeaderTableSizeSetting), | |
39 size_(0), | |
40 max_size_(kDefaultHeaderTableSizeSetting), | |
41 total_insertions_(static_entries_.size()) {} | |
42 | |
43 HpackHeaderTable::~HpackHeaderTable() {} | |
44 | |
45 const HpackEntry* HpackHeaderTable::GetByIndex(size_t index) { | |
46 if (index == 0) { | |
47 return NULL; | |
48 } | |
49 index -= 1; | |
50 if (index < static_entries_.size()) { | |
51 return &static_entries_[index]; | |
52 } | |
53 index -= static_entries_.size(); | |
54 if (index < dynamic_entries_.size()) { | |
55 const HpackEntry* result = &dynamic_entries_[index]; | |
56 if (debug_visitor_ != nullptr) { | |
57 debug_visitor_->OnUseEntry(*result); | |
58 } | |
59 return result; | |
60 } | |
61 return NULL; | |
62 } | |
63 | |
64 const HpackEntry* HpackHeaderTable::GetByName(SpdyStringPiece name) { | |
65 { | |
66 NameToEntryMap::const_iterator it = static_name_index_.find(name); | |
67 if (it != static_name_index_.end()) { | |
68 return it->second; | |
69 } | |
70 } | |
71 { | |
72 NameToEntryMap::const_iterator it = dynamic_name_index_.find(name); | |
73 if (it != dynamic_name_index_.end()) { | |
74 const HpackEntry* result = it->second; | |
75 if (debug_visitor_ != nullptr) { | |
76 debug_visitor_->OnUseEntry(*result); | |
77 } | |
78 return result; | |
79 } | |
80 } | |
81 return NULL; | |
82 } | |
83 | |
84 const HpackEntry* HpackHeaderTable::GetByNameAndValue(SpdyStringPiece name, | |
85 SpdyStringPiece value) { | |
86 HpackEntry query(name, value); | |
87 { | |
88 UnorderedEntrySet::const_iterator it = static_index_.find(&query); | |
89 if (it != static_index_.end()) { | |
90 return *it; | |
91 } | |
92 } | |
93 { | |
94 UnorderedEntrySet::const_iterator it = dynamic_index_.find(&query); | |
95 if (it != dynamic_index_.end()) { | |
96 const HpackEntry* result = *it; | |
97 if (debug_visitor_ != nullptr) { | |
98 debug_visitor_->OnUseEntry(*result); | |
99 } | |
100 return result; | |
101 } | |
102 } | |
103 return NULL; | |
104 } | |
105 | |
106 size_t HpackHeaderTable::IndexOf(const HpackEntry* entry) const { | |
107 if (entry->IsLookup()) { | |
108 return 0; | |
109 } else if (entry->IsStatic()) { | |
110 return 1 + entry->InsertionIndex(); | |
111 } else { | |
112 return total_insertions_ - entry->InsertionIndex() + static_entries_.size(); | |
113 } | |
114 } | |
115 | |
116 void HpackHeaderTable::SetMaxSize(size_t max_size) { | |
117 CHECK_LE(max_size, settings_size_bound_); | |
118 | |
119 max_size_ = max_size; | |
120 if (size_ > max_size_) { | |
121 Evict(EvictionCountToReclaim(size_ - max_size_)); | |
122 CHECK_LE(size_, max_size_); | |
123 } | |
124 } | |
125 | |
126 void HpackHeaderTable::SetSettingsHeaderTableSize(size_t settings_size) { | |
127 settings_size_bound_ = settings_size; | |
128 SetMaxSize(settings_size_bound_); | |
129 } | |
130 | |
131 void HpackHeaderTable::EvictionSet(SpdyStringPiece name, | |
132 SpdyStringPiece value, | |
133 EntryTable::iterator* begin_out, | |
134 EntryTable::iterator* end_out) { | |
135 size_t eviction_count = EvictionCountForEntry(name, value); | |
136 *begin_out = dynamic_entries_.end() - eviction_count; | |
137 *end_out = dynamic_entries_.end(); | |
138 } | |
139 | |
140 size_t HpackHeaderTable::EvictionCountForEntry(SpdyStringPiece name, | |
141 SpdyStringPiece value) const { | |
142 size_t available_size = max_size_ - size_; | |
143 size_t entry_size = HpackEntry::Size(name, value); | |
144 | |
145 if (entry_size <= available_size) { | |
146 // No evictions are required. | |
147 return 0; | |
148 } | |
149 return EvictionCountToReclaim(entry_size - available_size); | |
150 } | |
151 | |
152 size_t HpackHeaderTable::EvictionCountToReclaim(size_t reclaim_size) const { | |
153 size_t count = 0; | |
154 for (EntryTable::const_reverse_iterator it = dynamic_entries_.rbegin(); | |
155 it != dynamic_entries_.rend() && reclaim_size != 0; ++it, ++count) { | |
156 reclaim_size -= std::min(reclaim_size, it->Size()); | |
157 } | |
158 return count; | |
159 } | |
160 | |
161 void HpackHeaderTable::Evict(size_t count) { | |
162 for (size_t i = 0; i != count; ++i) { | |
163 CHECK(!dynamic_entries_.empty()); | |
164 HpackEntry* entry = &dynamic_entries_.back(); | |
165 | |
166 size_ -= entry->Size(); | |
167 UnorderedEntrySet::iterator it = dynamic_index_.find(entry); | |
168 DCHECK(it != dynamic_index_.end()); | |
169 // Only remove an entry from the index if its insertion index matches; | |
170 // otherwise, the index refers to another entry with the same name and | |
171 // value. | |
172 if ((*it)->InsertionIndex() == entry->InsertionIndex()) { | |
173 dynamic_index_.erase(it); | |
174 } | |
175 NameToEntryMap::iterator name_it = dynamic_name_index_.find(entry->name()); | |
176 DCHECK(name_it != dynamic_name_index_.end()); | |
177 // Only remove an entry from the literal index if its insertion index | |
178 /// matches; otherwise, the index refers to another entry with the same | |
179 // name. | |
180 if (name_it->second->InsertionIndex() == entry->InsertionIndex()) { | |
181 dynamic_name_index_.erase(name_it); | |
182 } | |
183 dynamic_entries_.pop_back(); | |
184 } | |
185 } | |
186 | |
187 const HpackEntry* HpackHeaderTable::TryAddEntry(SpdyStringPiece name, | |
188 SpdyStringPiece value) { | |
189 Evict(EvictionCountForEntry(name, value)); | |
190 | |
191 size_t entry_size = HpackEntry::Size(name, value); | |
192 if (entry_size > (max_size_ - size_)) { | |
193 // Entire table has been emptied, but there's still insufficient room. | |
194 DCHECK(dynamic_entries_.empty()); | |
195 DCHECK_EQ(0u, size_); | |
196 return NULL; | |
197 } | |
198 dynamic_entries_.push_front(HpackEntry(name, value, | |
199 false, // is_static | |
200 total_insertions_)); | |
201 HpackEntry* new_entry = &dynamic_entries_.front(); | |
202 auto index_result = dynamic_index_.insert(new_entry); | |
203 if (!index_result.second) { | |
204 // An entry with the same name and value already exists in the dynamic | |
205 // index. We should replace it with the newly added entry. | |
206 DVLOG(1) << "Found existing entry: " | |
207 << (*index_result.first)->GetDebugString() | |
208 << " replacing with: " << new_entry->GetDebugString(); | |
209 DCHECK_GT(new_entry->InsertionIndex(), | |
210 (*index_result.first)->InsertionIndex()); | |
211 dynamic_index_.erase(index_result.first); | |
212 CHECK(dynamic_index_.insert(new_entry).second); | |
213 } | |
214 | |
215 auto name_result = | |
216 dynamic_name_index_.insert(std::make_pair(new_entry->name(), new_entry)); | |
217 if (!name_result.second) { | |
218 // An entry with the same name already exists in the dynamic index. We | |
219 // should replace it with the newly added entry. | |
220 DVLOG(1) << "Found existing entry: " | |
221 << name_result.first->second->GetDebugString() | |
222 << " replacing with: " << new_entry->GetDebugString(); | |
223 DCHECK_GT(new_entry->InsertionIndex(), | |
224 name_result.first->second->InsertionIndex()); | |
225 dynamic_name_index_.erase(name_result.first); | |
226 auto insert_result = dynamic_name_index_.insert( | |
227 std::make_pair(new_entry->name(), new_entry)); | |
228 CHECK(insert_result.second); | |
229 } | |
230 | |
231 size_ += entry_size; | |
232 ++total_insertions_; | |
233 if (debug_visitor_ != nullptr) { | |
234 // Call |debug_visitor_->OnNewEntry()| to get the current time. | |
235 HpackEntry& entry = dynamic_entries_.front(); | |
236 entry.set_time_added(debug_visitor_->OnNewEntry(entry)); | |
237 DVLOG(2) << "HpackHeaderTable::OnNewEntry: name=" << entry.name() | |
238 << ", value=" << entry.value() | |
239 << ", insert_index=" << entry.InsertionIndex() | |
240 << ", time_added=" << entry.time_added(); | |
241 } | |
242 | |
243 return &dynamic_entries_.front(); | |
244 } | |
245 | |
246 void HpackHeaderTable::DebugLogTableState() const { | |
247 DVLOG(2) << "Dynamic table:"; | |
248 for (EntryTable::const_iterator it = dynamic_entries_.begin(); | |
249 it != dynamic_entries_.end(); ++it) { | |
250 DVLOG(2) << " " << it->GetDebugString(); | |
251 } | |
252 DVLOG(2) << "Full Static Index:"; | |
253 for (auto* entry : static_index_) { | |
254 DVLOG(2) << " " << entry->GetDebugString(); | |
255 } | |
256 DVLOG(2) << "Full Static Name Index:"; | |
257 for (const auto it : static_name_index_) { | |
258 DVLOG(2) << " " << it.first << ": " << it.second->GetDebugString(); | |
259 } | |
260 DVLOG(2) << "Full Dynamic Index:"; | |
261 for (auto* entry : dynamic_index_) { | |
262 DVLOG(2) << " " << entry->GetDebugString(); | |
263 } | |
264 DVLOG(2) << "Full Dynamic Name Index:"; | |
265 for (const auto it : dynamic_name_index_) { | |
266 DVLOG(2) << " " << it.first << ": " << it.second->GetDebugString(); | |
267 } | |
268 } | |
269 | |
270 size_t HpackHeaderTable::EstimateMemoryUsage() const { | |
271 return SpdyEstimateMemoryUsage(dynamic_entries_) + | |
272 SpdyEstimateMemoryUsage(dynamic_index_) + | |
273 SpdyEstimateMemoryUsage(dynamic_name_index_); | |
274 } | |
275 | |
276 } // namespace net | |
OLD | NEW |