Add a new implementation of SpdyUnsafeArena for code sharing between chromium
and envoy. This new implementation is merely a copy of SpdyUnsafeArenaImpl from
chromium (https://cs.chromium.org/chromium/src/net/spdy/platform/impl/spdy_unsafe_arena_impl.h?g=0&l=17) which itself is an older data structure used by SpdyHeaderBlock.
This new implemenation is not used as google3 impl. However, it's nice to have
it in quiche for code sharing between platforms.
Once this class is in quiche, chromium platform can point to this one instead of keeping the same code in SpdyUnsafeArenaImpl.
gfe-relnote: n/a (new class not in use)
PiperOrigin-RevId: 243133213
Change-Id: I52875503efc6be97c7a531d9c3e02780d66b88cc
diff --git a/spdy/core/spdy_simple_arena.cc b/spdy/core/spdy_simple_arena.cc
new file mode 100644
index 0000000..08cdc62
--- /dev/null
+++ b/spdy/core/spdy_simple_arena.cc
@@ -0,0 +1,103 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/spdy/core/spdy_simple_arena.h"
+
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
+
+namespace spdy {
+
+SpdySimpleArena::SpdySimpleArena(size_t block_size) : block_size_(block_size) {}
+
+SpdySimpleArena::~SpdySimpleArena() = default;
+
+SpdySimpleArena::SpdySimpleArena(SpdySimpleArena&& other) = default;
+SpdySimpleArena& SpdySimpleArena::operator=(SpdySimpleArena&& other) = default;
+
+char* SpdySimpleArena::Alloc(size_t size) {
+ Reserve(size);
+ Block& b = blocks_.back();
+ DCHECK_GE(b.size, b.used + size);
+ char* out = b.data.get() + b.used;
+ b.used += size;
+ return out;
+}
+
+char* SpdySimpleArena::Realloc(char* original, size_t oldsize, size_t newsize) {
+ DCHECK(!blocks_.empty());
+ Block& last = blocks_.back();
+ if (last.data.get() <= original && original < last.data.get() + last.size) {
+ // (original, oldsize) is in the last Block.
+ DCHECK_GE(last.data.get() + last.used, original + oldsize);
+ if (original + oldsize == last.data.get() + last.used) {
+ // (original, oldsize) was the most recent allocation,
+ if (original + newsize < last.data.get() + last.size) {
+ // (original, newsize) fits in the same Block.
+ last.used += newsize - oldsize;
+ return original;
+ }
+ }
+ }
+ char* out = Alloc(newsize);
+ memcpy(out, original, oldsize);
+ return out;
+}
+
+char* SpdySimpleArena::Memdup(const char* data, size_t size) {
+ char* out = Alloc(size);
+ memcpy(out, data, size);
+ return out;
+}
+
+void SpdySimpleArena::Free(char* data, size_t size) {
+ if (blocks_.empty()) {
+ return;
+ }
+ Block& b = blocks_.back();
+ if (size <= b.used && data + size == b.data.get() + b.used) {
+ // The memory region passed by the caller was the most recent allocation
+ // from the final block in this arena.
+ b.used -= size;
+ }
+}
+
+void SpdySimpleArena::Reset() {
+ blocks_.clear();
+ status_.bytes_allocated_ = 0;
+}
+
+void SpdySimpleArena::Reserve(size_t additional_space) {
+ if (blocks_.empty()) {
+ AllocBlock(std::max(additional_space, block_size_));
+ } else {
+ const Block& last = blocks_.back();
+ if (last.size < last.used + additional_space) {
+ AllocBlock(std::max(additional_space, block_size_));
+ }
+ }
+}
+
+void SpdySimpleArena::AllocBlock(size_t size) {
+ blocks_.push_back(Block(size));
+ status_.bytes_allocated_ += size;
+}
+
+SpdySimpleArena::Block::Block(size_t s) : data(new char[s]), size(s), used(0) {}
+
+SpdySimpleArena::Block::~Block() = default;
+
+SpdySimpleArena::Block::Block(SpdySimpleArena::Block&& other)
+ : size(other.size), used(other.used) {
+ data = std::move(other.data);
+}
+
+SpdySimpleArena::Block& SpdySimpleArena::Block::operator=(
+ SpdySimpleArena::Block&& other) {
+ size = other.size;
+ used = other.used;
+ data = std::move(other.data);
+ return *this;
+}
+
+} // namespace spdy
diff --git a/spdy/core/spdy_simple_arena.h b/spdy/core/spdy_simple_arena.h
new file mode 100644
index 0000000..4cd48ce
--- /dev/null
+++ b/spdy/core/spdy_simple_arena.h
@@ -0,0 +1,77 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_SPDY_CORE_SPDY_SIMPLE_ARENA_H_
+#define QUICHE_SPDY_CORE_SPDY_SIMPLE_ARENA_H_
+
+#include <memory>
+#include <vector>
+
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h"
+
+namespace spdy {
+
+// Allocates large blocks of memory, and doles them out in smaller chunks.
+// Not thread-safe.
+class SPDY_EXPORT_PRIVATE SpdySimpleArena {
+ public:
+ class Status {
+ private:
+ friend class SpdySimpleArena;
+ size_t bytes_allocated_;
+
+ public:
+ Status() : bytes_allocated_(0) {}
+ size_t bytes_allocated() const { return bytes_allocated_; }
+ };
+
+ // Blocks allocated by this arena will be at least |block_size| bytes.
+ explicit SpdySimpleArena(size_t block_size);
+ ~SpdySimpleArena();
+
+ // Copy and assign are not allowed.
+ SpdySimpleArena() = delete;
+ SpdySimpleArena(const SpdySimpleArena&) = delete;
+ SpdySimpleArena& operator=(const SpdySimpleArena&) = delete;
+
+ // Move is allowed.
+ SpdySimpleArena(SpdySimpleArena&& other);
+ SpdySimpleArena& operator=(SpdySimpleArena&& other);
+
+ char* Alloc(size_t size);
+ char* Realloc(char* original, size_t oldsize, size_t newsize);
+ char* Memdup(const char* data, size_t size);
+
+ // If |data| and |size| describe the most recent allocation made from this
+ // arena, the memory is reclaimed. Otherwise, this method is a no-op.
+ void Free(char* data, size_t size);
+
+ void Reset();
+
+ Status status() const { return status_; }
+
+ private:
+ struct Block {
+ std::unique_ptr<char[]> data;
+ size_t size = 0;
+ size_t used = 0;
+
+ explicit Block(size_t s);
+ ~Block();
+
+ Block(Block&& other);
+ Block& operator=(Block&& other);
+ };
+
+ void Reserve(size_t additional_space);
+ void AllocBlock(size_t size);
+
+ size_t block_size_;
+ std::vector<Block> blocks_;
+ Status status_;
+};
+
+} // namespace spdy
+
+#endif // QUICHE_SPDY_CORE_SPDY_SIMPLE_ARENA_H_
diff --git a/spdy/core/spdy_simple_arena_test.cc b/spdy/core/spdy_simple_arena_test.cc
new file mode 100644
index 0000000..fc4b130
--- /dev/null
+++ b/spdy/core/spdy_simple_arena_test.cc
@@ -0,0 +1,136 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/spdy/core/spdy_simple_arena.h"
+
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h"
+
+namespace spdy {
+namespace {
+
+size_t kDefaultBlockSize = 2048;
+const char kTestString[] = "This is a decently long test string.";
+
+TEST(SpdySimpleArenaTest, Memdup) {
+ SpdySimpleArena arena(kDefaultBlockSize);
+ const size_t length = strlen(kTestString);
+ char* c = arena.Memdup(kTestString, length);
+ EXPECT_NE(nullptr, c);
+ EXPECT_NE(c, kTestString);
+ EXPECT_EQ(SpdyStringPiece(c, length), kTestString);
+}
+
+TEST(SpdySimpleArenaTest, MemdupLargeString) {
+ SpdySimpleArena arena(10 /* block size */);
+ const size_t length = strlen(kTestString);
+ char* c = arena.Memdup(kTestString, length);
+ EXPECT_NE(nullptr, c);
+ EXPECT_NE(c, kTestString);
+ EXPECT_EQ(SpdyStringPiece(c, length), kTestString);
+}
+
+TEST(SpdySimpleArenaTest, MultipleBlocks) {
+ SpdySimpleArena arena(40 /* block size */);
+ std::vector<SpdyString> strings = {
+ "One decently long string.", "Another string.",
+ "A third string that will surely go in a different block."};
+ std::vector<SpdyStringPiece> copies;
+ for (const SpdyString& s : strings) {
+ SpdyStringPiece sp(arena.Memdup(s.data(), s.size()), s.size());
+ copies.push_back(sp);
+ }
+ EXPECT_EQ(strings.size(), copies.size());
+ for (size_t i = 0; i < strings.size(); ++i) {
+ EXPECT_EQ(copies[i], strings[i]);
+ }
+}
+
+TEST(SpdySimpleArenaTest, UseAfterReset) {
+ SpdySimpleArena arena(kDefaultBlockSize);
+ const size_t length = strlen(kTestString);
+ char* c = arena.Memdup(kTestString, length);
+ arena.Reset();
+ c = arena.Memdup(kTestString, length);
+ EXPECT_NE(nullptr, c);
+ EXPECT_NE(c, kTestString);
+ EXPECT_EQ(SpdyStringPiece(c, length), kTestString);
+}
+
+TEST(SpdySimpleArenaTest, Free) {
+ SpdySimpleArena arena(kDefaultBlockSize);
+ const size_t length = strlen(kTestString);
+ // Freeing memory not owned by the arena should be a no-op, and freeing
+ // before any allocations from the arena should be a no-op.
+ arena.Free(const_cast<char*>(kTestString), length);
+ char* c1 = arena.Memdup("Foo", 3);
+ char* c2 = arena.Memdup(kTestString, length);
+ arena.Free(const_cast<char*>(kTestString), length);
+ char* c3 = arena.Memdup("Bar", 3);
+ char* c4 = arena.Memdup(kTestString, length);
+ EXPECT_NE(c1, c2);
+ EXPECT_NE(c1, c3);
+ EXPECT_NE(c1, c4);
+ EXPECT_NE(c2, c3);
+ EXPECT_NE(c2, c4);
+ EXPECT_NE(c3, c4);
+ // Freeing c4 should succeed, since it was the most recent allocation.
+ arena.Free(c4, length);
+ // Freeing c2 should be a no-op.
+ arena.Free(c2, length);
+ // c5 should reuse memory that was previously used by c4.
+ char* c5 = arena.Memdup("Baz", 3);
+ EXPECT_EQ(c4, c5);
+}
+
+TEST(SpdySimpleArenaTest, Alloc) {
+ SpdySimpleArena arena(kDefaultBlockSize);
+ const size_t length = strlen(kTestString);
+ char* c1 = arena.Alloc(length);
+ char* c2 = arena.Alloc(2 * length);
+ char* c3 = arena.Alloc(3 * length);
+ char* c4 = arena.Memdup(kTestString, length);
+ EXPECT_EQ(c1 + length, c2);
+ EXPECT_EQ(c2 + 2 * length, c3);
+ EXPECT_EQ(c3 + 3 * length, c4);
+ EXPECT_EQ(SpdyStringPiece(c4, length), kTestString);
+}
+
+TEST(SpdySimpleArenaTest, Realloc) {
+ SpdySimpleArena arena(kDefaultBlockSize);
+ const size_t length = strlen(kTestString);
+ // Simple realloc that fits in the block.
+ char* c1 = arena.Memdup(kTestString, length);
+ char* c2 = arena.Realloc(c1, length, 2 * length);
+ EXPECT_TRUE(c1);
+ EXPECT_EQ(c1, c2);
+ EXPECT_EQ(SpdyStringPiece(c1, length), kTestString);
+ // Multiple reallocs.
+ char* c3 = arena.Memdup(kTestString, length);
+ EXPECT_EQ(c2 + 2 * length, c3);
+ EXPECT_EQ(SpdyStringPiece(c3, length), kTestString);
+ char* c4 = arena.Realloc(c3, length, 2 * length);
+ EXPECT_EQ(c3, c4);
+ EXPECT_EQ(SpdyStringPiece(c4, length), kTestString);
+ char* c5 = arena.Realloc(c4, 2 * length, 3 * length);
+ EXPECT_EQ(c4, c5);
+ EXPECT_EQ(SpdyStringPiece(c5, length), kTestString);
+ char* c6 = arena.Memdup(kTestString, length);
+ EXPECT_EQ(c5 + 3 * length, c6);
+ EXPECT_EQ(SpdyStringPiece(c6, length), kTestString);
+ // Realloc that does not fit in the remainder of the first block.
+ char* c7 = arena.Realloc(c6, length, kDefaultBlockSize);
+ EXPECT_EQ(SpdyStringPiece(c7, length), kTestString);
+ arena.Free(c7, kDefaultBlockSize);
+ char* c8 = arena.Memdup(kTestString, length);
+ EXPECT_NE(c6, c7);
+ EXPECT_EQ(c7, c8);
+ EXPECT_EQ(SpdyStringPiece(c8, length), kTestString);
+}
+
+} // namespace
+} // namespace spdy