tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TestSegmentedVector.cpp (11930B)


      1 /* -*- Mode: C++; tab-width: 9; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 // This is included first to ensure it doesn't implicitly depend on anything
      8 // else.
      9 #include "mozilla/SegmentedVector.h"
     10 
     11 #include "mozilla/Alignment.h"
     12 #include "mozilla/Assertions.h"
     13 #include "mozilla/CheckedArithmetic.h"
     14 
     15 using mozilla::SegmentedVector;
     16 
     17 // It would be nice if we could use the InfallibleAllocPolicy from mozalloc,
     18 // but MFBT cannot use mozalloc.
     19 class InfallibleAllocPolicy {
     20 public:
     21  template <typename T>
     22  T* pod_malloc(size_t aNumElems) {
     23    size_t size;
     24    if (!mozilla::SafeMul(aNumElems, sizeof(T), &size)) {
     25      MOZ_CRASH("TestSegmentedVector.cpp: overflow");
     26    }
     27    T* rv = static_cast<T*>(malloc(size));
     28    if (!rv) {
     29      MOZ_CRASH("TestSegmentedVector.cpp: out of memory");
     30    }
     31    return rv;
     32  }
     33 
     34  template <typename T>
     35  void free_(T* aPtr, size_t aNumElems = 0) {
     36    free(aPtr);
     37  }
     38 };
     39 
     40 template <typename Vector>
     41 void CheckContents(Vector& vector, size_t expectedLength) {
     42  MOZ_RELEASE_ASSERT(vector.Length() == expectedLength);
     43  size_t n = 0;
     44  for (auto iter = vector.Iter(); !iter.Done(); iter.Next()) {
     45    MOZ_RELEASE_ASSERT(iter.Get() == int(n));
     46    n++;
     47  }
     48  MOZ_RELEASE_ASSERT(n == expectedLength);
     49 }
     50 
     51 // We want to test Append(), which is fallible and marked with
     52 // [[nodiscard]]. But we're using an infallible alloc policy, and so
     53 // don't really need to check the result. Casting to |void| works with clang
     54 // but not GCC, so we instead use this dummy variable which works with both
     55 // compilers.
     56 static int gDummy;
     57 
     58 // This tests basic segmented vector construction and iteration.
     59 void TestBasics() {
     60  // A SegmentedVector with a POD element type.
     61  typedef SegmentedVector<int, 1024, InfallibleAllocPolicy> MyVector;
     62  MyVector v;
     63  int i;
     64 
     65  MOZ_RELEASE_ASSERT(v.IsEmpty());
     66 
     67  // Add 100 elements, then check various things.
     68  i = 0;
     69  for (; i < 100; i++) {
     70    gDummy = v.Append(std::move(i));
     71  }
     72  MOZ_RELEASE_ASSERT(!v.IsEmpty());
     73  CheckContents(v, 100);
     74 
     75  // Add another 900 elements, then re-check.
     76  for (; i < 1000; i++) {
     77    v.InfallibleAppend(std::move(i));
     78  }
     79  MOZ_RELEASE_ASSERT(!v.IsEmpty());
     80  CheckContents(v, 1000);
     81 
     82  // Pop off all of the elements.
     83  MOZ_RELEASE_ASSERT(v.Length() == 1000);
     84  for (int len = (int)v.Length(); len > 0; len--) {
     85    MOZ_RELEASE_ASSERT(v.GetLast() == len - 1);
     86    v.PopLast();
     87  }
     88  MOZ_RELEASE_ASSERT(v.IsEmpty());
     89  MOZ_RELEASE_ASSERT(v.Length() == 0);
     90 
     91  // Fill the vector up again to prepare for the clear.
     92  for (i = 0; i < 1000; i++) {
     93    v.InfallibleAppend(std::move(i));
     94  }
     95  MOZ_RELEASE_ASSERT(!v.IsEmpty());
     96  MOZ_RELEASE_ASSERT(v.Length() == 1000);
     97 
     98  v.Clear();
     99  MOZ_RELEASE_ASSERT(v.IsEmpty());
    100  MOZ_RELEASE_ASSERT(v.Length() == 0);
    101 
    102  // Fill the vector up to verify PopLastN works.
    103  for (i = 0; i < 1000; ++i) {
    104    v.InfallibleAppend(std::move(i));
    105  }
    106  MOZ_RELEASE_ASSERT(!v.IsEmpty());
    107  MOZ_RELEASE_ASSERT(v.Length() == 1000);
    108 
    109  // Verify we pop the right amount of elements.
    110  v.PopLastN(300);
    111  MOZ_RELEASE_ASSERT(v.Length() == 700);
    112 
    113  // Verify the contents are what we expect.
    114  CheckContents(v, 700);
    115 
    116  // Verify PopLastN can take larger than .Length() value as an argument.
    117  v.PopLastN(1000);
    118  MOZ_RELEASE_ASSERT(v.Length() == 0);
    119  MOZ_RELEASE_ASSERT(v.IsEmpty());
    120 
    121  // Fill the vector again.
    122  for (i = 0; i < 1000; ++i) {
    123    v.InfallibleAppend(std::move(i));
    124  }
    125  MOZ_RELEASE_ASSERT(!v.IsEmpty());
    126  MOZ_RELEASE_ASSERT(v.Length() == 1000);
    127 
    128  // Verify that calling PopLastN with Length() empties the vector.
    129  v.PopLastN(v.Length());
    130  MOZ_RELEASE_ASSERT(v.Length() == 0);
    131  MOZ_RELEASE_ASSERT(v.IsEmpty());
    132 }
    133 
    134 void TestMoveAndSwap() {
    135  typedef SegmentedVector<int, 32, InfallibleAllocPolicy> MyVector;
    136  MyVector v;
    137 
    138  for (int i = 0; i < 100; i++) {
    139    (void)v.Append(i);
    140  }
    141  MOZ_RELEASE_ASSERT(!v.IsEmpty());
    142  CheckContents(v, 100);
    143 
    144  // Test move constructor.
    145  MyVector w(std::move(v));
    146  CheckContents(w, 100);
    147  MOZ_RELEASE_ASSERT(v.IsEmpty());
    148 
    149  // Test move assignment.
    150  v = std::move(w);
    151  CheckContents(v, 100);
    152  MOZ_RELEASE_ASSERT(w.IsEmpty());
    153 
    154  // Test swap.
    155  std::swap(v, w);
    156  CheckContents(w, 100);
    157  MOZ_RELEASE_ASSERT(v.IsEmpty());
    158 }
    159 
    160 static size_t gNumDefaultCtors;
    161 static size_t gNumExplicitCtors;
    162 static size_t gNumCopyCtors;
    163 static size_t gNumMoveCtors;
    164 static size_t gNumDtors;
    165 
    166 struct NonPOD {
    167  NonPOD() { gNumDefaultCtors++; }
    168  explicit NonPOD(int x) { gNumExplicitCtors++; }
    169  NonPOD(NonPOD&) { gNumCopyCtors++; }
    170  NonPOD(NonPOD&&) { gNumMoveCtors++; }
    171  ~NonPOD() { gNumDtors++; }
    172 };
    173 
    174 // This tests how segmented vectors with non-POD elements construct and
    175 // destruct those elements.
    176 void TestConstructorsAndDestructors() {
    177  size_t defaultCtorCalls = 0;
    178  size_t explicitCtorCalls = 0;
    179  size_t copyCtorCalls = 0;
    180  size_t moveCtorCalls = 0;
    181  size_t dtorCalls = 0;
    182 
    183  {
    184    static const size_t segmentSize = 64;
    185 
    186    // A SegmentedVector with a non-POD element type.
    187    NonPOD x(1);  // explicit constructor called
    188    explicitCtorCalls++;
    189    SegmentedVector<NonPOD, segmentSize, InfallibleAllocPolicy> v;
    190    // default constructor called 0 times
    191    MOZ_RELEASE_ASSERT(v.IsEmpty());
    192    gDummy = v.Append(x);  // copy constructor called
    193    copyCtorCalls++;
    194    NonPOD y(1);  // explicit constructor called
    195    explicitCtorCalls++;
    196    gDummy = v.Append(std::move(y));  // move constructor called
    197    moveCtorCalls++;
    198    NonPOD z(1);  // explicit constructor called
    199    explicitCtorCalls++;
    200    v.InfallibleAppend(std::move(z));  // move constructor called
    201    moveCtorCalls++;
    202    v.PopLast();  // destructor called 1 time
    203    dtorCalls++;
    204    MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls);
    205    v.Clear();  // destructor called 2 times
    206    dtorCalls += 2;
    207 
    208    // Test that PopLastN() correctly calls the destructors of all the
    209    // elements in the segments it destroys.
    210    //
    211    // We depend on the size of NonPOD when determining how many things
    212    // to push onto the vector.  It would be nicer to get this information
    213    // from SegmentedVector itself...
    214    static_assert(sizeof(NonPOD) == 1, "Fix length calculations!");
    215 
    216    size_t nonFullLastSegmentSize = segmentSize - 1;
    217    for (size_t i = 0; i < nonFullLastSegmentSize; ++i) {
    218      gDummy = v.Append(x);  // copy constructor called
    219      copyCtorCalls++;
    220    }
    221    MOZ_RELEASE_ASSERT(gNumCopyCtors == copyCtorCalls);
    222 
    223    // Pop some of the elements.
    224    {
    225      size_t partialPopAmount = 5;
    226      MOZ_RELEASE_ASSERT(nonFullLastSegmentSize > partialPopAmount);
    227      v.PopLastN(partialPopAmount);  // destructor called partialPopAmount times
    228      dtorCalls += partialPopAmount;
    229      MOZ_RELEASE_ASSERT(v.Length() ==
    230                         nonFullLastSegmentSize - partialPopAmount);
    231      MOZ_RELEASE_ASSERT(!v.IsEmpty());
    232      MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls);
    233    }
    234 
    235    // Pop a full segment.
    236    {
    237      size_t length = v.Length();
    238      v.PopLastN(length);
    239      dtorCalls += length;
    240      // These two tests *are* semantically different given the underlying
    241      // implementation; Length sums up the sizes of the internal segments,
    242      // while IsEmpty looks at the sequence of internal segments.
    243      MOZ_RELEASE_ASSERT(v.Length() == 0);
    244      MOZ_RELEASE_ASSERT(v.IsEmpty());
    245      MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls);
    246    }
    247 
    248    size_t multipleSegmentsSize = (segmentSize * 3) / 2;
    249    for (size_t i = 0; i < multipleSegmentsSize; ++i) {
    250      gDummy = v.Append(x);  // copy constructor called
    251      copyCtorCalls++;
    252    }
    253    MOZ_RELEASE_ASSERT(gNumCopyCtors == copyCtorCalls);
    254 
    255    // Pop across segment boundaries.
    256    {
    257      v.PopLastN(segmentSize);
    258      dtorCalls += segmentSize;
    259      MOZ_RELEASE_ASSERT(v.Length() == (multipleSegmentsSize - segmentSize));
    260      MOZ_RELEASE_ASSERT(!v.IsEmpty());
    261      MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls);
    262    }
    263 
    264    // Clear everything here to make calculations easier.
    265    {
    266      size_t length = v.Length();
    267      v.Clear();
    268      dtorCalls += length;
    269      MOZ_RELEASE_ASSERT(v.IsEmpty());
    270      MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls);
    271    }
    272 
    273    MOZ_RELEASE_ASSERT(gNumDefaultCtors == defaultCtorCalls);
    274    MOZ_RELEASE_ASSERT(gNumExplicitCtors == explicitCtorCalls);
    275    MOZ_RELEASE_ASSERT(gNumCopyCtors == copyCtorCalls);
    276    MOZ_RELEASE_ASSERT(gNumMoveCtors == moveCtorCalls);
    277    MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls);
    278  }  // destructor called for x, y, z
    279  dtorCalls += 3;
    280  MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls);
    281 }
    282 
    283 struct A {
    284  int mX;
    285  int mY;
    286 };
    287 struct B {
    288  int mX;
    289  char mY;
    290  double mZ;
    291 };
    292 struct C {
    293  A mA;
    294  B mB;
    295 };
    296 struct D {
    297  char mBuf[101];
    298 };
    299 struct E {};
    300 
    301 // This tests that we get the right segment capacities for specified segment
    302 // sizes, and that the elements are aligned appropriately.
    303 void TestSegmentCapacitiesAndAlignments() {
    304  // When SegmentedVector's constructor is passed a size, it asserts that the
    305  // vector's segment capacity results in a segment size equal to (or very
    306  // close to) the passed size.
    307  //
    308  // Also, SegmentedVector has a static assertion that elements are
    309  // appropriately aligned.
    310  SegmentedVector<double, 512> v1(512);
    311  SegmentedVector<A, 1024> v2(1024);
    312  SegmentedVector<B, 999> v3(999);
    313  SegmentedVector<C, 10> v4(10);
    314  SegmentedVector<D, 1234> v5(1234);
    315  SegmentedVector<E> v6(4096);  // 4096 is the default segment size
    316  SegmentedVector<mozilla::AlignedElem<16>, 100> v7(100);
    317 }
    318 
    319 void TestIterator() {
    320  SegmentedVector<int, 4> v;
    321 
    322  auto iter = v.Iter();
    323  auto iterFromLast = v.IterFromLast();
    324  MOZ_RELEASE_ASSERT(iter.Done());
    325  MOZ_RELEASE_ASSERT(iterFromLast.Done());
    326 
    327  gDummy = v.Append(1);
    328  iter = v.Iter();
    329  iterFromLast = v.IterFromLast();
    330  MOZ_RELEASE_ASSERT(!iter.Done());
    331  MOZ_RELEASE_ASSERT(!iterFromLast.Done());
    332 
    333  iter.Next();
    334  MOZ_RELEASE_ASSERT(iter.Done());
    335  iterFromLast.Next();
    336  MOZ_RELEASE_ASSERT(iterFromLast.Done());
    337 
    338  iter = v.Iter();
    339  iterFromLast = v.IterFromLast();
    340  MOZ_RELEASE_ASSERT(!iter.Done());
    341  MOZ_RELEASE_ASSERT(!iterFromLast.Done());
    342 
    343  iter.Prev();
    344  MOZ_RELEASE_ASSERT(iter.Done());
    345  iterFromLast.Prev();
    346  MOZ_RELEASE_ASSERT(iterFromLast.Done());
    347 
    348  // Append enough entries to ensure we have at least two segments.
    349  gDummy = v.Append(1);
    350  gDummy = v.Append(1);
    351  gDummy = v.Append(1);
    352  gDummy = v.Append(1);
    353 
    354  iter = v.Iter();
    355  iterFromLast = v.IterFromLast();
    356  MOZ_RELEASE_ASSERT(!iter.Done());
    357  MOZ_RELEASE_ASSERT(!iterFromLast.Done());
    358 
    359  iter.Prev();
    360  MOZ_RELEASE_ASSERT(iter.Done());
    361  iterFromLast.Next();
    362  MOZ_RELEASE_ASSERT(iterFromLast.Done());
    363 
    364  iter = v.Iter();
    365  iterFromLast = v.IterFromLast();
    366  MOZ_RELEASE_ASSERT(!iter.Done());
    367  MOZ_RELEASE_ASSERT(!iterFromLast.Done());
    368 
    369  iter.Next();
    370  MOZ_RELEASE_ASSERT(!iter.Done());
    371  iterFromLast.Prev();
    372  MOZ_RELEASE_ASSERT(!iterFromLast.Done());
    373 
    374  iter = v.Iter();
    375  iterFromLast = v.IterFromLast();
    376  int count = 0;
    377  for (; !iter.Done() && !iterFromLast.Done();
    378       iter.Next(), iterFromLast.Prev()) {
    379    ++count;
    380  }
    381  MOZ_RELEASE_ASSERT(count == 5);
    382 
    383  // Modify the vector while using the iterator.
    384  iterFromLast = v.IterFromLast();
    385  gDummy = v.Append(2);
    386  gDummy = v.Append(3);
    387  gDummy = v.Append(4);
    388  iterFromLast.Next();
    389  MOZ_RELEASE_ASSERT(!iterFromLast.Done());
    390  MOZ_RELEASE_ASSERT(iterFromLast.Get() == 2);
    391  iterFromLast.Next();
    392  MOZ_RELEASE_ASSERT(iterFromLast.Get() == 3);
    393  iterFromLast.Next();
    394  MOZ_RELEASE_ASSERT(iterFromLast.Get() == 4);
    395  iterFromLast.Next();
    396  MOZ_RELEASE_ASSERT(iterFromLast.Done());
    397 }
    398 
    399 int main(void) {
    400  TestBasics();
    401  TestMoveAndSwap();
    402  TestConstructorsAndDestructors();
    403  TestSegmentCapacitiesAndAlignments();
    404  TestIterator();
    405 
    406  return 0;
    407 }