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 }