summaryrefslogtreecommitdiffstats
path: root/xpcom/tests/gtest/TestArenaAllocator.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--xpcom/tests/gtest/TestArenaAllocator.cpp310
1 files changed, 310 insertions, 0 deletions
diff --git a/xpcom/tests/gtest/TestArenaAllocator.cpp b/xpcom/tests/gtest/TestArenaAllocator.cpp
new file mode 100644
index 0000000000..fb11952927
--- /dev/null
+++ b/xpcom/tests/gtest/TestArenaAllocator.cpp
@@ -0,0 +1,310 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArenaAllocator.h"
+#include "mozilla/ArenaAllocatorExtensions.h"
+#include "nsIMemoryReporter.h" // MOZ_MALLOC_SIZE_OF
+
+#include "gtest/gtest.h"
+
+using mozilla::ArenaAllocator;
+
+TEST(ArenaAllocator, Constructor)
+{ ArenaAllocator<4096, 4> a; }
+
+TEST(ArenaAllocator, DefaultAllocate)
+{
+ // Test default 1-byte alignment.
+ ArenaAllocator<1024> a;
+ void* x = a.Allocate(101);
+ void* y = a.Allocate(101);
+
+ // Given 1-byte aligment, we expect the allocations to follow
+ // each other exactly.
+ EXPECT_EQ(uintptr_t(x) + 101, uintptr_t(y));
+}
+
+TEST(ArenaAllocator, AllocateAlignment)
+{
+ // Test non-default 8-byte alignment.
+ static const size_t kAlignment = 8;
+ ArenaAllocator<1024, kAlignment> a;
+
+ // Make sure aligment is correct for 1-8.
+ for (size_t i = 1; i <= kAlignment; i++) {
+ // All of these should be 8 bytes
+ void* x = a.Allocate(i);
+ void* y = a.Allocate(i);
+ EXPECT_EQ(uintptr_t(x) + kAlignment, uintptr_t(y));
+ }
+
+ // Test with slightly larger than specified alignment.
+ void* x = a.Allocate(kAlignment + 1);
+ void* y = a.Allocate(kAlignment + 1);
+
+ // Given 8-byte aligment, and a non-8-byte aligned request we expect the
+ // allocations to be padded.
+ EXPECT_NE(uintptr_t(x) + kAlignment, uintptr_t(y));
+
+ // We expect 7 bytes of padding to have been added.
+ EXPECT_EQ(uintptr_t(x) + kAlignment * 2, uintptr_t(y));
+}
+
+#if 0
+TEST(ArenaAllocator, AllocateZeroBytes)
+{
+ // This would have to be a death test. Since we chose to provide an
+ // infallible allocator we can't just return nullptr in the 0 case as
+ // there's no way to differentiate that from the OOM case.
+ ArenaAllocator<1024> a;
+ void* x = a.Allocate(0);
+ EXPECT_FALSE(x);
+}
+
+TEST(ArenaAllocator, BadAlignment)
+{
+ // This test causes build failures by triggering the static assert enforcing
+ // a power-of-two alignment.
+ ArenaAllocator<256, 3> a;
+ ArenaAllocator<256, 7> b;
+ ArenaAllocator<256, 17> c;
+}
+#endif
+
+TEST(ArenaAllocator, AllocateMultipleSizes)
+{
+ // Test non-default 4-byte alignment.
+ ArenaAllocator<4096, 4> a;
+
+ for (int i = 1; i < 50; i++) {
+ void* x = a.Allocate(i);
+ // All the allocations should be aligned properly.
+ EXPECT_EQ(uintptr_t(x) % 4, uintptr_t(0));
+ }
+
+ // Test a large 64-byte alignment
+ ArenaAllocator<8192, 64> b;
+ for (int i = 1; i < 100; i++) {
+ void* x = b.Allocate(i);
+ // All the allocations should be aligned properly.
+ EXPECT_EQ(uintptr_t(x) % 64, uintptr_t(0));
+ }
+}
+
+TEST(ArenaAllocator, AllocateInDifferentChunks)
+{
+ // Test default 1-byte alignment.
+ ArenaAllocator<4096> a;
+ void* x = a.Allocate(4000);
+ void* y = a.Allocate(4000);
+ EXPECT_NE(uintptr_t(x) + 4000, uintptr_t(y));
+}
+
+TEST(ArenaAllocator, AllocateLargerThanArenaSize)
+{
+ // Test default 1-byte alignment.
+ ArenaAllocator<256> a;
+ void* x = a.Allocate(4000);
+ void* y = a.Allocate(4000);
+ EXPECT_TRUE(x);
+ EXPECT_TRUE(y);
+
+ // Now try a normal allocation, it should behave as expected.
+ x = a.Allocate(8);
+ y = a.Allocate(8);
+ EXPECT_EQ(uintptr_t(x) + 8, uintptr_t(y));
+}
+
+TEST(ArenaAllocator, AllocationsPerChunk)
+{
+ // Test that expected number of allocations fit in one chunk.
+ // We use an alignment of 64-bytes to avoid worrying about differences in
+ // the header size on 32 and 64-bit platforms.
+ const size_t kArenaSize = 1024;
+ const size_t kAlignment = 64;
+ ArenaAllocator<kArenaSize, kAlignment> a;
+
+ // With an alignment of 64 bytes we expect the header to take up the first
+ // alignment sized slot leaving bytes leaving the rest available for
+ // allocation.
+ const size_t kAllocationsPerChunk = (kArenaSize / kAlignment) - 1;
+ void* x = nullptr;
+ void* y = a.Allocate(kAlignment);
+ EXPECT_TRUE(y);
+ for (size_t i = 1; i < kAllocationsPerChunk; i++) {
+ x = y;
+ y = a.Allocate(kAlignment);
+ EXPECT_EQ(uintptr_t(x) + kAlignment, uintptr_t(y));
+ }
+
+ // The next allocation should be in a different chunk.
+ x = y;
+ y = a.Allocate(kAlignment);
+ EXPECT_NE(uintptr_t(x) + kAlignment, uintptr_t(y));
+}
+
+TEST(ArenaAllocator, MemoryIsValid)
+{
+ // Make multiple allocations and actually access the memory. This is
+ // expected to trip up ASAN or valgrind if out of bounds memory is
+ // accessed.
+ static const size_t kArenaSize = 1024;
+ static const size_t kAlignment = 64;
+ static const char kMark = char(0xBC);
+ ArenaAllocator<kArenaSize, kAlignment> a;
+
+ // Single allocation that should fill the arena.
+ size_t sz = kArenaSize - kAlignment;
+ char* x = (char*)a.Allocate(sz);
+ EXPECT_EQ(uintptr_t(x) % kAlignment, uintptr_t(0));
+ memset(x, kMark, sz);
+ for (size_t i = 0; i < sz; i++) {
+ EXPECT_EQ(x[i], kMark);
+ }
+
+ // Allocation over arena size.
+ sz = kArenaSize * 2;
+ x = (char*)a.Allocate(sz);
+ EXPECT_EQ(uintptr_t(x) % kAlignment, uintptr_t(0));
+ memset(x, kMark, sz);
+ for (size_t i = 0; i < sz; i++) {
+ EXPECT_EQ(x[i], kMark);
+ }
+
+ // Allocation half the arena size.
+ sz = kArenaSize / 2;
+ x = (char*)a.Allocate(sz);
+ EXPECT_EQ(uintptr_t(x) % kAlignment, uintptr_t(0));
+ memset(x, kMark, sz);
+ for (size_t i = 0; i < sz; i++) {
+ EXPECT_EQ(x[i], kMark);
+ }
+
+ // Repeat, this should actually end up in a new chunk.
+ x = (char*)a.Allocate(sz);
+ EXPECT_EQ(uintptr_t(x) % kAlignment, uintptr_t(0));
+ memset(x, kMark, sz);
+ for (size_t i = 0; i < sz; i++) {
+ EXPECT_EQ(x[i], kMark);
+ }
+}
+
+MOZ_DEFINE_MALLOC_SIZE_OF(TestSizeOf);
+
+TEST(ArenaAllocator, SizeOf)
+{
+ // This tests the sizeof functionality. We can't test for equality as we
+ // can't reliably guarantee what sizes the underlying allocator is going to
+ // choose, so we just test that things grow (or not) as expected.
+ static const size_t kArenaSize = 4096;
+ ArenaAllocator<kArenaSize> a;
+
+ // Excluding *this we expect an empty arena allocator to have no overhead.
+ size_t sz = a.SizeOfExcludingThis(TestSizeOf);
+ EXPECT_EQ(sz, size_t(0));
+
+ // Cause one chunk to be allocated.
+ (void)a.Allocate(kArenaSize / 2);
+ size_t prev_sz = sz;
+ sz = a.SizeOfExcludingThis(TestSizeOf);
+ EXPECT_GT(sz, prev_sz);
+
+ // Allocate within the current chunk.
+ (void)a.Allocate(kArenaSize / 4);
+ prev_sz = sz;
+ sz = a.SizeOfExcludingThis(TestSizeOf);
+ EXPECT_EQ(sz, prev_sz);
+
+ // Overflow to a new chunk.
+ (void)a.Allocate(kArenaSize / 2);
+ prev_sz = sz;
+ sz = a.SizeOfExcludingThis(TestSizeOf);
+ EXPECT_GT(sz, prev_sz);
+
+ // Allocate an oversized chunk with enough room for a header to fit in page
+ // size. We expect the underlying allocator to round up to page alignment.
+ (void)a.Allocate((kArenaSize * 2) - 64);
+ sz = a.SizeOfExcludingThis(TestSizeOf);
+ EXPECT_GT(sz, prev_sz);
+}
+
+TEST(ArenaAllocator, Clear)
+{
+ // Tests that the Clear function works as expected. The best proxy for
+ // checking if a clear is successful is to measure the size. If it's empty we
+ // expect the size to be 0.
+ static const size_t kArenaSize = 128;
+ ArenaAllocator<kArenaSize> a;
+
+ // Clearing an empty arena should work.
+ a.Clear();
+
+ size_t sz = a.SizeOfExcludingThis(TestSizeOf);
+ EXPECT_EQ(sz, size_t(0));
+
+ // Allocating should work after clearing an empty arena.
+ void* x = a.Allocate(10);
+ EXPECT_TRUE(x);
+
+ size_t prev_sz = sz;
+ sz = a.SizeOfExcludingThis(TestSizeOf);
+ EXPECT_GT(sz, prev_sz);
+
+ // Allocate enough for a few arena chunks to be necessary.
+ for (size_t i = 0; i < kArenaSize * 2; i++) {
+ x = a.Allocate(1);
+ EXPECT_TRUE(x);
+ }
+
+ prev_sz = sz;
+ sz = a.SizeOfExcludingThis(TestSizeOf);
+ EXPECT_GT(sz, prev_sz);
+
+ // Clearing should reduce the size back to zero.
+ a.Clear();
+ sz = a.SizeOfExcludingThis(TestSizeOf);
+ EXPECT_EQ(sz, size_t(0));
+
+ // Allocating should work after clearing an arena with allocations.
+ x = a.Allocate(10);
+ EXPECT_TRUE(x);
+
+ prev_sz = sz;
+ sz = a.SizeOfExcludingThis(TestSizeOf);
+ EXPECT_GT(sz, prev_sz);
+}
+
+TEST(ArenaAllocator, Extensions)
+{
+ ArenaAllocator<4096, 8> a;
+
+ // Test with raw strings.
+ const char* const kTestCStr = "This is a test string.";
+ char* c_dup = mozilla::ArenaStrdup(kTestCStr, a);
+ EXPECT_STREQ(c_dup, kTestCStr);
+
+ const char16_t* const kTestStr = u"This is a wide test string.";
+ char16_t* dup = mozilla::ArenaStrdup(kTestStr, a);
+ EXPECT_TRUE(nsString(dup).Equals(kTestStr));
+
+ // Make sure it works with literal strings.
+ constexpr auto wideStr = u"A wide string."_ns;
+ nsLiteralString::char_type* wide = mozilla::ArenaStrdup(wideStr, a);
+ EXPECT_TRUE(wideStr.Equals(wide));
+
+ constexpr auto cStr = "A c-string."_ns;
+ nsLiteralCString::char_type* cstr = mozilla::ArenaStrdup(cStr, a);
+ EXPECT_TRUE(cStr.Equals(cstr));
+
+ // Make sure it works with normal strings.
+ nsAutoString x(u"testing wide");
+ nsAutoString::char_type* x_copy = mozilla::ArenaStrdup(x, a);
+ EXPECT_TRUE(x.Equals(x_copy));
+
+ nsAutoCString y("testing c-string");
+ nsAutoCString::char_type* y_copy = mozilla::ArenaStrdup(y, a);
+ EXPECT_TRUE(y.Equals(y_copy));
+}