Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(230)

Side by Side Diff: net/spdy/spdy_header_block.cc

Issue 2832973003: Split net/spdy into core and chromium subdirectories. (Closed)
Patch Set: Fix some more build rules. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/spdy/spdy_header_block.h ('k') | net/spdy/spdy_header_block_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012 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/spdy_header_block.h"
6
7 #include <string.h>
8
9 #include <algorithm>
10 #include <utility>
11
12 #include "base/logging.h"
13 #include "base/macros.h"
14 #include "base/values.h"
15 #include "net/base/arena.h"
16 #include "net/http/http_log_util.h"
17 #include "net/log/net_log_capture_mode.h"
18 #include "net/spdy/platform/api/spdy_estimate_memory_usage.h"
19 #include "net/spdy/platform/api/spdy_string_utils.h"
20
21 namespace net {
22 namespace {
23
24 // By default, linked_hash_map's internal map allocates space for 100 map
25 // buckets on construction, which is larger than necessary. Standard library
26 // unordered map implementations use a list of prime numbers to set the bucket
27 // count for a particular capacity. |kInitialMapBuckets| is chosen to reduce
28 // memory usage for small header blocks, at the cost of having to rehash for
29 // large header blocks.
30 const size_t kInitialMapBuckets = 11;
31
32 // SpdyHeaderBlock::Storage allocates blocks of this size by default.
33 const size_t kDefaultStorageBlockSize = 2048;
34
35 const char kCookieKey[] = "cookie";
36 const char kNullSeparator = 0;
37
38 SpdyStringPiece SeparatorForKey(SpdyStringPiece key) {
39 if (key == kCookieKey) {
40 static SpdyStringPiece cookie_separator = "; ";
41 return cookie_separator;
42 } else {
43 return SpdyStringPiece(&kNullSeparator, 1);
44 }
45 }
46
47 } // namespace
48
49 // This class provides a backing store for SpdyStringPieces. It previously used
50 // custom allocation logic, but now uses an UnsafeArena instead. It has the
51 // property that SpdyStringPieces that refer to data in Storage are never
52 // invalidated until the Storage is deleted or Clear() is called.
53 //
54 // Write operations always append to the last block. If there is not enough
55 // space to perform the write, a new block is allocated, and any unused space
56 // is wasted.
57 class SpdyHeaderBlock::Storage {
58 public:
59 Storage() : arena_(kDefaultStorageBlockSize) {}
60 ~Storage() { Clear(); }
61
62 SpdyStringPiece Write(const SpdyStringPiece s) {
63 return SpdyStringPiece(arena_.Memdup(s.data(), s.size()), s.size());
64 }
65
66 // If |s| points to the most recent allocation from arena_, the arena will
67 // reclaim the memory. Otherwise, this method is a no-op.
68 void Rewind(const SpdyStringPiece s) {
69 arena_.Free(const_cast<char*>(s.data()), s.size());
70 }
71
72 void Clear() { arena_.Reset(); }
73
74 // Given a list of fragments and a separator, writes the fragments joined by
75 // the separator to a contiguous region of memory. Returns a SpdyStringPiece
76 // pointing to the region of memory.
77 SpdyStringPiece WriteFragments(const std::vector<SpdyStringPiece>& fragments,
78 SpdyStringPiece separator) {
79 if (fragments.empty()) {
80 return SpdyStringPiece();
81 }
82 size_t total_size = separator.size() * (fragments.size() - 1);
83 for (const auto fragment : fragments) {
84 total_size += fragment.size();
85 }
86 char* dst = arena_.Alloc(total_size);
87 size_t written = Join(dst, fragments, separator);
88 DCHECK_EQ(written, total_size);
89 return SpdyStringPiece(dst, total_size);
90 }
91
92 size_t bytes_allocated() const { return arena_.status().bytes_allocated(); }
93
94 // TODO(xunjieli): https://crbug.com/669108. Merge this with bytes_allocated()
95 size_t EstimateMemoryUsage() const {
96 return arena_.status().bytes_allocated();
97 }
98
99 private:
100 UnsafeArena arena_;
101
102 DISALLOW_COPY_AND_ASSIGN(Storage);
103 };
104
105 SpdyHeaderBlock::HeaderValue::HeaderValue(Storage* storage,
106 SpdyStringPiece key,
107 SpdyStringPiece initial_value)
108 : storage_(storage), fragments_({initial_value}), pair_({key, {}}) {}
109
110 SpdyHeaderBlock::HeaderValue::HeaderValue(HeaderValue&& other)
111 : storage_(other.storage_),
112 fragments_(std::move(other.fragments_)),
113 pair_(std::move(other.pair_)) {}
114
115 SpdyHeaderBlock::HeaderValue& SpdyHeaderBlock::HeaderValue::operator=(
116 HeaderValue&& other) {
117 storage_ = other.storage_;
118 fragments_ = std::move(other.fragments_);
119 pair_ = std::move(other.pair_);
120 return *this;
121 }
122
123 SpdyHeaderBlock::HeaderValue::~HeaderValue() {}
124
125 SpdyStringPiece SpdyHeaderBlock::HeaderValue::ConsolidatedValue() const {
126 if (fragments_.empty()) {
127 return SpdyStringPiece();
128 }
129 if (fragments_.size() > 1) {
130 fragments_ = {
131 storage_->WriteFragments(fragments_, SeparatorForKey(pair_.first))};
132 }
133 return fragments_[0];
134 }
135
136 void SpdyHeaderBlock::HeaderValue::Append(SpdyStringPiece fragment) {
137 fragments_.push_back(fragment);
138 }
139
140 const std::pair<SpdyStringPiece, SpdyStringPiece>&
141 SpdyHeaderBlock::HeaderValue::as_pair() const {
142 pair_.second = ConsolidatedValue();
143 return pair_;
144 }
145
146 SpdyHeaderBlock::iterator::iterator(MapType::const_iterator it) : it_(it) {}
147
148 SpdyHeaderBlock::iterator::iterator(const iterator& other) : it_(other.it_) {}
149
150 SpdyHeaderBlock::iterator::~iterator() {}
151
152 SpdyHeaderBlock::ValueProxy::ValueProxy(
153 SpdyHeaderBlock::MapType* block,
154 SpdyHeaderBlock::Storage* storage,
155 SpdyHeaderBlock::MapType::iterator lookup_result,
156 const SpdyStringPiece key)
157 : block_(block),
158 storage_(storage),
159 lookup_result_(lookup_result),
160 key_(key),
161 valid_(true) {}
162
163 SpdyHeaderBlock::ValueProxy::ValueProxy(ValueProxy&& other)
164 : block_(other.block_),
165 storage_(other.storage_),
166 lookup_result_(other.lookup_result_),
167 key_(other.key_),
168 valid_(true) {
169 other.valid_ = false;
170 }
171
172 SpdyHeaderBlock::ValueProxy& SpdyHeaderBlock::ValueProxy::operator=(
173 SpdyHeaderBlock::ValueProxy&& other) {
174 block_ = other.block_;
175 storage_ = other.storage_;
176 lookup_result_ = other.lookup_result_;
177 key_ = other.key_;
178 valid_ = true;
179 other.valid_ = false;
180 return *this;
181 }
182
183 SpdyHeaderBlock::ValueProxy::~ValueProxy() {
184 // If the ValueProxy is destroyed while lookup_result_ == block_->end(),
185 // the assignment operator was never used, and the block's Storage can
186 // reclaim the memory used by the key. This makes lookup-only access to
187 // SpdyHeaderBlock through operator[] memory-neutral.
188 if (valid_ && lookup_result_ == block_->end()) {
189 storage_->Rewind(key_);
190 }
191 }
192
193 SpdyHeaderBlock::ValueProxy& SpdyHeaderBlock::ValueProxy::operator=(
194 const SpdyStringPiece value) {
195 if (lookup_result_ == block_->end()) {
196 DVLOG(1) << "Inserting: (" << key_ << ", " << value << ")";
197 lookup_result_ =
198 block_
199 ->emplace(std::make_pair(
200 key_, HeaderValue(storage_, key_, storage_->Write(value))))
201 .first;
202 } else {
203 DVLOG(1) << "Updating key: " << key_ << " with value: " << value;
204 lookup_result_->second =
205 HeaderValue(storage_, key_, storage_->Write(value));
206 }
207 return *this;
208 }
209
210 SpdyString SpdyHeaderBlock::ValueProxy::as_string() const {
211 if (lookup_result_ == block_->end()) {
212 return "";
213 } else {
214 return SpdyString(lookup_result_->second.value());
215 }
216 }
217
218 SpdyHeaderBlock::SpdyHeaderBlock() : block_(kInitialMapBuckets) {}
219
220 SpdyHeaderBlock::SpdyHeaderBlock(SpdyHeaderBlock&& other) = default;
221
222 SpdyHeaderBlock::~SpdyHeaderBlock() {}
223
224 SpdyHeaderBlock& SpdyHeaderBlock::operator=(SpdyHeaderBlock&& other) {
225 block_.swap(other.block_);
226 storage_.swap(other.storage_);
227 return *this;
228 }
229
230 SpdyHeaderBlock SpdyHeaderBlock::Clone() const {
231 SpdyHeaderBlock copy;
232 for (const auto& p : *this) {
233 copy.AppendHeader(p.first, p.second);
234 }
235 return copy;
236 }
237
238 bool SpdyHeaderBlock::operator==(const SpdyHeaderBlock& other) const {
239 return size() == other.size() && std::equal(begin(), end(), other.begin());
240 }
241
242 bool SpdyHeaderBlock::operator!=(const SpdyHeaderBlock& other) const {
243 return !(operator==(other));
244 }
245
246 SpdyString SpdyHeaderBlock::DebugString() const {
247 if (empty()) {
248 return "{}";
249 }
250
251 SpdyString output = "\n{\n";
252 for (auto it = begin(); it != end(); ++it) {
253 SpdyStrAppend(&output, " ", it->first, " ", it->second, "\n");
254 }
255 SpdyStrAppend(&output, "}\n");
256 return output;
257 }
258
259 void SpdyHeaderBlock::clear() {
260 block_.clear();
261 storage_.reset();
262 }
263
264 void SpdyHeaderBlock::insert(const SpdyHeaderBlock::value_type& value) {
265 // TODO(birenroy): Write new value in place of old value, if it fits.
266 auto iter = block_.find(value.first);
267 if (iter == block_.end()) {
268 DVLOG(1) << "Inserting: (" << value.first << ", " << value.second << ")";
269 AppendHeader(value.first, value.second);
270 } else {
271 DVLOG(1) << "Updating key: " << iter->first
272 << " with value: " << value.second;
273 auto* storage = GetStorage();
274 iter->second =
275 HeaderValue(storage, iter->first, storage->Write(value.second));
276 }
277 }
278
279 SpdyHeaderBlock::ValueProxy SpdyHeaderBlock::operator[](
280 const SpdyStringPiece key) {
281 DVLOG(2) << "Operator[] saw key: " << key;
282 SpdyStringPiece out_key;
283 auto iter = block_.find(key);
284 if (iter == block_.end()) {
285 // We write the key first, to assure that the ValueProxy has a
286 // reference to a valid SpdyStringPiece in its operator=.
287 out_key = GetStorage()->Write(key);
288 DVLOG(2) << "Key written as: " << std::hex
289 << static_cast<const void*>(key.data()) << ", " << std::dec
290 << key.size();
291 } else {
292 out_key = iter->first;
293 }
294 return ValueProxy(&block_, GetStorage(), iter, out_key);
295 }
296
297 void SpdyHeaderBlock::AppendValueOrAddHeader(const SpdyStringPiece key,
298 const SpdyStringPiece value) {
299 auto iter = block_.find(key);
300 if (iter == block_.end()) {
301 DVLOG(1) << "Inserting: (" << key << ", " << value << ")";
302 AppendHeader(key, value);
303 return;
304 }
305 DVLOG(1) << "Updating key: " << iter->first << "; appending value: " << value;
306 iter->second.Append(GetStorage()->Write(value));
307 }
308
309 size_t SpdyHeaderBlock::EstimateMemoryUsage() const {
310 // TODO(xunjieli): https://crbug.com/669108. Also include |block_| when EMU()
311 // supports linked_hash_map.
312 return SpdyEstimateMemoryUsage(storage_);
313 }
314
315 void SpdyHeaderBlock::AppendHeader(const SpdyStringPiece key,
316 const SpdyStringPiece value) {
317 auto* storage = GetStorage();
318 auto backed_key = storage->Write(key);
319 block_.emplace(std::make_pair(
320 backed_key, HeaderValue(storage, backed_key, storage->Write(value))));
321 }
322
323 SpdyHeaderBlock::Storage* SpdyHeaderBlock::GetStorage() {
324 if (!storage_) {
325 storage_.reset(new Storage);
326 }
327 return storage_.get();
328 }
329
330 std::unique_ptr<base::Value> SpdyHeaderBlockNetLogCallback(
331 const SpdyHeaderBlock* headers,
332 NetLogCaptureMode capture_mode) {
333 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
334 base::DictionaryValue* headers_dict = new base::DictionaryValue();
335 for (SpdyHeaderBlock::const_iterator it = headers->begin();
336 it != headers->end(); ++it) {
337 headers_dict->SetWithoutPathExpansion(
338 it->first.as_string(),
339 new base::Value(ElideHeaderValueForNetLog(
340 capture_mode, it->first.as_string(), it->second.as_string())));
341 }
342 dict->Set("headers", headers_dict);
343 return std::move(dict);
344 }
345
346 bool SpdyHeaderBlockFromNetLogParam(
347 const base::Value* event_param,
348 SpdyHeaderBlock* headers) {
349 headers->clear();
350
351 const base::DictionaryValue* dict = NULL;
352 const base::DictionaryValue* header_dict = NULL;
353
354 if (!event_param ||
355 !event_param->GetAsDictionary(&dict) ||
356 !dict->GetDictionary("headers", &header_dict)) {
357 return false;
358 }
359
360 for (base::DictionaryValue::Iterator it(*header_dict); !it.IsAtEnd();
361 it.Advance()) {
362 SpdyString value;
363 if (!it.value().GetAsString(&value)) {
364 headers->clear();
365 return false;
366 }
367 (*headers)[it.key()] = value;
368 }
369 return true;
370 }
371
372 size_t SpdyHeaderBlock::bytes_allocated() const {
373 if (storage_ == nullptr) {
374 return 0;
375 } else {
376 return storage_->bytes_allocated();
377 }
378 }
379
380 size_t Join(char* dst,
381 const std::vector<SpdyStringPiece>& fragments,
382 SpdyStringPiece separator) {
383 if (fragments.empty()) {
384 return 0;
385 }
386 auto* original_dst = dst;
387 auto it = fragments.begin();
388 memcpy(dst, it->data(), it->size());
389 dst += it->size();
390 for (++it; it != fragments.end(); ++it) {
391 memcpy(dst, separator.data(), separator.size());
392 dst += separator.size();
393 memcpy(dst, it->data(), it->size());
394 dst += it->size();
395 }
396 return dst - original_dst;
397 }
398
399 } // namespace net
OLDNEW
« no previous file with comments | « net/spdy/spdy_header_block.h ('k') | net/spdy/spdy_header_block_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698