Extracts SpdyHeaderBlock::Storage as a separate library. This is a continuation of the attempt to simplify SpdyHeaderBlock.
gfe-relnote: Refactoring, no functional change.
PiperOrigin-RevId: 284571559
Change-Id: I8396e585439ec58bcdba6aeb22f44f56352625ba
diff --git a/spdy/core/spdy_header_block.cc b/spdy/core/spdy_header_block.cc
index 91c8494..9ccc627 100644
--- a/spdy/core/spdy_header_block.cc
+++ b/spdy/core/spdy_header_block.cc
@@ -25,9 +25,6 @@
// large header blocks.
const size_t kInitialMapBuckets = 11;
-// SpdyHeaderBlock::Storage allocates blocks of this size by default.
-const size_t kDefaultStorageBlockSize = 2048;
-
const char kCookieKey[] = "cookie";
const char kNullSeparator = 0;
@@ -42,62 +39,7 @@
} // namespace
-// This class provides a backing store for SpdyStringPieces. It previously used
-// custom allocation logic, but now uses an UnsafeArena instead. It has the
-// property that SpdyStringPieces that refer to data in Storage are never
-// invalidated until the Storage is deleted or Clear() is called.
-//
-// 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() : arena_(kDefaultStorageBlockSize) {}
- Storage(const Storage&) = delete;
- Storage& operator=(const Storage&) = delete;
-
- SpdyStringPiece Write(const SpdyStringPiece s) {
- return SpdyStringPiece(arena_.Memdup(s.data(), s.size()), s.size());
- }
-
- // If |s| points to the most recent allocation from arena_, the arena will
- // reclaim the memory. Otherwise, this method is a no-op.
- void Rewind(const SpdyStringPiece s) {
- arena_.Free(const_cast<char*>(s.data()), s.size());
- }
-
- void Clear() { arena_.Reset(); }
-
- // Given a list of fragments and a separator, writes the fragments joined by
- // the separator to a contiguous region of memory. Returns a SpdyStringPiece
- // pointing to the region of memory.
- SpdyStringPiece WriteFragments(const std::vector<SpdyStringPiece>& fragments,
- SpdyStringPiece separator) {
- if (fragments.empty()) {
- return SpdyStringPiece();
- }
- size_t total_size = separator.size() * (fragments.size() - 1);
- for (const auto fragment : fragments) {
- total_size += fragment.size();
- }
- char* dst = arena_.Alloc(total_size);
- size_t written = Join(dst, fragments, separator);
- DCHECK_EQ(written, total_size);
- return SpdyStringPiece(dst, total_size);
- }
-
- size_t bytes_allocated() const { return arena_.status().bytes_allocated(); }
-
- // TODO(xunjieli): https://crbug.com/669108. Merge this with bytes_allocated()
- size_t EstimateMemoryUsage() const {
- return arena_.status().bytes_allocated();
- }
-
- private:
- SpdyUnsafeArena arena_;
-};
-
-SpdyHeaderBlock::HeaderValue::HeaderValue(Storage* storage,
+SpdyHeaderBlock::HeaderValue::HeaderValue(SpdyHeaderStorage* storage,
SpdyStringPiece key,
SpdyStringPiece initial_value)
: storage_(storage),
@@ -186,8 +128,8 @@
SpdyHeaderBlock::ValueProxy::~ValueProxy() {
// If the ValueProxy is destroyed while lookup_result_ == block_->end(),
- // the assignment operator was never used, and the block's Storage can
- // reclaim the memory used by the key. This makes lookup-only access to
+ // the assignment operator was never used, and the block's SpdyHeaderStorage
+ // can reclaim the memory used by the key. This makes lookup-only access to
// SpdyHeaderBlock through operator[] memory-neutral.
if (valid_ && lookup_result_ == block_->map_.end()) {
block_->GetStorage()->Rewind(key_);
@@ -197,7 +139,7 @@
SpdyHeaderBlock::ValueProxy& SpdyHeaderBlock::ValueProxy::operator=(
const SpdyStringPiece value) {
*spdy_header_block_value_size_ += value.size();
- Storage* storage = block_->GetStorage();
+ SpdyHeaderStorage* storage = block_->GetStorage();
if (lookup_result_ == block_->map_.end()) {
SPDY_DVLOG(1) << "Inserting: (" << key_ << ", " << value << ")";
lookup_result_ =
@@ -300,7 +242,7 @@
SPDY_DVLOG(1) << "Updating key: " << iter->first
<< " with value: " << value.second;
value_size_ -= iter->second.SizeEstimate();
- auto* storage = GetStorage();
+ SpdyHeaderStorage* storage = GetStorage();
iter->second =
HeaderValue(storage, iter->first, storage->Write(value.second));
}
@@ -350,14 +292,14 @@
void SpdyHeaderBlock::AppendHeader(const SpdyStringPiece key,
const SpdyStringPiece value) {
auto backed_key = WriteKey(key);
- auto* storage = GetStorage();
+ SpdyHeaderStorage* storage = GetStorage();
map_.emplace(std::make_pair(
backed_key, HeaderValue(storage, backed_key, storage->Write(value))));
}
-SpdyHeaderBlock::Storage* SpdyHeaderBlock::GetStorage() {
+SpdyHeaderStorage* SpdyHeaderBlock::GetStorage() {
if (storage_ == nullptr) {
- storage_ = std::make_unique<Storage>();
+ storage_ = std::make_unique<SpdyHeaderStorage>();
}
return storage_.get();
}
@@ -375,23 +317,4 @@
}
}
-size_t Join(char* dst,
- const std::vector<SpdyStringPiece>& fragments,
- SpdyStringPiece separator) {
- if (fragments.empty()) {
- return 0;
- }
- auto* original_dst = dst;
- auto it = fragments.begin();
- memcpy(dst, it->data(), it->size());
- dst += it->size();
- for (++it; it != fragments.end(); ++it) {
- memcpy(dst, separator.data(), separator.size());
- dst += separator.size();
- memcpy(dst, it->data(), it->size());
- dst += it->size();
- }
- return dst - original_dst;
-}
-
} // namespace spdy
diff --git a/spdy/core/spdy_header_block.h b/spdy/core/spdy_header_block.h
index b9c0967..ff5f4c9 100644
--- a/spdy/core/spdy_header_block.h
+++ b/spdy/core/spdy_header_block.h
@@ -13,6 +13,7 @@
#include <utility>
#include <vector>
+#include "net/third_party/quiche/src/spdy/core/spdy_header_storage.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_containers.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_macros.h"
@@ -38,13 +39,11 @@
// It's expected that keys are rarely deleted from a SpdyHeaderBlock.
class SPDY_EXPORT_PRIVATE SpdyHeaderBlock {
private:
- class Storage;
-
// Stores a list of value fragments that can be joined later with a
// key-dependent separator.
class SPDY_EXPORT_PRIVATE HeaderValue {
public:
- HeaderValue(Storage* storage,
+ HeaderValue(SpdyHeaderStorage* storage,
SpdyStringPiece key,
SpdyStringPiece initial_value);
@@ -73,7 +72,7 @@
// fragments and separators.
SpdyStringPiece ConsolidatedValue() const;
- mutable Storage* storage_;
+ mutable SpdyHeaderStorage* storage_;
mutable std::vector<SpdyStringPiece> fragments_;
// The first element is the key; the second is the consolidated value.
mutable std::pair<SpdyStringPiece, SpdyStringPiece> pair_;
@@ -180,7 +179,7 @@
// This object provides automatic conversions that allow SpdyHeaderBlock to be
// nearly a drop-in replacement for
// SpdyLinkedHashMap<std::string, std::string>.
- // It reads data from or writes data to a SpdyHeaderBlock::Storage.
+ // It reads data from or writes data to a SpdyHeaderStorage.
class SPDY_EXPORT_PRIVATE ValueProxy {
public:
~ValueProxy();
@@ -226,25 +225,19 @@
friend class test::SpdyHeaderBlockPeer;
void AppendHeader(const SpdyStringPiece key, const SpdyStringPiece value);
- Storage* GetStorage();
+ SpdyHeaderStorage* GetStorage();
SpdyStringPiece WriteKey(const SpdyStringPiece key);
size_t bytes_allocated() const;
// SpdyStringPieces held by |map_| point to memory owned by |*storage_|.
// |storage_| might be nullptr as long as |map_| is empty.
MapType map_;
- std::unique_ptr<Storage> storage_;
+ std::unique_ptr<SpdyHeaderStorage> storage_;
size_t key_size_ = 0;
size_t value_size_ = 0;
};
-// Writes |fragments| to |dst|, joined by |separator|. |dst| must be large
-// enough to hold the result. Returns the number of bytes written.
-SPDY_EXPORT_PRIVATE size_t Join(char* dst,
- const std::vector<SpdyStringPiece>& fragments,
- SpdyStringPiece separator);
-
} // namespace spdy
#endif // QUICHE_SPDY_CORE_SPDY_HEADER_BLOCK_H_
diff --git a/spdy/core/spdy_header_block_test.cc b/spdy/core/spdy_header_block_test.cc
index 56facb4..a744a3b 100644
--- a/spdy/core/spdy_header_block_test.cc
+++ b/spdy/core/spdy_header_block_test.cc
@@ -192,32 +192,6 @@
Pair("foo", "baz")));
}
-TEST(JoinTest, JoinEmpty) {
- std::vector<SpdyStringPiece> empty;
- SpdyStringPiece separator = ", ";
- char buf[10] = "";
- size_t written = Join(buf, empty, separator);
- EXPECT_EQ(0u, written);
-}
-
-TEST(JoinTest, JoinOne) {
- std::vector<SpdyStringPiece> v = {"one"};
- SpdyStringPiece separator = ", ";
- char buf[15];
- size_t written = Join(buf, v, separator);
- EXPECT_EQ(3u, written);
- EXPECT_EQ("one", SpdyStringPiece(buf, written));
-}
-
-TEST(JoinTest, JoinMultiple) {
- std::vector<SpdyStringPiece> v = {"one", "two", "three"};
- SpdyStringPiece separator = ", ";
- char buf[15];
- size_t written = Join(buf, v, separator);
- EXPECT_EQ(15u, written);
- EXPECT_EQ("one, two, three", SpdyStringPiece(buf, written));
-}
-
namespace {
size_t SpdyHeaderBlockSize(const SpdyHeaderBlock& block) {
size_t size = 0;
diff --git a/spdy/core/spdy_header_storage.cc b/spdy/core/spdy_header_storage.cc
new file mode 100644
index 0000000..7028e90
--- /dev/null
+++ b/spdy/core/spdy_header_storage.cc
@@ -0,0 +1,56 @@
+#include "net/third_party/quiche/src/spdy/core/spdy_header_storage.h"
+
+namespace spdy {
+namespace {
+
+// SpdyHeaderStorage allocates blocks of this size by default.
+const size_t kDefaultStorageBlockSize = 2048;
+
+} // namespace
+
+SpdyHeaderStorage::SpdyHeaderStorage() : arena_(kDefaultStorageBlockSize) {}
+
+SpdyStringPiece SpdyHeaderStorage::Write(const SpdyStringPiece s) {
+ return SpdyStringPiece(arena_.Memdup(s.data(), s.size()), s.size());
+}
+
+void SpdyHeaderStorage::Rewind(const SpdyStringPiece s) {
+ arena_.Free(const_cast<char*>(s.data()), s.size());
+}
+
+SpdyStringPiece SpdyHeaderStorage::WriteFragments(
+ const std::vector<SpdyStringPiece>& fragments,
+ SpdyStringPiece separator) {
+ if (fragments.empty()) {
+ return SpdyStringPiece();
+ }
+ size_t total_size = separator.size() * (fragments.size() - 1);
+ for (const auto fragment : fragments) {
+ total_size += fragment.size();
+ }
+ char* dst = arena_.Alloc(total_size);
+ size_t written = Join(dst, fragments, separator);
+ DCHECK_EQ(written, total_size);
+ return SpdyStringPiece(dst, total_size);
+}
+
+size_t Join(char* dst,
+ const std::vector<SpdyStringPiece>& fragments,
+ SpdyStringPiece separator) {
+ if (fragments.empty()) {
+ return 0;
+ }
+ auto* original_dst = dst;
+ auto it = fragments.begin();
+ memcpy(dst, it->data(), it->size());
+ dst += it->size();
+ for (++it; it != fragments.end(); ++it) {
+ memcpy(dst, separator.data(), separator.size());
+ dst += separator.size();
+ memcpy(dst, it->data(), it->size());
+ dst += it->size();
+ }
+ return dst - original_dst;
+}
+
+} // namespace spdy
diff --git a/spdy/core/spdy_header_storage.h b/spdy/core/spdy_header_storage.h
new file mode 100644
index 0000000..26ae2ae
--- /dev/null
+++ b/spdy/core/spdy_header_storage.h
@@ -0,0 +1,55 @@
+#ifndef QUICHE_SPDY_CORE_SPDY_HEADER_STORAGE_H_
+#define QUICHE_SPDY_CORE_SPDY_HEADER_STORAGE_H_
+
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_unsafe_arena.h"
+
+namespace spdy {
+
+// This class provides a backing store for SpdyStringPieces. It previously used
+// custom allocation logic, but now uses an UnsafeArena instead. It has the
+// property that SpdyStringPieces that refer to data in SpdyHeaderStorage are
+// never invalidated until the SpdyHeaderStorage is deleted or Clear() is
+// called.
+//
+// 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 SPDY_EXPORT_PRIVATE SpdyHeaderStorage {
+ public:
+ SpdyHeaderStorage();
+ SpdyHeaderStorage(const SpdyHeaderStorage&) = delete;
+ SpdyHeaderStorage& operator=(const SpdyHeaderStorage&) = delete;
+
+ SpdyStringPiece Write(SpdyStringPiece s);
+
+ // If |s| points to the most recent allocation from arena_, the arena will
+ // reclaim the memory. Otherwise, this method is a no-op.
+ void Rewind(SpdyStringPiece s);
+
+ void Clear() { arena_.Reset(); }
+
+ // Given a list of fragments and a separator, writes the fragments joined by
+ // the separator to a contiguous region of memory. Returns a SpdyStringPiece
+ // pointing to the region of memory.
+ SpdyStringPiece WriteFragments(const std::vector<SpdyStringPiece>& fragments,
+ SpdyStringPiece separator);
+
+ size_t bytes_allocated() const { return arena_.status().bytes_allocated(); }
+
+ size_t EstimateMemoryUsage() const { return bytes_allocated(); }
+
+ private:
+ SpdyUnsafeArena arena_;
+};
+
+// Writes |fragments| to |dst|, joined by |separator|. |dst| must be large
+// enough to hold the result. Returns the number of bytes written.
+SPDY_EXPORT_PRIVATE size_t Join(char* dst,
+ const std::vector<SpdyStringPiece>& fragments,
+ SpdyStringPiece separator);
+
+} // namespace spdy
+
+#endif // QUICHE_SPDY_CORE_SPDY_HEADER_STORAGE_H_
diff --git a/spdy/core/spdy_header_storage_test.cc b/spdy/core/spdy_header_storage_test.cc
new file mode 100644
index 0000000..6f82013
--- /dev/null
+++ b/spdy/core/spdy_header_storage_test.cc
@@ -0,0 +1,35 @@
+#include "net/third_party/quiche/src/spdy/core/spdy_header_storage.h"
+
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_test.h"
+
+namespace spdy {
+namespace test {
+
+TEST(JoinTest, JoinEmpty) {
+ std::vector<SpdyStringPiece> empty;
+ SpdyStringPiece separator = ", ";
+ char buf[10] = "";
+ size_t written = Join(buf, empty, separator);
+ EXPECT_EQ(0u, written);
+}
+
+TEST(JoinTest, JoinOne) {
+ std::vector<SpdyStringPiece> v = {"one"};
+ SpdyStringPiece separator = ", ";
+ char buf[15];
+ size_t written = Join(buf, v, separator);
+ EXPECT_EQ(3u, written);
+ EXPECT_EQ("one", SpdyStringPiece(buf, written));
+}
+
+TEST(JoinTest, JoinMultiple) {
+ std::vector<SpdyStringPiece> v = {"one", "two", "three"};
+ SpdyStringPiece separator = ", ";
+ char buf[15];
+ size_t written = Join(buf, v, separator);
+ EXPECT_EQ(15u, written);
+ EXPECT_EQ("one, two, three", SpdyStringPiece(buf, written));
+}
+
+} // namespace test
+} // namespace spdy