gecko-dev/mfbt/tests/TestSegmentedVector.cpp

180 lines
5.6 KiB
C++

/* -*- Mode: C++; tab-width: 9; 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/. */
// This is included first to ensure it doesn't implicitly depend on anything
// else.
#include "mozilla/SegmentedVector.h"
#include "mozilla/Alignment.h"
#include "mozilla/Assertions.h"
using mozilla::SegmentedVector;
// It would be nice if we could use the InfallibleAllocPolicy from mozalloc,
// but MFBT cannot use mozalloc.
class InfallibleAllocPolicy
{
public:
template <typename T>
T* pod_malloc(size_t aNumElems)
{
if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
MOZ_CRASH("TestSegmentedVector.cpp: overflow");
}
T* rv = static_cast<T*>(malloc(aNumElems * sizeof(T)));
if (!rv) {
MOZ_CRASH("TestSegmentedVector.cpp: out of memory");
}
return rv;
}
void free_(void* aPtr) { free(aPtr); }
};
// We want to test Append(), which is fallible and marked with
// MOZ_WARN_UNUSED_RESULT. But we're using an infallible alloc policy, and so
// don't really need to check the result. Casting to |void| works with clang
// but not GCC, so we instead use this dummy variable which works with both
// compilers.
static int gDummy;
// This tests basic segmented vector construction and iteration.
void TestBasics()
{
// A SegmentedVector with a POD element type.
typedef SegmentedVector<int, 1024, InfallibleAllocPolicy> MyVector;
MyVector v;
int i, n;
MOZ_RELEASE_ASSERT(v.IsEmpty());
// Add 100 elements, then check various things.
i = 0;
for ( ; i < 100; i++) {
gDummy = v.Append(mozilla::Move(i));
}
MOZ_RELEASE_ASSERT(!v.IsEmpty());
MOZ_RELEASE_ASSERT(v.Length() == 100);
n = 0;
for (auto iter = v.Iter(); !iter.Done(); iter.Next()) {
MOZ_RELEASE_ASSERT(iter.Get() == n);
n++;
}
MOZ_RELEASE_ASSERT(n == 100);
// Add another 900 elements, then re-check.
for ( ; i < 1000; i++) {
v.InfallibleAppend(mozilla::Move(i));
}
MOZ_RELEASE_ASSERT(!v.IsEmpty());
MOZ_RELEASE_ASSERT(v.Length() == 1000);
n = 0;
for (auto iter = v.Iter(); !iter.Done(); iter.Next()) {
MOZ_RELEASE_ASSERT(iter.Get() == n);
n++;
}
MOZ_RELEASE_ASSERT(n == 1000);
// Pop off all of the elements.
MOZ_RELEASE_ASSERT(v.Length() == 1000);
for (int len = (int)v.Length(); len > 0; len--) {
MOZ_RELEASE_ASSERT(v.GetLast() == len - 1);
v.PopLast();
}
MOZ_RELEASE_ASSERT(v.IsEmpty());
MOZ_RELEASE_ASSERT(v.Length() == 0);
// Fill the vector up again to prepare for the clear.
for (i = 0; i < 1000; i++) {
v.InfallibleAppend(mozilla::Move(i));
}
MOZ_RELEASE_ASSERT(!v.IsEmpty());
MOZ_RELEASE_ASSERT(v.Length() == 1000);
v.Clear();
MOZ_RELEASE_ASSERT(v.IsEmpty());
MOZ_RELEASE_ASSERT(v.Length() == 0);
}
static size_t gNumDefaultCtors;
static size_t gNumExplicitCtors;
static size_t gNumCopyCtors;
static size_t gNumMoveCtors;
static size_t gNumDtors;
struct NonPOD
{
NonPOD() { gNumDefaultCtors++; }
explicit NonPOD(int x) { gNumExplicitCtors++; }
NonPOD(NonPOD&) { gNumCopyCtors++; }
NonPOD(NonPOD&&) { gNumMoveCtors++; }
~NonPOD() { gNumDtors++; }
};
// This tests how segmented vectors with non-POD elements construct and
// destruct those elements.
void TestConstructorsAndDestructors()
{
{
// A SegmentedVector with a non-POD element type.
NonPOD x(1); // explicit constructor called
SegmentedVector<NonPOD, 64, InfallibleAllocPolicy> v;
// default constructor called 0 times
MOZ_RELEASE_ASSERT(v.IsEmpty());
gDummy = v.Append(x); // copy constructor called
NonPOD y(1); // explicit constructor called
gDummy = v.Append(mozilla::Move(y)); // move constructor called
NonPOD z(1); // explicit constructor called
v.InfallibleAppend(mozilla::Move(z)); // move constructor called
v.PopLast(); // destructor called 1 time
MOZ_RELEASE_ASSERT(gNumDtors == 1);
v.Clear(); // destructor called 2 times
MOZ_RELEASE_ASSERT(gNumDefaultCtors == 0);
MOZ_RELEASE_ASSERT(gNumExplicitCtors == 3);
MOZ_RELEASE_ASSERT(gNumCopyCtors == 1);
MOZ_RELEASE_ASSERT(gNumMoveCtors == 2);
MOZ_RELEASE_ASSERT(gNumDtors == 3);
} // destructor called for x, y, z
MOZ_RELEASE_ASSERT(gNumDtors == 6);
}
struct A { int mX; int mY; };
struct B { int mX; char mY; double mZ; };
struct C { A mA; B mB; };
struct D { char mBuf[101]; };
struct E { };
// This tests that we get the right segment capacities for specified segment
// sizes, and that the elements are aligned appropriately.
void TestSegmentCapacitiesAndAlignments()
{
// When SegmentedVector's constructor is passed a size, it asserts that the
// vector's segment capacity results in a segment size equal to (or very
// close to) the passed size.
//
// Also, SegmentedVector has a static assertion that elements are
// appropriately aligned.
SegmentedVector<double, 512> v1(512);
SegmentedVector<A, 1024> v2(1024);
SegmentedVector<B, 999> v3(999);
SegmentedVector<C, 10> v4(10);
SegmentedVector<D, 1234> v5(1234);
SegmentedVector<E> v6(4096); // 4096 is the default segment size
SegmentedVector<mozilla::AlignedElem<16>, 100> v7(100);
}
int main(void)
{
TestBasics();
TestConstructorsAndDestructors();
TestSegmentCapacitiesAndAlignments();
return 0;
}