Index: net/spdy/spdy_header_block.cc |
diff --git a/net/spdy/spdy_header_block.cc b/net/spdy/spdy_header_block.cc |
index 6e72451112d96c7d982070976b2fbbd6003f6df0..d12ab70cb9000c78e4d3bd0da1224cdc1bb31485 100644 |
--- a/net/spdy/spdy_header_block.cc |
+++ b/net/spdy/spdy_header_block.cc |
@@ -4,10 +4,207 @@ |
#include "net/spdy/spdy_header_block.h" |
+#include <algorithm> |
+#include <ios> |
+#include <utility> |
+#include <vector> |
+ |
+#include "base/logging.h" |
#include "base/values.h" |
#include "net/http/http_log_util.h" |
+using base::StringPiece; |
+using std::dec; |
+using std::hex; |
+using std::max; |
+using std::min; |
+ |
namespace net { |
+namespace { |
+ |
+// SpdyHeaderBlock::Storage uses a small initial block in case we only have a |
+// minimal set of headers. |
+const size_t kInitialStorageBlockSize = 512; |
+ |
+// SpdyHeaderBlock::Storage allocates blocks of this size by default. |
+const size_t kDefaultStorageBlockSize = 2048; |
+ |
+// When copying a SpdyHeaderBlock, the new block will allocate at most this |
+// much memory for the initial contiguous block. |
+const size_t kMaxContiguousAllocation = 16 * 1024; |
+ |
+} // namespace |
+ |
+// This class provides a backing store for StringPieces. It uses a sequence of |
+// large, contiguous blocks. It has the property that StringPieces that refer |
+// to data in Storage are never invalidated until the Storage is deleted. |
+// |
+// Write operations always append to the last block. If there is not enough |
+// space to perform the write, a new block is allocated, and any unused space |
+// is wasted. |
+class SpdyHeaderBlock::Storage { |
+ public: |
+ Storage() : bytes_used_(0) {} |
+ ~Storage() { Clear(); } |
+ |
+ void Reserve(size_t additional_space) { |
+ if (blocks_.empty()) { |
+ AllocBlock(max(additional_space, kInitialStorageBlockSize)); |
+ } else { |
+ const Block& last = blocks_.back(); |
+ if (last.size - last.used < additional_space) { |
+ AllocBlock(max(additional_space, kDefaultStorageBlockSize)); |
+ } |
+ } |
+ } |
+ |
+ StringPiece Write(const StringPiece s) { |
+ Reserve(s.size()); |
+ Block* last = &blocks_.back(); |
+ memcpy(last->data + last->used, s.data(), s.size()); |
+ StringPiece out(last->data + last->used, s.size()); |
+ VLOG(3) << "Write result: " << hex |
+ << reinterpret_cast<const void*>(out.data()) << ", " << dec |
+ << out.size(); |
+ last->used += s.size(); |
+ bytes_used_ += s.size(); |
+ return out; |
+ } |
+ |
+ void Clear() { |
+ while (!blocks_.empty()) { |
+ delete[] blocks_.back().data; |
+ blocks_.pop_back(); |
+ } |
+ bytes_used_ = 0; |
+ } |
+ |
+ size_t BytesUsed() const { return bytes_used_; } |
+ |
+ private: |
+ // TODO(bnc): As soon as move semantics are allowed, change from naked pointer |
+ // to scoped_ptr<>, or better yet, unique_ptr<>. |
+ struct Block { |
+ char* data; |
+ size_t size = 0; |
+ size_t used = 0; |
+ |
+ Block(char* data, size_t s) : data(data), size(s), used(0) {} |
+ }; |
+ |
+ void AllocBlock(size_t size) { |
+ blocks_.push_back(Block(new char[size], size)); |
+ } |
+ |
+ std::vector<Block> blocks_; |
+ size_t bytes_used_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(Storage); |
+}; |
+ |
+SpdyHeaderBlock::StringPieceProxy::StringPieceProxy( |
+ SpdyHeaderBlock::MapType* block, |
+ SpdyHeaderBlock::Storage* storage, |
+ SpdyHeaderBlock::MapType::iterator lookup_result, |
+ const StringPiece key) |
+ : block_(block), |
+ storage_(storage), |
+ lookup_result_(lookup_result), |
+ key_(key) {} |
+ |
+SpdyHeaderBlock::StringPieceProxy::~StringPieceProxy() {} |
+ |
+SpdyHeaderBlock::StringPieceProxy& SpdyHeaderBlock::StringPieceProxy::operator=( |
+ const StringPiece value) { |
+ if (lookup_result_ == block_->end()) { |
+ VLOG(1) << "Inserting: (" << key_ << ", " << value << ")"; |
+ lookup_result_ = |
+ block_->insert(std::make_pair(key_, storage_->Write(value))).first; |
+ } else { |
+ VLOG(1) << "Updating key: " << key_ << " with value: " << value; |
+ lookup_result_->second = storage_->Write(value); |
+ } |
+ return *this; |
+} |
+ |
+SpdyHeaderBlock::StringPieceProxy::operator StringPiece() const { |
+ return (lookup_result_ == block_->end()) ? StringPiece() |
+ : lookup_result_->second; |
+} |
+ |
+void SpdyHeaderBlock::StringPieceProxy::reserve(size_t size) { |
+ storage_->Reserve(size); |
+} |
+ |
+SpdyHeaderBlock::SpdyHeaderBlock() : storage_(new Storage) {} |
+ |
+SpdyHeaderBlock::~SpdyHeaderBlock() {} |
+ |
+SpdyHeaderBlock::SpdyHeaderBlock(const SpdyHeaderBlock& other) |
+ : storage_(new Storage) { |
+ storage_->Reserve(min(other.storage_->BytesUsed(), kMaxContiguousAllocation)); |
+ for (auto iter : other) { |
+ AppendHeader(iter.first, iter.second); |
+ } |
+} |
+ |
+SpdyHeaderBlock& SpdyHeaderBlock::operator=(const SpdyHeaderBlock& other) { |
+ clear(); |
+ storage_->Reserve(min(other.storage_->BytesUsed(), kMaxContiguousAllocation)); |
+ for (auto iter : other) { |
+ AppendHeader(iter.first, iter.second); |
+ } |
+ return *this; |
+} |
+ |
+void SpdyHeaderBlock::clear() { |
+ block_.clear(); |
+ storage_->Clear(); |
+} |
+ |
+void SpdyHeaderBlock::insert( |
+ const SpdyHeaderBlock::MapType::value_type& value) { |
+ ReplaceOrAppendHeader(value.first, value.second); |
+} |
+ |
+SpdyHeaderBlock::StringPieceProxy SpdyHeaderBlock::operator[]( |
+ const StringPiece key) { |
+ VLOG(2) << "Operator[] saw key: " << key; |
+ StringPiece out_key; |
+ auto iter = block_.find(key); |
+ if (iter == block_.end()) { |
+ // We write the key first, to assure that the StringPieceProxy has a |
+ // reference to a valid StringPiece in its operator=. |
+ out_key = storage_->Write(key); |
+ VLOG(2) << "Key written as: " << hex << static_cast<const void*>(key.data()) |
+ << ", " << dec << key.size(); |
+ } else { |
+ out_key = iter->first; |
+ } |
+ return StringPieceProxy(&block_, storage_.get(), iter, out_key); |
+} |
+ |
+bool SpdyHeaderBlock::operator==(const SpdyHeaderBlock& other) const { |
+ return size() == other.size() && std::equal(begin(), end(), other.begin()); |
+} |
+ |
+void SpdyHeaderBlock::ReplaceOrAppendHeader(const StringPiece key, |
+ const StringPiece value) { |
+ // TODO(birenroy): Write new value in place of old value, if it fits. |
+ auto iter = block_.find(key); |
+ if (iter == block_.end()) { |
+ VLOG(1) << "Inserting: (" << key << ", " << value << ")"; |
+ AppendHeader(key, value); |
+ } else { |
+ VLOG(1) << "Updating key: " << iter->first << " with value: " << value; |
+ iter->second = storage_->Write(value); |
+ } |
+} |
+ |
+void SpdyHeaderBlock::AppendHeader(const StringPiece key, |
+ const StringPiece value) { |
+ block_.insert(make_pair(storage_->Write(key), storage_->Write(value))); |
+} |
scoped_ptr<base::Value> SpdyHeaderBlockNetLogCallback( |
const SpdyHeaderBlock* headers, |
@@ -17,8 +214,9 @@ scoped_ptr<base::Value> SpdyHeaderBlockNetLogCallback( |
for (SpdyHeaderBlock::const_iterator it = headers->begin(); |
it != headers->end(); ++it) { |
headers_dict->SetWithoutPathExpansion( |
- it->first, new base::StringValue(ElideHeaderValueForNetLog( |
- capture_mode, it->first, it->second))); |
+ it->first.as_string(), |
+ new base::StringValue(ElideHeaderValueForNetLog( |
+ capture_mode, it->first.as_string(), it->second.as_string()))); |
} |
dict->Set("headers", headers_dict); |
return dict.Pass(); |
@@ -40,10 +238,12 @@ bool SpdyHeaderBlockFromNetLogParam( |
for (base::DictionaryValue::Iterator it(*header_dict); !it.IsAtEnd(); |
it.Advance()) { |
- if (!it.value().GetAsString(&(*headers)[it.key()])) { |
+ std::string value; |
+ if (!it.value().GetAsString(&value)) { |
headers->clear(); |
return false; |
} |
+ (*headers)[it.key()] = value; |
} |
return true; |
} |