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

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

Issue 2611173004: Modify SpdyHeaderBlock's internals to consolidate header values only on first access. (Closed)
Patch Set: Rebase. Created 3 years, 11 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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/spdy/spdy_header_block.h" 5 #include "net/spdy/spdy_header_block.h"
6 6
7 #include <string.h> 7 #include <string.h>
8 8
9 #include <algorithm> 9 #include <algorithm>
10 #include <utility> 10 #include <utility>
11 11
12 #include "base/logging.h" 12 #include "base/logging.h"
13 #include "base/macros.h" 13 #include "base/macros.h"
14 #include "base/values.h" 14 #include "base/values.h"
15 #include "net/base/arena.h" 15 #include "net/base/arena.h"
16 #include "net/http/http_log_util.h" 16 #include "net/http/http_log_util.h"
17 #include "net/log/net_log_capture_mode.h" 17 #include "net/log/net_log_capture_mode.h"
18 18
19 using base::StringPiece; 19 using base::StringPiece;
20 using std::dec; 20 using std::dec;
21 using std::hex; 21 using std::hex;
22 using std::make_pair;
22 using std::max; 23 using std::max;
23 using std::min; 24 using std::min;
24 using std::string; 25 using std::string;
25 26
26 namespace net { 27 namespace net {
27 namespace { 28 namespace {
28 29
29 // SpdyHeaderBlock::Storage allocates blocks of this size by default. 30 // SpdyHeaderBlock::Storage allocates blocks of this size by default.
30 const size_t kDefaultStorageBlockSize = 2048; 31 const size_t kDefaultStorageBlockSize = 2048;
31 32
32 const char kCookieKey[] = "cookie"; 33 const char kCookieKey[] = "cookie";
34 const char kNullSeparator = 0;
35
36 StringPiece SeparatorForKey(StringPiece key) {
37 if (key == kCookieKey) {
38 static StringPiece cookie_separator = "; ";
39 return cookie_separator;
40 } else {
41 return StringPiece(&kNullSeparator, 1);
42 }
43 }
33 44
34 } // namespace 45 } // namespace
35 46
36 // This class provides a backing store for StringPieces. It previously used 47 // This class provides a backing store for StringPieces. It previously used
37 // custom allocation logic, but now uses an UnsafeArena instead. It has the 48 // custom allocation logic, but now uses an UnsafeArena instead. It has the
38 // property that StringPieces that refer to data in Storage are never 49 // property that StringPieces that refer to data in Storage are never
39 // invalidated until the Storage is deleted or Clear() is called. 50 // invalidated until the Storage is deleted or Clear() is called.
40 // 51 //
41 // Write operations always append to the last block. If there is not enough 52 // Write operations always append to the last block. If there is not enough
42 // space to perform the write, a new block is allocated, and any unused space 53 // space to perform the write, a new block is allocated, and any unused space
43 // is wasted. 54 // is wasted.
44 class SpdyHeaderBlock::Storage { 55 class SpdyHeaderBlock::Storage {
45 public: 56 public:
46 Storage() : arena_(kDefaultStorageBlockSize) {} 57 Storage() : arena_(kDefaultStorageBlockSize) {}
47 ~Storage() { Clear(); } 58 ~Storage() { Clear(); }
48 59
49 StringPiece Write(const StringPiece s) { 60 StringPiece Write(const StringPiece s) {
50 return StringPiece(arena_.Memdup(s.data(), s.size()), s.size()); 61 return StringPiece(arena_.Memdup(s.data(), s.size()), s.size());
51 } 62 }
52 63
53 // Given value, a string already in the arena, perform a realloc and append
54 // separator and more to the end of the value's new location. If value is the
55 // most recently added string (via Write), then UnsafeArena will not copy the
56 // existing value but instead will increase the space reserved for value.
57 StringPiece Realloc(StringPiece value,
58 StringPiece separator,
59 StringPiece more) {
60 size_t total_length = value.size() + separator.size() + more.size();
61 char* ptr = const_cast<char*>(value.data());
62 ptr = arena_.Realloc(ptr, value.size(), total_length);
63 StringPiece result(ptr, total_length);
64 ptr += value.size();
65 memcpy(ptr, separator.data(), separator.size());
66 ptr += separator.size();
67 memcpy(ptr, more.data(), more.size());
68 return result;
69 }
70
71 // If |s| points to the most recent allocation from arena_, the arena will 64 // If |s| points to the most recent allocation from arena_, the arena will
72 // reclaim the memory. Otherwise, this method is a no-op. 65 // reclaim the memory. Otherwise, this method is a no-op.
73 void Rewind(const StringPiece s) { 66 void Rewind(const StringPiece s) {
74 arena_.Free(const_cast<char*>(s.data()), s.size()); 67 arena_.Free(const_cast<char*>(s.data()), s.size());
75 } 68 }
76 69
77 void Clear() { arena_.Reset(); } 70 void Clear() { arena_.Reset(); }
78 71
72 // Given a list of fragments and a separator, writes the fragments joined by
73 // the separator to a contiguous region of memory. Returns a StringPiece
74 // pointing to the region of memory.
75 StringPiece WriteFragments(const std::vector<StringPiece>& fragments,
76 StringPiece separator) {
77 if (fragments.empty()) {
78 return StringPiece();
79 }
80 size_t total_size = separator.size() * (fragments.size() - 1);
81 for (const auto fragment : fragments) {
82 total_size += fragment.size();
83 }
84 char* dst = arena_.Alloc(total_size);
85 size_t written = Join(dst, fragments, separator);
86 DCHECK_EQ(written, total_size);
87 return StringPiece(dst, total_size);
88 }
89
90 size_t bytes_allocated() const { return arena_.status().bytes_allocated(); }
91
79 private: 92 private:
80 UnsafeArena arena_; 93 UnsafeArena arena_;
81 }; 94 };
82 95
96 SpdyHeaderBlock::HeaderValue::HeaderValue(Storage* storage,
97 StringPiece key,
98 StringPiece initial_value)
99 : storage_(storage), fragments_({initial_value}), pair_({key, {}}) {}
100
101 SpdyHeaderBlock::HeaderValue::HeaderValue(HeaderValue&& other)
102 : storage_(other.storage_),
103 fragments_(std::move(other.fragments_)),
104 pair_(std::move(other.pair_)) {}
105
106 SpdyHeaderBlock::HeaderValue& SpdyHeaderBlock::HeaderValue::operator=(
107 HeaderValue&& other) {
108 storage_ = other.storage_;
109 fragments_ = std::move(other.fragments_);
110 pair_ = std::move(other.pair_);
111 return *this;
112 }
113
114 SpdyHeaderBlock::HeaderValue::~HeaderValue() {}
115
116 StringPiece SpdyHeaderBlock::HeaderValue::ConsolidatedValue() const {
117 if (fragments_.empty()) {
118 return StringPiece();
119 }
120 if (fragments_.size() > 1) {
121 fragments_ = {
122 storage_->WriteFragments(fragments_, SeparatorForKey(pair_.first))};
123 }
124 return fragments_[0];
125 }
126
127 void SpdyHeaderBlock::HeaderValue::Append(StringPiece fragment) {
128 fragments_.push_back(fragment);
129 }
130
131 const std::pair<StringPiece, StringPiece>&
132 SpdyHeaderBlock::HeaderValue::as_pair() const {
133 pair_.second = ConsolidatedValue();
134 return pair_;
135 }
136
137 SpdyHeaderBlock::iterator::iterator(MapType::const_iterator it) : it_(it) {}
138
139 SpdyHeaderBlock::iterator::iterator(const iterator& other) : it_(other.it_) {}
140
141 SpdyHeaderBlock::iterator::~iterator() {}
142
83 SpdyHeaderBlock::ValueProxy::ValueProxy( 143 SpdyHeaderBlock::ValueProxy::ValueProxy(
84 SpdyHeaderBlock::MapType* block, 144 SpdyHeaderBlock::MapType* block,
85 SpdyHeaderBlock::Storage* storage, 145 SpdyHeaderBlock::Storage* storage,
86 SpdyHeaderBlock::MapType::iterator lookup_result, 146 SpdyHeaderBlock::MapType::iterator lookup_result,
87 const StringPiece key) 147 const StringPiece key)
88 : block_(block), 148 : block_(block),
89 storage_(storage), 149 storage_(storage),
90 lookup_result_(lookup_result), 150 lookup_result_(lookup_result),
91 key_(key), 151 key_(key),
92 valid_(true) { 152 valid_(true) {}
93 }
94 153
95 SpdyHeaderBlock::ValueProxy::ValueProxy(ValueProxy&& other) 154 SpdyHeaderBlock::ValueProxy::ValueProxy(ValueProxy&& other)
96 : block_(other.block_), 155 : block_(other.block_),
97 storage_(other.storage_), 156 storage_(other.storage_),
98 lookup_result_(other.lookup_result_), 157 lookup_result_(other.lookup_result_),
99 key_(other.key_), 158 key_(other.key_),
100 valid_(true) { 159 valid_(true) {
101 other.valid_ = false; 160 other.valid_ = false;
102 } 161 }
103 162
(...skipping 16 matching lines...) Expand all
120 if (valid_ && lookup_result_ == block_->end()) { 179 if (valid_ && lookup_result_ == block_->end()) {
121 storage_->Rewind(key_); 180 storage_->Rewind(key_);
122 } 181 }
123 } 182 }
124 183
125 SpdyHeaderBlock::ValueProxy& SpdyHeaderBlock::ValueProxy::operator=( 184 SpdyHeaderBlock::ValueProxy& SpdyHeaderBlock::ValueProxy::operator=(
126 const StringPiece value) { 185 const StringPiece value) {
127 if (lookup_result_ == block_->end()) { 186 if (lookup_result_ == block_->end()) {
128 DVLOG(1) << "Inserting: (" << key_ << ", " << value << ")"; 187 DVLOG(1) << "Inserting: (" << key_ << ", " << value << ")";
129 lookup_result_ = 188 lookup_result_ =
130 block_->insert(std::make_pair(key_, storage_->Write(value))).first; 189 block_
190 ->emplace(make_pair(
191 key_, HeaderValue(storage_, key_, storage_->Write(value))))
192 .first;
131 } else { 193 } else {
132 DVLOG(1) << "Updating key: " << key_ << " with value: " << value; 194 DVLOG(1) << "Updating key: " << key_ << " with value: " << value;
133 lookup_result_->second = storage_->Write(value); 195 lookup_result_->second =
196 HeaderValue(storage_, key_, storage_->Write(value));
134 } 197 }
135 return *this; 198 return *this;
136 } 199 }
137 200
138 string SpdyHeaderBlock::ValueProxy::as_string() const { 201 string SpdyHeaderBlock::ValueProxy::as_string() const {
139 if (lookup_result_ == block_->end()) { 202 if (lookup_result_ == block_->end()) {
140 return ""; 203 return "";
141 } else { 204 } else {
142 return lookup_result_->second.as_string(); 205 return lookup_result_->second.value().as_string();
143 } 206 }
144 } 207 }
145 208
146 SpdyHeaderBlock::SpdyHeaderBlock() {} 209 SpdyHeaderBlock::SpdyHeaderBlock() {}
147 210
148 SpdyHeaderBlock::SpdyHeaderBlock(SpdyHeaderBlock&& other) { 211 SpdyHeaderBlock::SpdyHeaderBlock(SpdyHeaderBlock&& other) {
149 block_.swap(other.block_); 212 block_.swap(other.block_);
150 storage_.swap(other.storage_); 213 storage_.swap(other.storage_);
151 } 214 }
152 215
153 SpdyHeaderBlock::~SpdyHeaderBlock() {} 216 SpdyHeaderBlock::~SpdyHeaderBlock() {}
154 217
155 SpdyHeaderBlock& SpdyHeaderBlock::operator=(SpdyHeaderBlock&& other) { 218 SpdyHeaderBlock& SpdyHeaderBlock::operator=(SpdyHeaderBlock&& other) {
156 block_.swap(other.block_); 219 block_.swap(other.block_);
157 storage_.swap(other.storage_); 220 storage_.swap(other.storage_);
158 return *this; 221 return *this;
159 } 222 }
160 223
161 SpdyHeaderBlock SpdyHeaderBlock::Clone() const { 224 SpdyHeaderBlock SpdyHeaderBlock::Clone() const {
162 SpdyHeaderBlock copy; 225 SpdyHeaderBlock copy;
163 for (auto iter : *this) { 226 for (const auto& p : *this) {
164 copy.AppendHeader(iter.first, iter.second); 227 copy.AppendHeader(p.first, p.second);
165 } 228 }
166 return copy; 229 return copy;
167 } 230 }
168 231
169 bool SpdyHeaderBlock::operator==(const SpdyHeaderBlock& other) const { 232 bool SpdyHeaderBlock::operator==(const SpdyHeaderBlock& other) const {
170 return size() == other.size() && std::equal(begin(), end(), other.begin()); 233 return size() == other.size() && std::equal(begin(), end(), other.begin());
171 } 234 }
172 235
173 bool SpdyHeaderBlock::operator!=(const SpdyHeaderBlock& other) const { 236 bool SpdyHeaderBlock::operator!=(const SpdyHeaderBlock& other) const {
174 return !(operator==(other)); 237 return !(operator==(other));
175 } 238 }
176 239
177 string SpdyHeaderBlock::DebugString() const { 240 string SpdyHeaderBlock::DebugString() const {
178 if (empty()) { 241 if (empty()) {
179 return "{}"; 242 return "{}";
180 } 243 }
181 string output = "\n{\n"; 244 string output = "\n{\n";
182 for (auto it = begin(); it != end(); ++it) { 245 for (auto it = begin(); it != end(); ++it) {
183 output += 246 output +=
184 " " + it->first.as_string() + ":" + it->second.as_string() + "\n"; 247 " " + it->first.as_string() + " " + it->second.as_string() + "\n";
185 } 248 }
186 output.append("}\n"); 249 output.append("}\n");
187 return output; 250 return output;
188 } 251 }
189 252
190 void SpdyHeaderBlock::clear() { 253 void SpdyHeaderBlock::clear() {
191 block_.clear(); 254 block_.clear();
192 storage_.reset(); 255 storage_.reset();
193 } 256 }
194 257
195 void SpdyHeaderBlock::insert( 258 void SpdyHeaderBlock::insert(const SpdyHeaderBlock::value_type& value) {
196 const SpdyHeaderBlock::MapType::value_type& value) {
197 // TODO(birenroy): Write new value in place of old value, if it fits. 259 // TODO(birenroy): Write new value in place of old value, if it fits.
198 auto iter = block_.find(value.first); 260 auto iter = block_.find(value.first);
199 if (iter == block_.end()) { 261 if (iter == block_.end()) {
200 DVLOG(1) << "Inserting: (" << value.first << ", " << value.second << ")"; 262 DVLOG(1) << "Inserting: (" << value.first << ", " << value.second << ")";
201 AppendHeader(value.first, value.second); 263 AppendHeader(value.first, value.second);
202 } else { 264 } else {
203 DVLOG(1) << "Updating key: " << iter->first 265 DVLOG(1) << "Updating key: " << iter->first
204 << " with value: " << value.second; 266 << " with value: " << value.second;
205 iter->second = GetStorage()->Write(value.second); 267 auto storage = GetStorage();
268 iter->second =
269 HeaderValue(storage, iter->first, storage->Write(value.second));
206 } 270 }
207 } 271 }
208 272
209 SpdyHeaderBlock::ValueProxy SpdyHeaderBlock::operator[](const StringPiece key) { 273 SpdyHeaderBlock::ValueProxy SpdyHeaderBlock::operator[](const StringPiece key) {
210 DVLOG(2) << "Operator[] saw key: " << key; 274 DVLOG(2) << "Operator[] saw key: " << key;
211 StringPiece out_key; 275 StringPiece out_key;
212 auto iter = block_.find(key); 276 auto iter = block_.find(key);
213 if (iter == block_.end()) { 277 if (iter == block_.end()) {
214 // We write the key first, to assure that the ValueProxy has a 278 // We write the key first, to assure that the ValueProxy has a
215 // reference to a valid StringPiece in its operator=. 279 // reference to a valid StringPiece in its operator=.
216 out_key = GetStorage()->Write(key); 280 out_key = GetStorage()->Write(key);
217 DVLOG(2) << "Key written as: " << std::hex 281 DVLOG(2) << "Key written as: " << std::hex
218 << static_cast<const void*>(key.data()) << ", " << std::dec 282 << static_cast<const void*>(key.data()) << ", " << std::dec
219 << key.size(); 283 << key.size();
220 } else { 284 } else {
221 out_key = iter->first; 285 out_key = iter->first;
222 } 286 }
223 return ValueProxy(&block_, GetStorage(), iter, out_key); 287 return ValueProxy(&block_, GetStorage(), iter, out_key);
224 } 288 }
225 289
226 void SpdyHeaderBlock::AppendValueOrAddHeader(const StringPiece key, 290 void SpdyHeaderBlock::AppendValueOrAddHeader(const StringPiece key,
227 const StringPiece value) { 291 const StringPiece value) {
228 auto iter = block_.find(key); 292 auto iter = block_.find(key);
229 if (iter == block_.end()) { 293 if (iter == block_.end()) {
230 DVLOG(1) << "Inserting: (" << key << ", " << value << ")"; 294 DVLOG(1) << "Inserting: (" << key << ", " << value << ")";
231 AppendHeader(key, value); 295 AppendHeader(key, value);
232 return; 296 return;
233 } 297 }
234 DVLOG(1) << "Updating key: " << iter->first << "; appending value: " << value; 298 DVLOG(1) << "Updating key: " << iter->first << "; appending value: " << value;
235 StringPiece separator("", 1); 299 iter->second.Append(GetStorage()->Write(value));
236 if (key == kCookieKey) {
237 separator = "; ";
238 }
239 iter->second = GetStorage()->Realloc(iter->second, separator, value);
240 } 300 }
241 301
242 void SpdyHeaderBlock::AppendHeader(const StringPiece key, 302 void SpdyHeaderBlock::AppendHeader(const StringPiece key,
243 const StringPiece value) { 303 const StringPiece value) {
244 block_.emplace(GetStorage()->Write(key), GetStorage()->Write(value)); 304 auto storage = GetStorage();
305 auto backed_key = storage->Write(key);
306 block_.emplace(make_pair(
307 backed_key, HeaderValue(storage, backed_key, storage->Write(value))));
245 } 308 }
246 309
247 SpdyHeaderBlock::Storage* SpdyHeaderBlock::GetStorage() { 310 SpdyHeaderBlock::Storage* SpdyHeaderBlock::GetStorage() {
248 if (!storage_) { 311 if (!storage_) {
249 storage_.reset(new Storage); 312 storage_.reset(new Storage);
250 } 313 }
251 return storage_.get(); 314 return storage_.get();
252 } 315 }
253 316
254 std::unique_ptr<base::Value> SpdyHeaderBlockNetLogCallback( 317 std::unique_ptr<base::Value> SpdyHeaderBlockNetLogCallback(
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
286 string value; 349 string value;
287 if (!it.value().GetAsString(&value)) { 350 if (!it.value().GetAsString(&value)) {
288 headers->clear(); 351 headers->clear();
289 return false; 352 return false;
290 } 353 }
291 (*headers)[it.key()] = value; 354 (*headers)[it.key()] = value;
292 } 355 }
293 return true; 356 return true;
294 } 357 }
295 358
359 size_t SpdyHeaderBlock::bytes_allocated() const {
360 if (storage_ == nullptr) {
361 return 0;
362 } else {
363 return storage_->bytes_allocated();
364 }
365 }
366
367 size_t Join(char* dst,
368 const std::vector<StringPiece>& fragments,
369 StringPiece separator) {
370 if (fragments.empty()) {
371 return 0;
372 }
373 auto original_dst = dst;
374 auto it = fragments.begin();
375 memcpy(dst, it->data(), it->size());
376 dst += it->size();
377 for (++it; it != fragments.end(); ++it) {
378 memcpy(dst, separator.data(), separator.size());
379 dst += separator.size();
380 memcpy(dst, it->data(), it->size());
381 dst += it->size();
382 }
383 return dst - original_dst;
384 }
385
296 } // namespace net 386 } // 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