summaryrefslogtreecommitdiffstats
path: root/mfbt/tests
diff options
context:
space:
mode:
Diffstat (limited to 'mfbt/tests')
-rw-r--r--mfbt/tests/TestArray.cpp34
-rw-r--r--mfbt/tests/TestArrayUtils.cpp313
-rw-r--r--mfbt/tests/TestAtomics.cpp295
-rw-r--r--mfbt/tests/TestBinarySearch.cpp113
-rw-r--r--mfbt/tests/TestBloomFilter.cpp103
-rw-r--r--mfbt/tests/TestBufferList.cpp256
-rw-r--r--mfbt/tests/TestCasting.cpp112
-rw-r--r--mfbt/tests/TestCeilingFloor.cpp89
-rw-r--r--mfbt/tests/TestCheckedInt.cpp626
-rw-r--r--mfbt/tests/TestCountPopulation.cpp34
-rw-r--r--mfbt/tests/TestCountZeroes.cpp102
-rw-r--r--mfbt/tests/TestEndian.cpp472
-rw-r--r--mfbt/tests/TestEnumSet.cpp289
-rw-r--r--mfbt/tests/TestEnumTypeTraits.cpp136
-rw-r--r--mfbt/tests/TestEnumeratedArray.cpp42
-rw-r--r--mfbt/tests/TestFastBernoulliTrial.cpp209
-rw-r--r--mfbt/tests/TestFloatingPoint.cpp592
-rw-r--r--mfbt/tests/TestFunction.cpp115
-rw-r--r--mfbt/tests/TestIntegerPrintfMacros.cpp1269
-rw-r--r--mfbt/tests/TestIntegerRange.cpp163
-rw-r--r--mfbt/tests/TestJSONWriter.cpp539
-rw-r--r--mfbt/tests/TestLinkedList.cpp268
-rw-r--r--mfbt/tests/TestMacroArgs.cpp31
-rw-r--r--mfbt/tests/TestMacroForEach.cpp35
-rw-r--r--mfbt/tests/TestMathAlgorithms.cpp87
-rw-r--r--mfbt/tests/TestMaybe.cpp860
-rw-r--r--mfbt/tests/TestNotNull.cpp314
-rw-r--r--mfbt/tests/TestPair.cpp83
-rw-r--r--mfbt/tests/TestPoisonArea.cpp549
-rw-r--r--mfbt/tests/TestRange.cpp23
-rw-r--r--mfbt/tests/TestRefPtr.cpp139
-rw-r--r--mfbt/tests/TestRollingMean.cpp129
-rw-r--r--mfbt/tests/TestSHA1.cpp208
-rw-r--r--mfbt/tests/TestSaturate.cpp215
-rw-r--r--mfbt/tests/TestScopeExit.cpp54
-rw-r--r--mfbt/tests/TestSegmentedVector.cpp279
-rw-r--r--mfbt/tests/TestSplayTree.cpp212
-rw-r--r--mfbt/tests/TestTemplateLib.cpp35
-rw-r--r--mfbt/tests/TestTuple.cpp296
-rw-r--r--mfbt/tests/TestTypeTraits.cpp660
-rw-r--r--mfbt/tests/TestTypedEnum.cpp556
-rw-r--r--mfbt/tests/TestUniquePtr.cpp592
-rw-r--r--mfbt/tests/TestVariant.cpp188
-rw-r--r--mfbt/tests/TestVector.cpp358
-rw-r--r--mfbt/tests/TestWeakPtr.cpp123
-rw-r--r--mfbt/tests/TestXorShift128PlusRNG.cpp115
-rw-r--r--mfbt/tests/moz.build76
47 files changed, 12388 insertions, 0 deletions
diff --git a/mfbt/tests/TestArray.cpp b/mfbt/tests/TestArray.cpp
new file mode 100644
index 000000000..bdaeed825
--- /dev/null
+++ b/mfbt/tests/TestArray.cpp
@@ -0,0 +1,34 @@
+/* -*- 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/Array.h"
+
+void TestInitialValueByConstructor()
+{
+ using namespace mozilla;
+ // Style 1
+ Array<int32_t, 3> arr1(1, 2, 3);
+ MOZ_RELEASE_ASSERT(arr1[0] == 1);
+ MOZ_RELEASE_ASSERT(arr1[1] == 2);
+ MOZ_RELEASE_ASSERT(arr1[2] == 3);
+ // Style 2
+ Array<int32_t, 3> arr2{5, 6, 7};
+ MOZ_RELEASE_ASSERT(arr2[0] == 5);
+ MOZ_RELEASE_ASSERT(arr2[1] == 6);
+ MOZ_RELEASE_ASSERT(arr2[2] == 7);
+ // Style 3
+ Array<int32_t, 3> arr3({8, 9, 10});
+ MOZ_RELEASE_ASSERT(arr3[0] == 8);
+ MOZ_RELEASE_ASSERT(arr3[1] == 9);
+ MOZ_RELEASE_ASSERT(arr3[2] == 10);
+}
+
+int
+main()
+{
+ TestInitialValueByConstructor();
+ return 0;
+} \ No newline at end of file
diff --git a/mfbt/tests/TestArrayUtils.cpp b/mfbt/tests/TestArrayUtils.cpp
new file mode 100644
index 000000000..6566a3cd8
--- /dev/null
+++ b/mfbt/tests/TestArrayUtils.cpp
@@ -0,0 +1,313 @@
+/* -*- 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/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+
+using mozilla::IsInRange;
+
+static void
+TestIsInRangeNonClass()
+{
+ void* nul = nullptr;
+ int* intBegin = nullptr;
+ int* intEnd = intBegin + 1;
+ int* intEnd2 = intBegin + 2;
+
+ MOZ_RELEASE_ASSERT(IsInRange(nul, intBegin, intEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(nul, intEnd, intEnd2));
+
+ MOZ_RELEASE_ASSERT(IsInRange(intBegin, intBegin, intEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(intEnd, intBegin, intEnd));
+
+ MOZ_RELEASE_ASSERT(IsInRange(intBegin, intBegin, intEnd2));
+ MOZ_RELEASE_ASSERT(IsInRange(intEnd, intBegin, intEnd2));
+ MOZ_RELEASE_ASSERT(!IsInRange(intEnd2, intBegin, intEnd2));
+
+ uintptr_t uintBegin = uintptr_t(intBegin);
+ uintptr_t uintEnd = uintptr_t(intEnd);
+ uintptr_t uintEnd2 = uintptr_t(intEnd2);
+
+ MOZ_RELEASE_ASSERT(IsInRange(nul, uintBegin, uintEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(nul, uintEnd, uintEnd2));
+
+ MOZ_RELEASE_ASSERT(IsInRange(intBegin, uintBegin, uintEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(intEnd, uintBegin, uintEnd));
+
+ MOZ_RELEASE_ASSERT(IsInRange(intBegin, uintBegin, uintEnd2));
+ MOZ_RELEASE_ASSERT(IsInRange(intEnd, uintBegin, uintEnd2));
+ MOZ_RELEASE_ASSERT(!IsInRange(intEnd2, uintBegin, uintEnd2));
+}
+
+static void
+TestIsInRangeVoid()
+{
+ int* intBegin = nullptr;
+ int* intEnd = intBegin + 1;
+ int* intEnd2 = intBegin + 2;
+
+ void* voidBegin = intBegin;
+ void* voidEnd = intEnd;
+ void* voidEnd2 = intEnd2;
+
+ MOZ_RELEASE_ASSERT(IsInRange(voidBegin, intBegin, intEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(voidEnd, intBegin, intEnd));
+
+ MOZ_RELEASE_ASSERT(IsInRange(voidBegin, voidBegin, voidEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(voidEnd, voidBegin, voidEnd));
+
+ MOZ_RELEASE_ASSERT(IsInRange(voidBegin, intBegin, intEnd2));
+ MOZ_RELEASE_ASSERT(IsInRange(voidEnd, intBegin, intEnd2));
+ MOZ_RELEASE_ASSERT(!IsInRange(voidEnd2, intBegin, intEnd2));
+
+ MOZ_RELEASE_ASSERT(IsInRange(voidBegin, voidBegin, voidEnd2));
+ MOZ_RELEASE_ASSERT(IsInRange(voidEnd, voidBegin, voidEnd2));
+ MOZ_RELEASE_ASSERT(!IsInRange(voidEnd2, voidBegin, voidEnd2));
+
+ uintptr_t uintBegin = uintptr_t(intBegin);
+ uintptr_t uintEnd = uintptr_t(intEnd);
+ uintptr_t uintEnd2 = uintptr_t(intEnd2);
+
+ MOZ_RELEASE_ASSERT(IsInRange(voidBegin, uintBegin, uintEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(voidEnd, uintBegin, uintEnd));
+
+ MOZ_RELEASE_ASSERT(IsInRange(voidBegin, uintBegin, uintEnd2));
+ MOZ_RELEASE_ASSERT(IsInRange(voidEnd, uintBegin, uintEnd2));
+ MOZ_RELEASE_ASSERT(!IsInRange(voidEnd2, uintBegin, uintEnd2));
+}
+
+struct Base { int mX; };
+
+static void
+TestIsInRangeClass()
+{
+ void* nul = nullptr;
+ Base* baseBegin = nullptr;
+ Base* baseEnd = baseBegin + 1;
+ Base* baseEnd2 = baseBegin + 2;
+
+ MOZ_RELEASE_ASSERT(IsInRange(nul, baseBegin, baseEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(nul, baseEnd, baseEnd2));
+
+ MOZ_RELEASE_ASSERT(IsInRange(baseBegin, baseBegin, baseEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(baseEnd, baseBegin, baseEnd));
+
+ MOZ_RELEASE_ASSERT(IsInRange(baseBegin, baseBegin, baseEnd2));
+ MOZ_RELEASE_ASSERT(IsInRange(baseEnd, baseBegin, baseEnd2));
+ MOZ_RELEASE_ASSERT(!IsInRange(baseEnd2, baseBegin, baseEnd2));
+
+ uintptr_t ubaseBegin = uintptr_t(baseBegin);
+ uintptr_t ubaseEnd = uintptr_t(baseEnd);
+ uintptr_t ubaseEnd2 = uintptr_t(baseEnd2);
+
+ MOZ_RELEASE_ASSERT(IsInRange(nul, ubaseBegin, ubaseEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(nul, ubaseEnd, ubaseEnd2));
+
+ MOZ_RELEASE_ASSERT(IsInRange(baseBegin, ubaseBegin, ubaseEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(baseEnd, ubaseBegin, ubaseEnd));
+
+ MOZ_RELEASE_ASSERT(IsInRange(baseBegin, ubaseBegin, ubaseEnd2));
+ MOZ_RELEASE_ASSERT(IsInRange(baseEnd, ubaseBegin, ubaseEnd2));
+ MOZ_RELEASE_ASSERT(!IsInRange(baseEnd2, ubaseBegin, ubaseEnd2));
+}
+
+struct EmptyBase {};
+
+static void
+TestIsInRangeEmptyClass()
+{
+ void* nul = nullptr;
+ EmptyBase* baseBegin = nullptr;
+ EmptyBase* baseEnd = baseBegin + 1;
+ EmptyBase* baseEnd2 = baseBegin + 2;
+
+ MOZ_RELEASE_ASSERT(IsInRange(nul, baseBegin, baseEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(nul, baseEnd, baseEnd2));
+
+ MOZ_RELEASE_ASSERT(IsInRange(baseBegin, baseBegin, baseEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(baseEnd, baseBegin, baseEnd));
+
+ MOZ_RELEASE_ASSERT(IsInRange(baseBegin, baseBegin, baseEnd2));
+ MOZ_RELEASE_ASSERT(IsInRange(baseEnd, baseBegin, baseEnd2));
+ MOZ_RELEASE_ASSERT(!IsInRange(baseEnd2, baseBegin, baseEnd2));
+
+ uintptr_t ubaseBegin = uintptr_t(baseBegin);
+ uintptr_t ubaseEnd = uintptr_t(baseEnd);
+ uintptr_t ubaseEnd2 = uintptr_t(baseEnd2);
+
+ MOZ_RELEASE_ASSERT(IsInRange(nul, ubaseBegin, ubaseEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(nul, ubaseEnd, ubaseEnd2));
+
+ MOZ_RELEASE_ASSERT(IsInRange(baseBegin, ubaseBegin, ubaseEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(baseEnd, ubaseBegin, ubaseEnd));
+
+ MOZ_RELEASE_ASSERT(IsInRange(baseBegin, ubaseBegin, ubaseEnd2));
+ MOZ_RELEASE_ASSERT(IsInRange(baseEnd, ubaseBegin, ubaseEnd2));
+ MOZ_RELEASE_ASSERT(!IsInRange(baseEnd2, ubaseBegin, ubaseEnd2));
+}
+
+struct Derived : Base {};
+
+static void
+TestIsInRangeClassDerived()
+{
+ void* nul = nullptr;
+ Derived* derivedBegin = nullptr;
+ Derived* derivedEnd = derivedBegin + 1;
+ Derived* derivedEnd2 = derivedBegin + 2;
+
+ Base* baseBegin = static_cast<Base*>(derivedBegin);
+ Base* baseEnd = static_cast<Base*>(derivedEnd);
+ Base* baseEnd2 = static_cast<Base*>(derivedEnd2);
+
+ MOZ_RELEASE_ASSERT(IsInRange(nul, derivedBegin, derivedEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(nul, derivedEnd, derivedEnd2));
+
+ MOZ_RELEASE_ASSERT(IsInRange(baseBegin, derivedBegin, derivedEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(baseEnd, derivedBegin, derivedEnd));
+
+ MOZ_RELEASE_ASSERT(IsInRange(baseBegin, derivedBegin, derivedEnd2));
+ MOZ_RELEASE_ASSERT(IsInRange(baseEnd, derivedBegin, derivedEnd2));
+ MOZ_RELEASE_ASSERT(!IsInRange(baseEnd2, derivedBegin, derivedEnd2));
+
+ uintptr_t uderivedBegin = uintptr_t(derivedBegin);
+ uintptr_t uderivedEnd = uintptr_t(derivedEnd);
+ uintptr_t uderivedEnd2 = uintptr_t(derivedEnd2);
+
+ MOZ_RELEASE_ASSERT(IsInRange(derivedBegin, uderivedBegin, uderivedEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(derivedEnd, uderivedBegin, uderivedEnd));
+
+ MOZ_RELEASE_ASSERT(IsInRange(derivedBegin, uderivedBegin, uderivedEnd2));
+ MOZ_RELEASE_ASSERT(IsInRange(derivedEnd, uderivedBegin, uderivedEnd2));
+ MOZ_RELEASE_ASSERT(!IsInRange(derivedEnd2, uderivedBegin, uderivedEnd2));
+}
+
+struct DerivedEmpty : EmptyBase {};
+
+static void
+TestIsInRangeClassDerivedEmpty()
+{
+ void* nul = nullptr;
+ DerivedEmpty* derivedEmptyBegin = nullptr;
+ DerivedEmpty* derivedEmptyEnd = derivedEmptyBegin + 1;
+ DerivedEmpty* derivedEmptyEnd2 = derivedEmptyBegin + 2;
+
+ EmptyBase* baseBegin = static_cast<EmptyBase*>(derivedEmptyBegin);
+ EmptyBase* baseEnd = static_cast<EmptyBase*>(derivedEmptyEnd);
+ EmptyBase* baseEnd2 = static_cast<EmptyBase*>(derivedEmptyEnd2);
+
+ MOZ_RELEASE_ASSERT(IsInRange(nul, derivedEmptyBegin, derivedEmptyEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(nul, derivedEmptyEnd, derivedEmptyEnd2));
+
+ MOZ_RELEASE_ASSERT(IsInRange(baseBegin, derivedEmptyBegin, derivedEmptyEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(baseEnd, derivedEmptyBegin, derivedEmptyEnd));
+
+ MOZ_RELEASE_ASSERT(IsInRange(baseBegin, derivedEmptyBegin, derivedEmptyEnd2));
+ MOZ_RELEASE_ASSERT(IsInRange(baseEnd, derivedEmptyBegin, derivedEmptyEnd2));
+ MOZ_RELEASE_ASSERT(!IsInRange(baseEnd2, derivedEmptyBegin, derivedEmptyEnd2));
+
+ uintptr_t uderivedEmptyBegin = uintptr_t(derivedEmptyBegin);
+ uintptr_t uderivedEmptyEnd = uintptr_t(derivedEmptyEnd);
+ uintptr_t uderivedEmptyEnd2 = uintptr_t(derivedEmptyEnd2);
+
+ MOZ_RELEASE_ASSERT(IsInRange(derivedEmptyBegin, uderivedEmptyBegin,
+ uderivedEmptyEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(derivedEmptyEnd, uderivedEmptyBegin,
+ uderivedEmptyEnd));
+
+ MOZ_RELEASE_ASSERT(IsInRange(derivedEmptyBegin, uderivedEmptyBegin,
+ uderivedEmptyEnd2));
+ MOZ_RELEASE_ASSERT(IsInRange(derivedEmptyEnd, uderivedEmptyBegin,
+ uderivedEmptyEnd2));
+ MOZ_RELEASE_ASSERT(!IsInRange(derivedEmptyEnd2, uderivedEmptyBegin,
+ uderivedEmptyEnd2));
+}
+
+struct ExtraDerived : Base { int y; };
+
+static void
+TestIsInRangeClassExtraDerived()
+{
+ void* nul = nullptr;
+ ExtraDerived* derivedBegin = nullptr;
+ ExtraDerived* derivedEnd = derivedBegin + 1;
+ ExtraDerived* derivedEnd2 = derivedBegin + 2;
+
+ Base* baseBegin = static_cast<Base*>(derivedBegin);
+ Base* baseEnd = static_cast<Base*>(derivedEnd);
+ Base* baseEnd2 = static_cast<Base*>(derivedEnd2);
+
+ MOZ_RELEASE_ASSERT(IsInRange(nul, derivedBegin, derivedEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(nul, derivedEnd, derivedEnd2));
+
+ MOZ_RELEASE_ASSERT(IsInRange(baseBegin, derivedBegin, derivedEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(baseEnd, derivedBegin, derivedEnd));
+
+ MOZ_RELEASE_ASSERT(IsInRange(baseBegin, derivedBegin, derivedEnd2));
+ MOZ_RELEASE_ASSERT(IsInRange(baseEnd, derivedBegin, derivedEnd2));
+ MOZ_RELEASE_ASSERT(!IsInRange(baseEnd2, derivedBegin, derivedEnd2));
+
+ uintptr_t uderivedBegin = uintptr_t(derivedBegin);
+ uintptr_t uderivedEnd = uintptr_t(derivedEnd);
+ uintptr_t uderivedEnd2 = uintptr_t(derivedEnd2);
+
+ MOZ_RELEASE_ASSERT(IsInRange(derivedBegin, uderivedBegin, uderivedEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(derivedEnd, uderivedBegin, uderivedEnd));
+
+ MOZ_RELEASE_ASSERT(IsInRange(derivedBegin, uderivedBegin, uderivedEnd2));
+ MOZ_RELEASE_ASSERT(IsInRange(derivedEnd, uderivedBegin, uderivedEnd2));
+ MOZ_RELEASE_ASSERT(!IsInRange(derivedEnd2, uderivedBegin, uderivedEnd2));
+}
+
+struct ExtraDerivedEmpty : EmptyBase { int y; };
+
+static void
+TestIsInRangeClassExtraDerivedEmpty()
+{
+ void* nul = nullptr;
+ ExtraDerivedEmpty* derivedBegin = nullptr;
+ ExtraDerivedEmpty* derivedEnd = derivedBegin + 1;
+ ExtraDerivedEmpty* derivedEnd2 = derivedBegin + 2;
+
+ EmptyBase* baseBegin = static_cast<EmptyBase*>(derivedBegin);
+ EmptyBase* baseEnd = static_cast<EmptyBase*>(derivedEnd);
+ EmptyBase* baseEnd2 = static_cast<EmptyBase*>(derivedEnd2);
+
+ MOZ_RELEASE_ASSERT(IsInRange(nul, derivedBegin, derivedEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(nul, derivedEnd, derivedEnd2));
+
+ MOZ_RELEASE_ASSERT(IsInRange(baseBegin, derivedBegin, derivedEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(baseEnd, derivedBegin, derivedEnd));
+
+ MOZ_RELEASE_ASSERT(IsInRange(baseBegin, derivedBegin, derivedEnd2));
+ MOZ_RELEASE_ASSERT(IsInRange(baseEnd, derivedBegin, derivedEnd2));
+ MOZ_RELEASE_ASSERT(!IsInRange(baseEnd2, derivedBegin, derivedEnd2));
+
+ uintptr_t uderivedBegin = uintptr_t(derivedBegin);
+ uintptr_t uderivedEnd = uintptr_t(derivedEnd);
+ uintptr_t uderivedEnd2 = uintptr_t(derivedEnd2);
+
+ MOZ_RELEASE_ASSERT(IsInRange(derivedBegin, uderivedBegin, uderivedEnd));
+ MOZ_RELEASE_ASSERT(!IsInRange(derivedEnd, uderivedBegin, uderivedEnd));
+
+ MOZ_RELEASE_ASSERT(IsInRange(derivedBegin, uderivedBegin, uderivedEnd2));
+ MOZ_RELEASE_ASSERT(IsInRange(derivedEnd, uderivedBegin, uderivedEnd2));
+ MOZ_RELEASE_ASSERT(!IsInRange(derivedEnd2, uderivedBegin, uderivedEnd2));
+}
+
+int
+main()
+{
+ TestIsInRangeNonClass();
+ TestIsInRangeVoid();
+ TestIsInRangeClass();
+ TestIsInRangeEmptyClass();
+ TestIsInRangeClassDerived();
+ TestIsInRangeClassDerivedEmpty();
+ TestIsInRangeClassExtraDerived();
+ TestIsInRangeClassExtraDerivedEmpty();
+ return 0;
+}
diff --git a/mfbt/tests/TestAtomics.cpp b/mfbt/tests/TestAtomics.cpp
new file mode 100644
index 000000000..e23b3d517
--- /dev/null
+++ b/mfbt/tests/TestAtomics.cpp
@@ -0,0 +1,295 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/Atomics.h"
+
+#include <stdint.h>
+
+using mozilla::Atomic;
+using mozilla::MemoryOrdering;
+using mozilla::Relaxed;
+using mozilla::ReleaseAcquire;
+using mozilla::SequentiallyConsistent;
+
+#define A(a,b) MOZ_RELEASE_ASSERT(a,b)
+
+template <typename T, MemoryOrdering Order>
+static void
+TestTypeWithOrdering()
+{
+ Atomic<T, Order> atomic(5);
+ A(atomic == 5, "Atomic variable did not initialize");
+
+ // Test atomic increment
+ A(++atomic == T(6), "Atomic increment did not work");
+ A(atomic++ == T(6), "Atomic post-increment did not work");
+ A(atomic == T(7), "Atomic post-increment did not work");
+
+ // Test atomic decrement
+ A(--atomic == 6, "Atomic decrement did not work");
+ A(atomic-- == 6, "Atomic post-decrement did not work");
+ A(atomic == 5, "Atomic post-decrement did not work");
+
+ // Test other arithmetic.
+ T result;
+ result = (atomic += T(5));
+ A(atomic == T(10), "Atomic += did not work");
+ A(result == T(10), "Atomic += returned the wrong value");
+ result = (atomic -= T(3));
+ A(atomic == T(7), "Atomic -= did not work");
+ A(result == T(7), "Atomic -= returned the wrong value");
+
+ // Test assignment
+ result = (atomic = T(5));
+ A(atomic == T(5), "Atomic assignment failed");
+ A(result == T(5), "Atomic assignment returned the wrong value");
+
+ // Test logical operations.
+ result = (atomic ^= T(2));
+ A(atomic == T(7), "Atomic ^= did not work");
+ A(result == T(7), "Atomic ^= returned the wrong value");
+ result = (atomic ^= T(4));
+ A(atomic == T(3), "Atomic ^= did not work");
+ A(result == T(3), "Atomic ^= returned the wrong value");
+ result = (atomic |= T(8));
+ A(atomic == T(11), "Atomic |= did not work");
+ A(result == T(11), "Atomic |= returned the wrong value");
+ result = (atomic |= T(8));
+ A(atomic == T(11), "Atomic |= did not work");
+ A(result == T(11), "Atomic |= returned the wrong value");
+ result = (atomic &= T(12));
+ A(atomic == T(8), "Atomic &= did not work");
+ A(result == T(8), "Atomic &= returned the wrong value");
+
+ // Test exchange.
+ atomic = T(30);
+ result = atomic.exchange(42);
+ A(atomic == T(42), "Atomic exchange did not work");
+ A(result == T(30), "Atomic exchange returned the wrong value");
+
+ // Test CAS.
+ atomic = T(1);
+ bool boolResult = atomic.compareExchange(0, 2);
+ A(!boolResult, "CAS should have returned false.");
+ A(atomic == T(1), "CAS shouldn't have done anything.");
+
+ boolResult = atomic.compareExchange(1, 42);
+ A(boolResult, "CAS should have succeeded.");
+ A(atomic == T(42), "CAS should have changed atomic's value.");
+}
+
+template<typename T, MemoryOrdering Order>
+static void
+TestPointerWithOrdering()
+{
+ T array1[10];
+ Atomic<T*, Order> atomic(array1);
+ A(atomic == array1, "Atomic variable did not initialize");
+
+ // Test atomic increment
+ A(++atomic == array1 + 1, "Atomic increment did not work");
+ A(atomic++ == array1 + 1, "Atomic post-increment did not work");
+ A(atomic == array1 + 2, "Atomic post-increment did not work");
+
+ // Test atomic decrement
+ A(--atomic == array1 + 1, "Atomic decrement did not work");
+ A(atomic-- == array1 + 1, "Atomic post-decrement did not work");
+ A(atomic == array1, "Atomic post-decrement did not work");
+
+ // Test other arithmetic operations
+ T* result;
+ result = (atomic += 2);
+ A(atomic == array1 + 2, "Atomic += did not work");
+ A(result == array1 + 2, "Atomic += returned the wrong value");
+ result = (atomic -= 1);
+ A(atomic == array1 + 1, "Atomic -= did not work");
+ A(result == array1 + 1, "Atomic -= returned the wrong value");
+
+ // Test stores
+ result = (atomic = array1);
+ A(atomic == array1, "Atomic assignment did not work");
+ A(result == array1, "Atomic assignment returned the wrong value");
+
+ // Test exchange
+ atomic = array1 + 2;
+ result = atomic.exchange(array1);
+ A(atomic == array1, "Atomic exchange did not work");
+ A(result == array1 + 2, "Atomic exchange returned the wrong value");
+
+ atomic = array1;
+ bool boolResult = atomic.compareExchange(array1 + 1, array1 + 2);
+ A(!boolResult, "CAS should have returned false.");
+ A(atomic == array1, "CAS shouldn't have done anything.");
+
+ boolResult = atomic.compareExchange(array1, array1 + 3);
+ A(boolResult, "CAS should have succeeded.");
+ A(atomic == array1 + 3, "CAS should have changed atomic's value.");
+}
+
+enum EnumType
+{
+ EnumType_0 = 0,
+ EnumType_1 = 1,
+ EnumType_2 = 2,
+ EnumType_3 = 3
+};
+
+template<MemoryOrdering Order>
+static void
+TestEnumWithOrdering()
+{
+ Atomic<EnumType, Order> atomic(EnumType_2);
+ A(atomic == EnumType_2, "Atomic variable did not initialize");
+
+ // Test assignment
+ EnumType result;
+ result = (atomic = EnumType_3);
+ A(atomic == EnumType_3, "Atomic assignment failed");
+ A(result == EnumType_3, "Atomic assignment returned the wrong value");
+
+ // Test exchange.
+ atomic = EnumType_1;
+ result = atomic.exchange(EnumType_2);
+ A(atomic == EnumType_2, "Atomic exchange did not work");
+ A(result == EnumType_1, "Atomic exchange returned the wrong value");
+
+ // Test CAS.
+ atomic = EnumType_1;
+ bool boolResult = atomic.compareExchange(EnumType_0, EnumType_2);
+ A(!boolResult, "CAS should have returned false.");
+ A(atomic == EnumType_1, "CAS shouldn't have done anything.");
+
+ boolResult = atomic.compareExchange(EnumType_1, EnumType_3);
+ A(boolResult, "CAS should have succeeded.");
+ A(atomic == EnumType_3, "CAS should have changed atomic's value.");
+}
+
+enum class EnumClass : uint32_t
+{
+ Value0 = 0,
+ Value1 = 1,
+ Value2 = 2,
+ Value3 = 3
+};
+
+template<MemoryOrdering Order>
+static void
+TestEnumClassWithOrdering()
+{
+ Atomic<EnumClass, Order> atomic(EnumClass::Value2);
+ A(atomic == EnumClass::Value2, "Atomic variable did not initialize");
+
+ // Test assignment
+ EnumClass result;
+ result = (atomic = EnumClass::Value3);
+ A(atomic == EnumClass::Value3, "Atomic assignment failed");
+ A(result == EnumClass::Value3, "Atomic assignment returned the wrong value");
+
+ // Test exchange.
+ atomic = EnumClass::Value1;
+ result = atomic.exchange(EnumClass::Value2);
+ A(atomic == EnumClass::Value2, "Atomic exchange did not work");
+ A(result == EnumClass::Value1, "Atomic exchange returned the wrong value");
+
+ // Test CAS.
+ atomic = EnumClass::Value1;
+ bool boolResult = atomic.compareExchange(EnumClass::Value0, EnumClass::Value2);
+ A(!boolResult, "CAS should have returned false.");
+ A(atomic == EnumClass::Value1, "CAS shouldn't have done anything.");
+
+ boolResult = atomic.compareExchange(EnumClass::Value1, EnumClass::Value3);
+ A(boolResult, "CAS should have succeeded.");
+ A(atomic == EnumClass::Value3, "CAS should have changed atomic's value.");
+}
+
+template <MemoryOrdering Order>
+static void
+TestBoolWithOrdering()
+{
+ Atomic<bool, Order> atomic(false);
+ A(atomic == false, "Atomic variable did not initialize");
+
+ // Test assignment
+ bool result;
+ result = (atomic = true);
+ A(atomic == true, "Atomic assignment failed");
+ A(result == true, "Atomic assignment returned the wrong value");
+
+ // Test exchange.
+ atomic = false;
+ result = atomic.exchange(true);
+ A(atomic == true, "Atomic exchange did not work");
+ A(result == false, "Atomic exchange returned the wrong value");
+
+ // Test CAS.
+ atomic = false;
+ bool boolResult = atomic.compareExchange(true, false);
+ A(!boolResult, "CAS should have returned false.");
+ A(atomic == false, "CAS shouldn't have done anything.");
+
+ boolResult = atomic.compareExchange(false, true);
+ A(boolResult, "CAS should have succeeded.");
+ A(atomic == true, "CAS should have changed atomic's value.");
+}
+
+template <typename T>
+static void
+TestType()
+{
+ TestTypeWithOrdering<T, SequentiallyConsistent>();
+ TestTypeWithOrdering<T, ReleaseAcquire>();
+ TestTypeWithOrdering<T, Relaxed>();
+}
+
+template<typename T>
+static void
+TestPointer()
+{
+ TestPointerWithOrdering<T, SequentiallyConsistent>();
+ TestPointerWithOrdering<T, ReleaseAcquire>();
+ TestPointerWithOrdering<T, Relaxed>();
+}
+
+static void
+TestEnum()
+{
+ TestEnumWithOrdering<SequentiallyConsistent>();
+ TestEnumWithOrdering<ReleaseAcquire>();
+ TestEnumWithOrdering<Relaxed>();
+
+ TestEnumClassWithOrdering<SequentiallyConsistent>();
+ TestEnumClassWithOrdering<ReleaseAcquire>();
+ TestEnumClassWithOrdering<Relaxed>();
+}
+
+static void
+TestBool()
+{
+ TestBoolWithOrdering<SequentiallyConsistent>();
+ TestBoolWithOrdering<ReleaseAcquire>();
+ TestBoolWithOrdering<Relaxed>();
+}
+
+#undef A
+
+int
+main()
+{
+ TestType<uint32_t>();
+ TestType<int32_t>();
+ TestType<uint64_t>();
+ TestType<int64_t>();
+ TestType<intptr_t>();
+ TestType<uintptr_t>();
+ TestPointer<int>();
+ TestPointer<float>();
+ TestPointer<uint16_t*>();
+ TestPointer<uint32_t*>();
+ TestEnum();
+ TestBool();
+ return 0;
+}
diff --git a/mfbt/tests/TestBinarySearch.cpp b/mfbt/tests/TestBinarySearch.cpp
new file mode 100644
index 000000000..44644bd3a
--- /dev/null
+++ b/mfbt/tests/TestBinarySearch.cpp
@@ -0,0 +1,113 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/BinarySearch.h"
+#include "mozilla/Vector.h"
+
+using mozilla::ArrayLength;
+using mozilla::BinarySearch;
+using mozilla::BinarySearchIf;
+using mozilla::Vector;
+
+#define A(a) MOZ_RELEASE_ASSERT(a)
+
+struct Person
+{
+ int mAge;
+ int mId;
+ Person(int aAge, int aId) : mAge(aAge), mId(aId) {}
+};
+
+struct GetAge
+{
+ Vector<Person>& mV;
+ explicit GetAge(Vector<Person>& aV) : mV(aV) {}
+ int operator[](size_t index) const { return mV[index].mAge; }
+};
+
+struct RangeFinder {
+ const int mLower, mUpper;
+ RangeFinder(int lower, int upper) : mLower(lower), mUpper(upper) {}
+ int operator()(int val) const {
+ if (val >= mUpper) return -1;
+ if (val < mLower) return 1;
+ return 0;
+ }
+};
+
+static void
+TestBinarySearch()
+{
+ size_t m;
+
+ Vector<int> v1;
+ MOZ_RELEASE_ASSERT(v1.append(2));
+ MOZ_RELEASE_ASSERT(v1.append(4));
+ MOZ_RELEASE_ASSERT(v1.append(6));
+ MOZ_RELEASE_ASSERT(v1.append(8));
+
+ MOZ_RELEASE_ASSERT(!BinarySearch(v1, 0, v1.length(), 1, &m) && m == 0);
+ MOZ_RELEASE_ASSERT( BinarySearch(v1, 0, v1.length(), 2, &m) && m == 0);
+ MOZ_RELEASE_ASSERT(!BinarySearch(v1, 0, v1.length(), 3, &m) && m == 1);
+ MOZ_RELEASE_ASSERT( BinarySearch(v1, 0, v1.length(), 4, &m) && m == 1);
+ MOZ_RELEASE_ASSERT(!BinarySearch(v1, 0, v1.length(), 5, &m) && m == 2);
+ MOZ_RELEASE_ASSERT( BinarySearch(v1, 0, v1.length(), 6, &m) && m == 2);
+ MOZ_RELEASE_ASSERT(!BinarySearch(v1, 0, v1.length(), 7, &m) && m == 3);
+ MOZ_RELEASE_ASSERT( BinarySearch(v1, 0, v1.length(), 8, &m) && m == 3);
+ MOZ_RELEASE_ASSERT(!BinarySearch(v1, 0, v1.length(), 9, &m) && m == 4);
+
+ MOZ_RELEASE_ASSERT(!BinarySearch(v1, 1, 3, 1, &m) && m == 1);
+ MOZ_RELEASE_ASSERT(!BinarySearch(v1, 1, 3, 2, &m) && m == 1);
+ MOZ_RELEASE_ASSERT(!BinarySearch(v1, 1, 3, 3, &m) && m == 1);
+ MOZ_RELEASE_ASSERT( BinarySearch(v1, 1, 3, 4, &m) && m == 1);
+ MOZ_RELEASE_ASSERT(!BinarySearch(v1, 1, 3, 5, &m) && m == 2);
+ MOZ_RELEASE_ASSERT( BinarySearch(v1, 1, 3, 6, &m) && m == 2);
+ MOZ_RELEASE_ASSERT(!BinarySearch(v1, 1, 3, 7, &m) && m == 3);
+ MOZ_RELEASE_ASSERT(!BinarySearch(v1, 1, 3, 8, &m) && m == 3);
+ MOZ_RELEASE_ASSERT(!BinarySearch(v1, 1, 3, 9, &m) && m == 3);
+
+ MOZ_RELEASE_ASSERT(!BinarySearch(v1, 0, 0, 0, &m) && m == 0);
+ MOZ_RELEASE_ASSERT(!BinarySearch(v1, 0, 0, 9, &m) && m == 0);
+
+ Vector<int> v2;
+ MOZ_RELEASE_ASSERT(!BinarySearch(v2, 0, 0, 0, &m) && m == 0);
+ MOZ_RELEASE_ASSERT(!BinarySearch(v2, 0, 0, 9, &m) && m == 0);
+
+ Vector<Person> v3;
+ MOZ_RELEASE_ASSERT(v3.append(Person(2, 42)));
+ MOZ_RELEASE_ASSERT(v3.append(Person(4, 13)));
+ MOZ_RELEASE_ASSERT(v3.append(Person(6, 360)));
+
+ A(!BinarySearch(GetAge(v3), 0, v3.length(), 1, &m) && m == 0);
+ A( BinarySearch(GetAge(v3), 0, v3.length(), 2, &m) && m == 0);
+ A(!BinarySearch(GetAge(v3), 0, v3.length(), 3, &m) && m == 1);
+ A( BinarySearch(GetAge(v3), 0, v3.length(), 4, &m) && m == 1);
+ A(!BinarySearch(GetAge(v3), 0, v3.length(), 5, &m) && m == 2);
+ A( BinarySearch(GetAge(v3), 0, v3.length(), 6, &m) && m == 2);
+ A(!BinarySearch(GetAge(v3), 0, v3.length(), 7, &m) && m == 3);
+}
+
+static void
+TestBinarySearchIf()
+{
+ const int v1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ const size_t len = ArrayLength(v1);
+ size_t m;
+
+ A( BinarySearchIf(v1, 0, len, RangeFinder( 2, 3), &m) && m == 2);
+ A(!BinarySearchIf(v1, 0, len, RangeFinder(-5, -2), &m) && m == 0);
+ A( BinarySearchIf(v1, 0, len, RangeFinder( 3, 5), &m) && m >= 3 && m < 5);
+ A(!BinarySearchIf(v1, 0, len, RangeFinder(10, 12), &m) && m == 10);
+}
+
+int
+main()
+{
+ TestBinarySearch();
+ TestBinarySearchIf();
+ return 0;
+}
diff --git a/mfbt/tests/TestBloomFilter.cpp b/mfbt/tests/TestBloomFilter.cpp
new file mode 100644
index 000000000..42e8b821e
--- /dev/null
+++ b/mfbt/tests/TestBloomFilter.cpp
@@ -0,0 +1,103 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/BloomFilter.h"
+
+#include <stddef.h>
+#include <stdio.h>
+
+using mozilla::BloomFilter;
+
+class FilterChecker
+{
+public:
+ explicit FilterChecker(uint32_t aHash) : mHash(aHash) { }
+
+ uint32_t hash() const { return mHash; }
+
+private:
+ uint32_t mHash;
+};
+
+int
+main()
+{
+ BloomFilter<12, FilterChecker>* filter = new BloomFilter<12, FilterChecker>();
+ MOZ_RELEASE_ASSERT(filter);
+
+ FilterChecker one(1);
+ FilterChecker two(0x20000);
+ FilterChecker many(0x10000);
+ FilterChecker multiple(0x20001);
+
+ filter->add(&one);
+ MOZ_RELEASE_ASSERT(filter->mightContain(&one),
+ "Filter should contain 'one'");
+
+ MOZ_RELEASE_ASSERT(!filter->mightContain(&multiple),
+ "Filter claims to contain 'multiple' when it should not");
+
+ MOZ_RELEASE_ASSERT(filter->mightContain(&many),
+ "Filter should contain 'many' (false positive)");
+
+ filter->add(&two);
+ MOZ_RELEASE_ASSERT(filter->mightContain(&multiple),
+ "Filter should contain 'multiple' (false positive)");
+
+ // Test basic removals
+ filter->remove(&two);
+ MOZ_RELEASE_ASSERT(!filter->mightContain(&multiple),
+ "Filter claims to contain 'multiple' when it should not after two "
+ "was removed");
+
+ // Test multiple addition/removal
+ const size_t FILTER_SIZE = 255;
+ for (size_t i = 0; i < FILTER_SIZE - 1; ++i) {
+ filter->add(&two);
+ }
+ MOZ_RELEASE_ASSERT(filter->mightContain(&multiple),
+ "Filter should contain 'multiple' after 'two' added lots of times "
+ "(false positive)");
+
+ for (size_t i = 0; i < FILTER_SIZE - 1; ++i) {
+ filter->remove(&two);
+ }
+ MOZ_RELEASE_ASSERT(!filter->mightContain(&multiple),
+ "Filter claims to contain 'multiple' when it should not after two "
+ "was removed lots of times");
+
+ // Test overflowing the filter buckets
+ for (size_t i = 0; i < FILTER_SIZE + 1; ++i) {
+ filter->add(&two);
+ }
+ MOZ_RELEASE_ASSERT(filter->mightContain(&multiple),
+ "Filter should contain 'multiple' after 'two' added lots more "
+ "times (false positive)");
+
+ for (size_t i = 0; i < FILTER_SIZE + 1; ++i) {
+ filter->remove(&two);
+ }
+ MOZ_RELEASE_ASSERT(filter->mightContain(&multiple),
+ "Filter claims to not contain 'multiple' even though we should "
+ "have run out of space in the buckets (false positive)");
+ MOZ_RELEASE_ASSERT(filter->mightContain(&two),
+ "Filter claims to not contain 'two' even though we should have "
+ "run out of space in the buckets (false positive)");
+
+ filter->remove(&one);
+
+ MOZ_RELEASE_ASSERT(!filter->mightContain(&one),
+ "Filter should not contain 'one', because we didn't overflow its "
+ "bucket");
+
+ filter->clear();
+
+ MOZ_RELEASE_ASSERT(!filter->mightContain(&multiple),
+ "clear() failed to work");
+
+ return 0;
+}
diff --git a/mfbt/tests/TestBufferList.cpp b/mfbt/tests/TestBufferList.cpp
new file mode 100644
index 000000000..cccaac021
--- /dev/null
+++ b/mfbt/tests/TestBufferList.cpp
@@ -0,0 +1,256 @@
+/* -*- 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/BufferList.h"
+
+// 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("TestBufferList.cpp: overflow");
+ }
+ T* rv = static_cast<T*>(malloc(aNumElems * sizeof(T)));
+ if (!rv) {
+ MOZ_CRASH("TestBufferList.cpp: out of memory");
+ }
+ return rv;
+ }
+
+ void free_(void* aPtr) { free(aPtr); }
+
+ void reportAllocOverflow() const {}
+
+ bool checkSimulatedOOM() const { return true; }
+};
+
+typedef mozilla::BufferList<InfallibleAllocPolicy> BufferList;
+
+int main(void)
+{
+ const size_t kInitialSize = 16;
+ const size_t kInitialCapacity = 24;
+ const size_t kStandardCapacity = 32;
+
+ BufferList bl(kInitialSize, kInitialCapacity, kStandardCapacity);
+
+ memset(bl.Start(), 0x0c, kInitialSize);
+ MOZ_RELEASE_ASSERT(bl.Size() == kInitialSize);
+
+ // Simple iteration and access.
+
+ BufferList::IterImpl iter(bl.Iter());
+ MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialSize);
+ MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialSize));
+ MOZ_RELEASE_ASSERT(!iter.HasRoomFor(kInitialSize + 1));
+ MOZ_RELEASE_ASSERT(!iter.HasRoomFor(size_t(-1)));
+ MOZ_RELEASE_ASSERT(*iter.Data() == 0x0c);
+ MOZ_RELEASE_ASSERT(!iter.Done());
+
+ iter.Advance(bl, 4);
+ MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialSize - 4);
+ MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialSize - 4));
+ MOZ_RELEASE_ASSERT(*iter.Data() == 0x0c);
+ MOZ_RELEASE_ASSERT(!iter.Done());
+
+ iter.Advance(bl, 11);
+ MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialSize - 4 - 11);
+ MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialSize - 4 - 11));
+ MOZ_RELEASE_ASSERT(!iter.HasRoomFor(kInitialSize - 4 - 11 + 1));
+ MOZ_RELEASE_ASSERT(*iter.Data() == 0x0c);
+ MOZ_RELEASE_ASSERT(!iter.Done());
+
+ iter.Advance(bl, kInitialSize - 4 - 11);
+ MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == 0);
+ MOZ_RELEASE_ASSERT(!iter.HasRoomFor(1));
+ MOZ_RELEASE_ASSERT(iter.Done());
+
+ // Writing to the buffer.
+
+ const size_t kSmallWrite = 16;
+
+ char toWrite[kSmallWrite];
+ memset(toWrite, 0x0a, kSmallWrite);
+ bl.WriteBytes(toWrite, kSmallWrite);
+
+ MOZ_RELEASE_ASSERT(bl.Size() == kInitialSize + kSmallWrite);
+
+ iter = bl.Iter();
+ iter.Advance(bl, kInitialSize);
+ MOZ_RELEASE_ASSERT(!iter.Done());
+ MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialCapacity - kInitialSize);
+ MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialCapacity - kInitialSize));
+ MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a);
+
+ // AdvanceAcrossSegments.
+
+ iter = bl.Iter();
+ MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kInitialCapacity - 4));
+ MOZ_RELEASE_ASSERT(!iter.Done());
+ MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == 4);
+ MOZ_RELEASE_ASSERT(iter.HasRoomFor(4));
+ MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a);
+
+ iter = bl.Iter();
+ MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite - 4));
+ MOZ_RELEASE_ASSERT(!iter.Done());
+ MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == 4);
+ MOZ_RELEASE_ASSERT(iter.HasRoomFor(4));
+ MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a);
+
+ MOZ_RELEASE_ASSERT(bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite - 1));
+ MOZ_RELEASE_ASSERT(bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite));
+ MOZ_RELEASE_ASSERT(!bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite + 1));
+ MOZ_RELEASE_ASSERT(!bl.Iter().AdvanceAcrossSegments(bl, size_t(-1)));
+
+ // Reading non-contiguous bytes.
+
+ char toRead[kSmallWrite];
+ iter = bl.Iter();
+ iter.Advance(bl, kInitialSize);
+ bl.ReadBytes(iter, toRead, kSmallWrite);
+ MOZ_RELEASE_ASSERT(memcmp(toRead, toWrite, kSmallWrite) == 0);
+ MOZ_RELEASE_ASSERT(iter.Done());
+
+ // Make sure reading up to the end of a segment advances the iter to the next
+ // segment.
+ iter = bl.Iter();
+ bl.ReadBytes(iter, toRead, kInitialSize);
+ MOZ_RELEASE_ASSERT(!iter.Done());
+ MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialCapacity - kInitialSize);
+
+ const size_t kBigWrite = 1024;
+
+ char* toWriteBig = static_cast<char*>(malloc(kBigWrite));
+ for (unsigned i = 0; i < kBigWrite; i++) {
+ toWriteBig[i] = i % 37;
+ }
+ bl.WriteBytes(toWriteBig, kBigWrite);
+
+ char* toReadBig = static_cast<char*>(malloc(kBigWrite));
+ iter = bl.Iter();
+ MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite));
+ bl.ReadBytes(iter, toReadBig, kBigWrite);
+ MOZ_RELEASE_ASSERT(memcmp(toReadBig, toWriteBig, kBigWrite) == 0);
+ MOZ_RELEASE_ASSERT(iter.Done());
+
+ free(toReadBig);
+ free(toWriteBig);
+
+ // Currently bl contains these segments:
+ // #0: offset 0, [0x0c]*16 + [0x0a]*8, size 24
+ // #1: offset 24, [0x0a]*8 + [i%37 for i in 0..24], size 32
+ // #2: offset 56, [i%37 for i in 24..56, size 32
+ // ...
+ // #32: offset 1016, [i%37 for i in 984..1016], size 32
+ // #33: offset 1048, [i%37 for i in 1016..1024], size 8
+
+ static size_t kTotalSize = kInitialSize + kSmallWrite + kBigWrite;
+
+ MOZ_RELEASE_ASSERT(bl.Size() == kTotalSize);
+
+ static size_t kLastSegmentSize = (kTotalSize - kInitialCapacity) % kStandardCapacity;
+
+ iter = bl.Iter();
+ MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kTotalSize - kLastSegmentSize - kStandardCapacity));
+ MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kStandardCapacity);
+ iter.Advance(bl, kStandardCapacity);
+ MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kLastSegmentSize);
+ MOZ_RELEASE_ASSERT(unsigned(*iter.Data()) == (kTotalSize - kLastSegmentSize - kInitialSize - kSmallWrite) % 37);
+
+ // Clear.
+
+ bl.Clear();
+ MOZ_RELEASE_ASSERT(bl.Size() == 0);
+ MOZ_RELEASE_ASSERT(bl.Iter().Done());
+
+ // Move assignment.
+
+ const size_t kSmallCapacity = 8;
+
+ BufferList bl2(0, kSmallCapacity, kSmallCapacity);
+ bl2.WriteBytes(toWrite, kSmallWrite);
+ bl2.WriteBytes(toWrite, kSmallWrite);
+ bl2.WriteBytes(toWrite, kSmallWrite);
+
+ bl = mozilla::Move(bl2);
+ MOZ_RELEASE_ASSERT(bl2.Size() == 0);
+ MOZ_RELEASE_ASSERT(bl2.Iter().Done());
+
+ iter = bl.Iter();
+ MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kSmallWrite * 3));
+ MOZ_RELEASE_ASSERT(iter.Done());
+
+ // MoveFallible
+
+ bool success;
+ bl2 = bl.MoveFallible<InfallibleAllocPolicy>(&success);
+ MOZ_RELEASE_ASSERT(success);
+ MOZ_RELEASE_ASSERT(bl.Size() == 0);
+ MOZ_RELEASE_ASSERT(bl.Iter().Done());
+ MOZ_RELEASE_ASSERT(bl2.Size() == kSmallWrite * 3);
+
+ iter = bl2.Iter();
+ MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl2, kSmallWrite * 3));
+ MOZ_RELEASE_ASSERT(iter.Done());
+
+ bl = bl2.MoveFallible<InfallibleAllocPolicy>(&success);
+
+ // Borrowing.
+
+ const size_t kBorrowStart = 4;
+ const size_t kBorrowSize = 24;
+
+ iter = bl.Iter();
+ iter.Advance(bl, kBorrowStart);
+ bl2 = bl.Borrow<InfallibleAllocPolicy>(iter, kBorrowSize, &success);
+ MOZ_RELEASE_ASSERT(success);
+ MOZ_RELEASE_ASSERT(bl2.Size() == kBorrowSize);
+
+ MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kSmallWrite * 3 - kBorrowSize - kBorrowStart));
+ MOZ_RELEASE_ASSERT(iter.Done());
+
+ iter = bl2.Iter();
+ MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl2, kBorrowSize));
+ MOZ_RELEASE_ASSERT(iter.Done());
+
+ BufferList::IterImpl iter1(bl.Iter()), iter2(bl2.Iter());
+ iter1.Advance(bl, kBorrowStart);
+ MOZ_RELEASE_ASSERT(iter1.Data() == iter2.Data());
+ MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl, kBorrowSize - 5));
+ MOZ_RELEASE_ASSERT(iter2.AdvanceAcrossSegments(bl2, kBorrowSize - 5));
+ MOZ_RELEASE_ASSERT(iter1.Data() == iter2.Data());
+
+ // Extracting.
+
+ const size_t kExtractStart = 8;
+ const size_t kExtractSize = 24;
+ const size_t kExtractOverSize = 1000;
+
+ iter = bl.Iter();
+ iter.Advance(bl, kExtractStart);
+ bl2 = bl.Extract(iter, kExtractSize, &success);
+ MOZ_RELEASE_ASSERT(success);
+ MOZ_RELEASE_ASSERT(bl2.Size() == kExtractSize);
+
+ BufferList bl3 = bl.Extract(iter, kExtractOverSize, &success);
+ MOZ_RELEASE_ASSERT(!success);
+
+ MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kSmallWrite * 3 - kExtractSize - kExtractStart));
+ MOZ_RELEASE_ASSERT(iter.Done());
+
+ iter = bl2.Iter();
+ MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl2, kExtractSize));
+ MOZ_RELEASE_ASSERT(iter.Done());
+
+ return 0;
+}
diff --git a/mfbt/tests/TestCasting.cpp b/mfbt/tests/TestCasting.cpp
new file mode 100644
index 000000000..c8bd12488
--- /dev/null
+++ b/mfbt/tests/TestCasting.cpp
@@ -0,0 +1,112 @@
+/* -*- 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/Casting.h"
+
+#include <stdint.h>
+
+using mozilla::BitwiseCast;
+using mozilla::detail::IsInBounds;
+
+template<typename Uint, typename Ulong, bool = (sizeof(Uint) == sizeof(Ulong))>
+struct UintUlongBitwiseCast;
+
+template<typename Uint, typename Ulong>
+struct UintUlongBitwiseCast<Uint, Ulong, true>
+{
+ static void test()
+ {
+ MOZ_RELEASE_ASSERT(BitwiseCast<Ulong>(Uint(8675309)) == Ulong(8675309));
+ }
+};
+
+template<typename Uint, typename Ulong>
+struct UintUlongBitwiseCast<Uint, Ulong, false>
+{
+ static void test() { }
+};
+
+static void
+TestBitwiseCast()
+{
+ MOZ_RELEASE_ASSERT(BitwiseCast<int>(int(8675309)) == int(8675309));
+ UintUlongBitwiseCast<unsigned int, unsigned long>::test();
+}
+
+static void
+TestSameSize()
+{
+ MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int16_t>(int16_t(0))));
+ MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int16_t>(int16_t(INT16_MIN))));
+ MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int16_t>(int16_t(INT16_MAX))));
+ MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, uint16_t>(uint16_t(UINT16_MAX))));
+ MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, int16_t>(uint16_t(0))));
+ MOZ_RELEASE_ASSERT((!IsInBounds<uint16_t, int16_t>(uint16_t(-1))));
+ MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint16_t>(int16_t(-1))));
+ MOZ_RELEASE_ASSERT((IsInBounds<int16_t, uint16_t>(int16_t(INT16_MAX))));
+ MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint16_t>(int16_t(INT16_MIN))));
+ MOZ_RELEASE_ASSERT((IsInBounds<int32_t, uint32_t>(int32_t(INT32_MAX))));
+ MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, uint32_t>(int32_t(INT32_MIN))));
+}
+
+static void
+TestToBiggerSize()
+{
+ MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int32_t>(int16_t(0))));
+ MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int32_t>(int16_t(INT16_MIN))));
+ MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int32_t>(int16_t(INT16_MAX))));
+ MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, uint32_t>(uint16_t(UINT16_MAX))));
+ MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, int32_t>(uint16_t(0))));
+ MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, int32_t>(uint16_t(-1))));
+ MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint32_t>(int16_t(-1))));
+ MOZ_RELEASE_ASSERT((IsInBounds<int16_t, uint32_t>(int16_t(INT16_MAX))));
+ MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint32_t>(int16_t(INT16_MIN))));
+ MOZ_RELEASE_ASSERT((IsInBounds<int32_t, uint64_t>(int32_t(INT32_MAX))));
+ MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, uint64_t>(int32_t(INT32_MIN))));
+}
+
+static void
+TestToSmallerSize()
+{
+ MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int8_t>(int16_t(0))));
+ MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, int8_t>(int16_t(INT16_MIN))));
+ MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, int8_t>(int16_t(INT16_MAX))));
+ MOZ_RELEASE_ASSERT((!IsInBounds<uint16_t, uint8_t>(uint16_t(UINT16_MAX))));
+ MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, int8_t>(uint16_t(0))));
+ MOZ_RELEASE_ASSERT((!IsInBounds<uint16_t, int8_t>(uint16_t(-1))));
+ MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint8_t>(int16_t(-1))));
+ MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint8_t>(int16_t(INT16_MAX))));
+ MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint8_t>(int16_t(INT16_MIN))));
+ MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, uint16_t>(int32_t(INT32_MAX))));
+ MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, uint16_t>(int32_t(INT32_MIN))));
+
+ // Boundary cases
+ MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, int32_t>(int64_t(INT32_MIN) - 1)));
+ MOZ_RELEASE_ASSERT((IsInBounds<int64_t, int32_t>(int64_t(INT32_MIN))));
+ MOZ_RELEASE_ASSERT((IsInBounds<int64_t, int32_t>(int64_t(INT32_MIN) + 1)));
+ MOZ_RELEASE_ASSERT((IsInBounds<int64_t, int32_t>(int64_t(INT32_MAX) - 1)));
+ MOZ_RELEASE_ASSERT((IsInBounds<int64_t, int32_t>(int64_t(INT32_MAX))));
+ MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, int32_t>(int64_t(INT32_MAX) + 1)));
+
+ MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, uint32_t>(int64_t(-1))));
+ MOZ_RELEASE_ASSERT((IsInBounds<int64_t, uint32_t>(int64_t(0))));
+ MOZ_RELEASE_ASSERT((IsInBounds<int64_t, uint32_t>(int64_t(1))));
+ MOZ_RELEASE_ASSERT((IsInBounds<int64_t, uint32_t>(int64_t(UINT32_MAX) - 1)));
+ MOZ_RELEASE_ASSERT((IsInBounds<int64_t, uint32_t>(int64_t(UINT32_MAX))));
+ MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, uint32_t>(int64_t(UINT32_MAX) + 1)));
+}
+
+int
+main()
+{
+ TestBitwiseCast();
+
+ TestSameSize();
+ TestToBiggerSize();
+ TestToSmallerSize();
+
+ return 0;
+}
diff --git a/mfbt/tests/TestCeilingFloor.cpp b/mfbt/tests/TestCeilingFloor.cpp
new file mode 100644
index 000000000..f1c50dec8
--- /dev/null
+++ b/mfbt/tests/TestCeilingFloor.cpp
@@ -0,0 +1,89 @@
+/* -*- 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/MathAlgorithms.h"
+
+using mozilla::CeilingLog2;
+using mozilla::FloorLog2;
+using mozilla::RoundUpPow2;
+
+static void
+TestCeiling()
+{
+ for (uint32_t i = 0; i <= 1; i++) {
+ MOZ_RELEASE_ASSERT(CeilingLog2(i) == 0);
+ }
+ for (uint32_t i = 2; i <= 2; i++) {
+ MOZ_RELEASE_ASSERT(CeilingLog2(i) == 1);
+ }
+ for (uint32_t i = 3; i <= 4; i++) {
+ MOZ_RELEASE_ASSERT(CeilingLog2(i) == 2);
+ }
+ for (uint32_t i = 5; i <= 8; i++) {
+ MOZ_RELEASE_ASSERT(CeilingLog2(i) == 3);
+ }
+ for (uint32_t i = 9; i <= 16; i++) {
+ MOZ_RELEASE_ASSERT(CeilingLog2(i) == 4);
+ }
+}
+
+static void
+TestFloor()
+{
+ for (uint32_t i = 0; i <= 1; i++) {
+ MOZ_RELEASE_ASSERT(FloorLog2(i) == 0);
+ }
+ for (uint32_t i = 2; i <= 3; i++) {
+ MOZ_RELEASE_ASSERT(FloorLog2(i) == 1);
+ }
+ for (uint32_t i = 4; i <= 7; i++) {
+ MOZ_RELEASE_ASSERT(FloorLog2(i) == 2);
+ }
+ for (uint32_t i = 8; i <= 15; i++) {
+ MOZ_RELEASE_ASSERT(FloorLog2(i) == 3);
+ }
+ for (uint32_t i = 16; i <= 31; i++) {
+ MOZ_RELEASE_ASSERT(FloorLog2(i) == 4);
+ }
+}
+
+static void
+TestRoundUpPow2()
+{
+ MOZ_RELEASE_ASSERT(RoundUpPow2(0) == 1);
+ MOZ_RELEASE_ASSERT(RoundUpPow2(1) == 1);
+ MOZ_RELEASE_ASSERT(RoundUpPow2(2) == 2);
+ MOZ_RELEASE_ASSERT(RoundUpPow2(3) == 4);
+ MOZ_RELEASE_ASSERT(RoundUpPow2(4) == 4);
+ MOZ_RELEASE_ASSERT(RoundUpPow2(5) == 8);
+ MOZ_RELEASE_ASSERT(RoundUpPow2(6) == 8);
+ MOZ_RELEASE_ASSERT(RoundUpPow2(7) == 8);
+ MOZ_RELEASE_ASSERT(RoundUpPow2(8) == 8);
+ MOZ_RELEASE_ASSERT(RoundUpPow2(9) == 16);
+
+ MOZ_RELEASE_ASSERT(RoundUpPow2(15) == 16);
+ MOZ_RELEASE_ASSERT(RoundUpPow2(16) == 16);
+ MOZ_RELEASE_ASSERT(RoundUpPow2(17) == 32);
+
+ MOZ_RELEASE_ASSERT(RoundUpPow2(31) == 32);
+ MOZ_RELEASE_ASSERT(RoundUpPow2(32) == 32);
+ MOZ_RELEASE_ASSERT(RoundUpPow2(33) == 64);
+
+ size_t MaxPow2 = size_t(1) << (sizeof(size_t) * CHAR_BIT - 1);
+ MOZ_RELEASE_ASSERT(RoundUpPow2(MaxPow2 - 1) == MaxPow2);
+ MOZ_RELEASE_ASSERT(RoundUpPow2(MaxPow2) == MaxPow2);
+ // not valid to round up when past the max power of two
+}
+
+int
+main()
+{
+ TestCeiling();
+ TestFloor();
+
+ TestRoundUpPow2();
+ return 0;
+}
diff --git a/mfbt/tests/TestCheckedInt.cpp b/mfbt/tests/TestCheckedInt.cpp
new file mode 100644
index 000000000..7ded8e350
--- /dev/null
+++ b/mfbt/tests/TestCheckedInt.cpp
@@ -0,0 +1,626 @@
+/* -*- 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/CheckedInt.h"
+
+#include <iostream>
+#include <climits>
+
+using namespace mozilla;
+
+int gIntegerTypesTested = 0;
+int gTestsPassed = 0;
+int gTestsFailed = 0;
+
+void verifyImplFunction(bool aX, bool aExpected,
+ const char* aFile, int aLine,
+ int aSize, bool aIsTSigned)
+{
+ if (aX == aExpected) {
+ gTestsPassed++;
+ } else {
+ gTestsFailed++;
+ std::cerr << "Test failed at " << aFile << ":" << aLine;
+ std::cerr << " with T a ";
+ if (aIsTSigned) {
+ std::cerr << "signed";
+ } else {
+ std::cerr << "unsigned";
+ }
+ std::cerr << " " << CHAR_BIT * aSize << "-bit integer type" << std::endl;
+ }
+}
+
+#define VERIFY_IMPL(x, expected) \
+ verifyImplFunction((x), \
+ (expected), \
+ __FILE__, \
+ __LINE__, \
+ sizeof(T), \
+ IsSigned<T>::value)
+
+#define VERIFY(x) VERIFY_IMPL(x, true)
+#define VERIFY_IS_FALSE(x) VERIFY_IMPL(x, false)
+#define VERIFY_IS_VALID(x) VERIFY_IMPL((x).isValid(), true)
+#define VERIFY_IS_INVALID(x) VERIFY_IMPL((x).isValid(), false)
+#define VERIFY_IS_VALID_IF(x,condition) VERIFY_IMPL((x).isValid(), (condition))
+
+template<typename T, size_t Size = sizeof(T)>
+struct testTwiceBiggerType
+{
+ static void run()
+ {
+ VERIFY(detail::IsSupported<typename detail::TwiceBiggerType<T>::Type>::value);
+ VERIFY(sizeof(typename detail::TwiceBiggerType<T>::Type) == 2 * sizeof(T));
+ VERIFY(bool(IsSigned<typename detail::TwiceBiggerType<T>::Type>::value) ==
+ bool(IsSigned<T>::value));
+ }
+};
+
+template<typename T>
+struct testTwiceBiggerType<T, 8>
+{
+ static void run()
+ {
+ VERIFY_IS_FALSE(detail::IsSupported<
+ typename detail::TwiceBiggerType<T>::Type
+ >::value);
+ }
+};
+
+
+template<typename T>
+void test()
+{
+ static bool alreadyRun = false;
+ // Integer types from different families may just be typedefs for types from
+ // other families. E.g. int32_t might be just a typedef for int. No point
+ // re-running the same tests then.
+ if (alreadyRun) {
+ return;
+ }
+ alreadyRun = true;
+
+ VERIFY(detail::IsSupported<T>::value);
+ const bool isTSigned = IsSigned<T>::value;
+ VERIFY(bool(isTSigned) == !bool(T(-1) > T(0)));
+
+ testTwiceBiggerType<T>::run();
+
+ typedef typename MakeUnsigned<T>::Type unsignedT;
+
+ VERIFY(sizeof(unsignedT) == sizeof(T));
+ VERIFY(IsSigned<unsignedT>::value == false);
+
+ const CheckedInt<T> max(MaxValue<T>::value);
+ const CheckedInt<T> min(MinValue<T>::value);
+
+ // Check MinValue and MaxValue, since they are custom implementations and a
+ // mistake there could potentially NOT be caught by any other tests... while
+ // making everything wrong!
+
+ unsignedT bit = 1;
+ unsignedT unsignedMinValue(min.value());
+ unsignedT unsignedMaxValue(max.value());
+ for (size_t i = 0; i < sizeof(T) * CHAR_BIT - 1; i++) {
+ VERIFY((unsignedMinValue & bit) == 0);
+ bit <<= 1;
+ }
+ VERIFY((unsignedMinValue & bit) == (isTSigned ? bit : unsignedT(0)));
+ VERIFY(unsignedMaxValue == unsignedT(~unsignedMinValue));
+
+ const CheckedInt<T> zero(0);
+ const CheckedInt<T> one(1);
+ const CheckedInt<T> two(2);
+ const CheckedInt<T> three(3);
+ const CheckedInt<T> four(4);
+
+ /* Addition / subtraction checks */
+
+ VERIFY_IS_VALID(zero + zero);
+ VERIFY(zero + zero == zero);
+ VERIFY_IS_FALSE(zero + zero == one); // Check == doesn't always return true
+ VERIFY_IS_VALID(zero + one);
+ VERIFY(zero + one == one);
+ VERIFY_IS_VALID(one + one);
+ VERIFY(one + one == two);
+
+ const CheckedInt<T> maxMinusOne = max - one;
+ const CheckedInt<T> maxMinusTwo = max - two;
+ VERIFY_IS_VALID(maxMinusOne);
+ VERIFY_IS_VALID(maxMinusTwo);
+ VERIFY_IS_VALID(maxMinusOne + one);
+ VERIFY_IS_VALID(maxMinusTwo + one);
+ VERIFY_IS_VALID(maxMinusTwo + two);
+ VERIFY(maxMinusOne + one == max);
+ VERIFY(maxMinusTwo + one == maxMinusOne);
+ VERIFY(maxMinusTwo + two == max);
+
+ VERIFY_IS_VALID(max + zero);
+ VERIFY_IS_VALID(max - zero);
+ VERIFY_IS_INVALID(max + one);
+ VERIFY_IS_INVALID(max + two);
+ VERIFY_IS_INVALID(max + maxMinusOne);
+ VERIFY_IS_INVALID(max + max);
+
+ const CheckedInt<T> minPlusOne = min + one;
+ const CheckedInt<T> minPlusTwo = min + two;
+ VERIFY_IS_VALID(minPlusOne);
+ VERIFY_IS_VALID(minPlusTwo);
+ VERIFY_IS_VALID(minPlusOne - one);
+ VERIFY_IS_VALID(minPlusTwo - one);
+ VERIFY_IS_VALID(minPlusTwo - two);
+ VERIFY(minPlusOne - one == min);
+ VERIFY(minPlusTwo - one == minPlusOne);
+ VERIFY(minPlusTwo - two == min);
+
+ const CheckedInt<T> minMinusOne = min - one;
+ VERIFY_IS_VALID(min + zero);
+ VERIFY_IS_VALID(min - zero);
+ VERIFY_IS_INVALID(min - one);
+ VERIFY_IS_INVALID(min - two);
+ VERIFY_IS_INVALID(min - minMinusOne);
+ VERIFY_IS_VALID(min - min);
+
+ const CheckedInt<T> maxOverTwo = max / two;
+ VERIFY_IS_VALID(maxOverTwo + maxOverTwo);
+ VERIFY_IS_VALID(maxOverTwo + one);
+ VERIFY((maxOverTwo + one) - one == maxOverTwo);
+ VERIFY_IS_VALID(maxOverTwo - maxOverTwo);
+ VERIFY(maxOverTwo - maxOverTwo == zero);
+
+ const CheckedInt<T> minOverTwo = min / two;
+ VERIFY_IS_VALID(minOverTwo + minOverTwo);
+ VERIFY_IS_VALID(minOverTwo + one);
+ VERIFY((minOverTwo + one) - one == minOverTwo);
+ VERIFY_IS_VALID(minOverTwo - minOverTwo);
+ VERIFY(minOverTwo - minOverTwo == zero);
+
+ VERIFY_IS_INVALID(min - one);
+ VERIFY_IS_INVALID(min - two);
+
+ if (isTSigned) {
+ VERIFY_IS_INVALID(min + min);
+ VERIFY_IS_INVALID(minOverTwo + minOverTwo + minOverTwo);
+ VERIFY_IS_INVALID(zero - min + min);
+ VERIFY_IS_INVALID(one - min + min);
+ }
+
+ /* Modulo checks */
+ VERIFY_IS_INVALID(zero % zero);
+ VERIFY_IS_INVALID(one % zero);
+ VERIFY_IS_VALID(zero % one);
+ VERIFY_IS_VALID(zero % max);
+ VERIFY_IS_VALID(one % max);
+ VERIFY_IS_VALID(max % one);
+ VERIFY_IS_VALID(max % max);
+ if (isTSigned) {
+ const CheckedInt<T> minusOne = zero - one;
+ VERIFY_IS_INVALID(minusOne % minusOne);
+ VERIFY_IS_INVALID(zero % minusOne);
+ VERIFY_IS_INVALID(one % minusOne);
+ VERIFY_IS_INVALID(minusOne % one);
+
+ VERIFY_IS_INVALID(min % min);
+ VERIFY_IS_INVALID(zero % min);
+ VERIFY_IS_INVALID(min % one);
+ }
+
+ /* Unary operator- checks */
+
+ const CheckedInt<T> negOne = -one;
+ const CheckedInt<T> negTwo = -two;
+
+ if (isTSigned) {
+ VERIFY_IS_VALID(-max);
+ VERIFY_IS_INVALID(-min);
+ VERIFY(-max - min == one);
+ VERIFY_IS_VALID(-max - one);
+ VERIFY_IS_VALID(negOne);
+ VERIFY_IS_VALID(-max + negOne);
+ VERIFY_IS_VALID(negOne + one);
+ VERIFY(negOne + one == zero);
+ VERIFY_IS_VALID(negTwo);
+ VERIFY_IS_VALID(negOne + negOne);
+ VERIFY(negOne + negOne == negTwo);
+ } else {
+ VERIFY_IS_INVALID(-max);
+ VERIFY_IS_VALID(-min);
+ VERIFY(min == zero);
+ VERIFY_IS_INVALID(negOne);
+ }
+
+ /* multiplication checks */
+
+ VERIFY_IS_VALID(zero * zero);
+ VERIFY(zero * zero == zero);
+ VERIFY_IS_VALID(zero * one);
+ VERIFY(zero * one == zero);
+ VERIFY_IS_VALID(one * zero);
+ VERIFY(one * zero == zero);
+ VERIFY_IS_VALID(one * one);
+ VERIFY(one * one == one);
+ VERIFY_IS_VALID(one * three);
+ VERIFY(one * three == three);
+ VERIFY_IS_VALID(two * two);
+ VERIFY(two * two == four);
+
+ VERIFY_IS_INVALID(max * max);
+ VERIFY_IS_INVALID(maxOverTwo * max);
+ VERIFY_IS_INVALID(maxOverTwo * maxOverTwo);
+
+ const CheckedInt<T> maxApproxSqrt(T(T(1) << (CHAR_BIT*sizeof(T)/2)));
+
+ VERIFY_IS_VALID(maxApproxSqrt);
+ VERIFY_IS_VALID(maxApproxSqrt * two);
+ VERIFY_IS_INVALID(maxApproxSqrt * maxApproxSqrt);
+ VERIFY_IS_INVALID(maxApproxSqrt * maxApproxSqrt * maxApproxSqrt);
+
+ if (isTSigned) {
+ VERIFY_IS_INVALID(min * min);
+ VERIFY_IS_INVALID(minOverTwo * min);
+ VERIFY_IS_INVALID(minOverTwo * minOverTwo);
+
+ const CheckedInt<T> minApproxSqrt = -maxApproxSqrt;
+
+ VERIFY_IS_VALID(minApproxSqrt);
+ VERIFY_IS_VALID(minApproxSqrt * two);
+ VERIFY_IS_INVALID(minApproxSqrt * maxApproxSqrt);
+ VERIFY_IS_INVALID(minApproxSqrt * minApproxSqrt);
+ }
+
+ // make sure to check all 4 paths in signed multiplication validity check.
+ // test positive * positive
+ VERIFY_IS_VALID(max * one);
+ VERIFY(max * one == max);
+ VERIFY_IS_INVALID(max * two);
+ VERIFY_IS_VALID(maxOverTwo * two);
+ VERIFY((maxOverTwo + maxOverTwo) == (maxOverTwo * two));
+
+ if (isTSigned) {
+ // test positive * negative
+ VERIFY_IS_VALID(max * negOne);
+ VERIFY_IS_VALID(-max);
+ VERIFY(max * negOne == -max);
+ VERIFY_IS_VALID(one * min);
+ VERIFY_IS_INVALID(max * negTwo);
+ VERIFY_IS_VALID(maxOverTwo * negTwo);
+ VERIFY_IS_VALID(two * minOverTwo);
+ VERIFY_IS_VALID((maxOverTwo + one) * negTwo);
+ VERIFY_IS_INVALID((maxOverTwo + two) * negTwo);
+ VERIFY_IS_INVALID(two * (minOverTwo - one));
+
+ // test negative * positive
+ VERIFY_IS_VALID(min * one);
+ VERIFY_IS_VALID(minPlusOne * one);
+ VERIFY_IS_INVALID(min * two);
+ VERIFY_IS_VALID(minOverTwo * two);
+ VERIFY(minOverTwo * two == min);
+ VERIFY_IS_INVALID((minOverTwo - one) * negTwo);
+ VERIFY_IS_INVALID(negTwo * max);
+ VERIFY_IS_VALID(minOverTwo * two);
+ VERIFY(minOverTwo * two == min);
+ VERIFY_IS_VALID(negTwo * maxOverTwo);
+ VERIFY_IS_INVALID((minOverTwo - one) * two);
+ VERIFY_IS_VALID(negTwo * (maxOverTwo + one));
+ VERIFY_IS_INVALID(negTwo * (maxOverTwo + two));
+
+ // test negative * negative
+ VERIFY_IS_INVALID(min * negOne);
+ VERIFY_IS_VALID(minPlusOne * negOne);
+ VERIFY(minPlusOne * negOne == max);
+ VERIFY_IS_INVALID(min * negTwo);
+ VERIFY_IS_INVALID(minOverTwo * negTwo);
+ VERIFY_IS_INVALID(negOne * min);
+ VERIFY_IS_VALID(negOne * minPlusOne);
+ VERIFY(negOne * minPlusOne == max);
+ VERIFY_IS_INVALID(negTwo * min);
+ VERIFY_IS_INVALID(negTwo * minOverTwo);
+ }
+
+ /* Division checks */
+
+ VERIFY_IS_VALID(one / one);
+ VERIFY(one / one == one);
+ VERIFY_IS_VALID(three / three);
+ VERIFY(three / three == one);
+ VERIFY_IS_VALID(four / two);
+ VERIFY(four / two == two);
+ VERIFY((four*three)/four == three);
+
+ // Check that div by zero is invalid
+ VERIFY_IS_INVALID(zero / zero);
+ VERIFY_IS_INVALID(one / zero);
+ VERIFY_IS_INVALID(two / zero);
+ VERIFY_IS_INVALID(negOne / zero);
+ VERIFY_IS_INVALID(max / zero);
+ VERIFY_IS_INVALID(min / zero);
+
+ if (isTSigned) {
+ // Check that min / -1 is invalid
+ VERIFY_IS_INVALID(min / negOne);
+
+ // Check that the test for div by -1 isn't banning other numerators than min
+ VERIFY_IS_VALID(one / negOne);
+ VERIFY_IS_VALID(zero / negOne);
+ VERIFY_IS_VALID(negOne / negOne);
+ VERIFY_IS_VALID(max / negOne);
+ }
+
+ /* Check that invalidity is correctly preserved by arithmetic ops */
+
+ const CheckedInt<T> someInvalid = max + max;
+ VERIFY_IS_INVALID(someInvalid + zero);
+ VERIFY_IS_INVALID(someInvalid - zero);
+ VERIFY_IS_INVALID(zero + someInvalid);
+ VERIFY_IS_INVALID(zero - someInvalid);
+ VERIFY_IS_INVALID(-someInvalid);
+ VERIFY_IS_INVALID(someInvalid * zero);
+ VERIFY_IS_INVALID(someInvalid * one);
+ VERIFY_IS_INVALID(zero * someInvalid);
+ VERIFY_IS_INVALID(one * someInvalid);
+ VERIFY_IS_INVALID(someInvalid / zero);
+ VERIFY_IS_INVALID(someInvalid / one);
+ VERIFY_IS_INVALID(zero / someInvalid);
+ VERIFY_IS_INVALID(one / someInvalid);
+ VERIFY_IS_INVALID(someInvalid % zero);
+ VERIFY_IS_INVALID(someInvalid % one);
+ VERIFY_IS_INVALID(zero % someInvalid);
+ VERIFY_IS_INVALID(one % someInvalid);
+ VERIFY_IS_INVALID(someInvalid + someInvalid);
+ VERIFY_IS_INVALID(someInvalid - someInvalid);
+ VERIFY_IS_INVALID(someInvalid * someInvalid);
+ VERIFY_IS_INVALID(someInvalid / someInvalid);
+ VERIFY_IS_INVALID(someInvalid % someInvalid);
+
+ // Check that mixing checked integers with plain integers in expressions is
+ // allowed
+
+ VERIFY(one + T(2) == three);
+ VERIFY(2 + one == three);
+ {
+ CheckedInt<T> x = one;
+ x += 2;
+ VERIFY(x == three);
+ }
+ VERIFY(two - 1 == one);
+ VERIFY(2 - one == one);
+ {
+ CheckedInt<T> x = two;
+ x -= 1;
+ VERIFY(x == one);
+ }
+ VERIFY(one * 2 == two);
+ VERIFY(2 * one == two);
+ {
+ CheckedInt<T> x = one;
+ x *= 2;
+ VERIFY(x == two);
+ }
+ VERIFY(four / 2 == two);
+ VERIFY(4 / two == two);
+ {
+ CheckedInt<T> x = four;
+ x /= 2;
+ VERIFY(x == two);
+ }
+ VERIFY(three % 2 == one);
+ VERIFY(3 % two == one);
+ {
+ CheckedInt<T> x = three;
+ x %= 2;
+ VERIFY(x == one);
+ }
+
+ VERIFY(one == 1);
+ VERIFY(1 == one);
+ VERIFY_IS_FALSE(two == 1);
+ VERIFY_IS_FALSE(1 == two);
+ VERIFY_IS_FALSE(someInvalid == 1);
+ VERIFY_IS_FALSE(1 == someInvalid);
+
+ // Check that compound operators work when both sides of the expression
+ // are checked integers
+ {
+ CheckedInt<T> x = one;
+ x += two;
+ VERIFY(x == three);
+ }
+ {
+ CheckedInt<T> x = two;
+ x -= one;
+ VERIFY(x == one);
+ }
+ {
+ CheckedInt<T> x = one;
+ x *= two;
+ VERIFY(x == two);
+ }
+ {
+ CheckedInt<T> x = four;
+ x /= two;
+ VERIFY(x == two);
+ }
+ {
+ CheckedInt<T> x = three;
+ x %= two;
+ VERIFY(x == one);
+ }
+
+ // Check that compound operators work when both sides of the expression
+ // are checked integers and the right-hand side is invalid
+ {
+ CheckedInt<T> x = one;
+ x += someInvalid;
+ VERIFY_IS_INVALID(x);
+ }
+ {
+ CheckedInt<T> x = two;
+ x -= someInvalid;
+ VERIFY_IS_INVALID(x);
+ }
+ {
+ CheckedInt<T> x = one;
+ x *= someInvalid;
+ VERIFY_IS_INVALID(x);
+ }
+ {
+ CheckedInt<T> x = four;
+ x /= someInvalid;
+ VERIFY_IS_INVALID(x);
+ }
+ {
+ CheckedInt<T> x = three;
+ x %= someInvalid;
+ VERIFY_IS_INVALID(x);
+ }
+
+ // Check simple casting between different signedness and sizes.
+ {
+ CheckedInt<uint8_t> foo = CheckedInt<uint16_t>(2).toChecked<uint8_t>();
+ VERIFY_IS_VALID(foo);
+ VERIFY(foo == 2);
+ }
+ {
+ CheckedInt<uint8_t> foo = CheckedInt<uint16_t>(255).toChecked<uint8_t>();
+ VERIFY_IS_VALID(foo);
+ VERIFY(foo == 255);
+ }
+ {
+ CheckedInt<uint8_t> foo = CheckedInt<uint16_t>(256).toChecked<uint8_t>();
+ VERIFY_IS_INVALID(foo);
+ }
+ {
+ CheckedInt<uint8_t> foo = CheckedInt<int8_t>(-2).toChecked<uint8_t>();
+ VERIFY_IS_INVALID(foo);
+ }
+
+ // Check that construction of CheckedInt from an integer value of a
+ // mismatched type is checked Also check casting between all types.
+
+ #define VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE2(U,V,PostVExpr) \
+ { \
+ bool isUSigned = IsSigned<U>::value; \
+ VERIFY_IS_VALID(CheckedInt<T>(V( 0)PostVExpr)); \
+ VERIFY_IS_VALID(CheckedInt<T>(V( 1)PostVExpr)); \
+ VERIFY_IS_VALID(CheckedInt<T>(V(100)PostVExpr)); \
+ if (isUSigned) { \
+ VERIFY_IS_VALID_IF(CheckedInt<T>(V(-1)PostVExpr), isTSigned); \
+ } \
+ if (sizeof(U) > sizeof(T)) { \
+ VERIFY_IS_INVALID(CheckedInt<T>(V(MaxValue<T>::value)PostVExpr + one.value())); \
+ } \
+ VERIFY_IS_VALID_IF(CheckedInt<T>(MaxValue<U>::value), \
+ (sizeof(T) > sizeof(U) || ((sizeof(T) == sizeof(U)) && (isUSigned || !isTSigned)))); \
+ VERIFY_IS_VALID_IF(CheckedInt<T>(MinValue<U>::value), \
+ isUSigned == false ? 1 \
+ : bool(isTSigned) == false ? 0 \
+ : sizeof(T) >= sizeof(U)); \
+ }
+ #define VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(U) \
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE2(U,U,+zero) \
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE2(U,CheckedInt<U>,.toChecked<T>())
+
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(int8_t)
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(uint8_t)
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(int16_t)
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(uint16_t)
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(int32_t)
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(uint32_t)
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(int64_t)
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(uint64_t)
+
+ typedef signed char signedChar;
+ typedef unsigned char unsignedChar;
+ typedef unsigned short unsignedShort;
+ typedef unsigned int unsignedInt;
+ typedef unsigned long unsignedLong;
+ typedef long long longLong;
+ typedef unsigned long long unsignedLongLong;
+
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(char)
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(signedChar)
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(unsignedChar)
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(short)
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(unsignedShort)
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(int)
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(unsignedInt)
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(long)
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(unsignedLong)
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(longLong)
+ VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(unsignedLongLong)
+
+ /* Test increment/decrement operators */
+
+ CheckedInt<T> x, y;
+ x = one;
+ y = x++;
+ VERIFY(x == two);
+ VERIFY(y == one);
+ x = one;
+ y = ++x;
+ VERIFY(x == two);
+ VERIFY(y == two);
+ x = one;
+ y = x--;
+ VERIFY(x == zero);
+ VERIFY(y == one);
+ x = one;
+ y = --x;
+ VERIFY(x == zero);
+ VERIFY(y == zero);
+ x = max;
+ VERIFY_IS_VALID(x++);
+ x = max;
+ VERIFY_IS_INVALID(++x);
+ x = min;
+ VERIFY_IS_VALID(x--);
+ x = min;
+ VERIFY_IS_INVALID(--x);
+
+ gIntegerTypesTested++;
+}
+
+int
+main()
+{
+ test<int8_t>();
+ test<uint8_t>();
+ test<int16_t>();
+ test<uint16_t>();
+ test<int32_t>();
+ test<uint32_t>();
+ test<int64_t>();
+ test<uint64_t>();
+
+ test<char>();
+ test<signed char>();
+ test<unsigned char>();
+ test<short>();
+ test<unsigned short>();
+ test<int>();
+ test<unsigned int>();
+ test<long>();
+ test<unsigned long>();
+ test<long long>();
+ test<unsigned long long>();
+
+ const int MIN_TYPES_TESTED = 9;
+ if (gIntegerTypesTested < MIN_TYPES_TESTED) {
+ std::cerr << "Only " << gIntegerTypesTested << " have been tested. "
+ << "This should not be less than " << MIN_TYPES_TESTED << "."
+ << std::endl;
+ gTestsFailed++;
+ }
+
+ std::cerr << gTestsFailed << " tests failed, "
+ << gTestsPassed << " tests passed out of "
+ << gTestsFailed + gTestsPassed
+ << " tests, covering " << gIntegerTypesTested
+ << " distinct integer types." << std::endl;
+
+ return gTestsFailed > 0;
+}
diff --git a/mfbt/tests/TestCountPopulation.cpp b/mfbt/tests/TestCountPopulation.cpp
new file mode 100644
index 000000000..272a0dc34
--- /dev/null
+++ b/mfbt/tests/TestCountPopulation.cpp
@@ -0,0 +1,34 @@
+/* -*- 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/MathAlgorithms.h"
+
+using mozilla::CountPopulation32;
+
+static void
+TestCountPopulation32()
+{
+ MOZ_RELEASE_ASSERT(CountPopulation32(0xFFFFFFFF) == 32);
+ MOZ_RELEASE_ASSERT(CountPopulation32(0xF0FF1000) == 13);
+ MOZ_RELEASE_ASSERT(CountPopulation32(0x7F8F0001) == 13);
+ MOZ_RELEASE_ASSERT(CountPopulation32(0x3FFF0100) == 15);
+ MOZ_RELEASE_ASSERT(CountPopulation32(0x1FF50010) == 12);
+ MOZ_RELEASE_ASSERT(CountPopulation32(0x00800000) == 1);
+ MOZ_RELEASE_ASSERT(CountPopulation32(0x00400000) == 1);
+ MOZ_RELEASE_ASSERT(CountPopulation32(0x00008000) == 1);
+ MOZ_RELEASE_ASSERT(CountPopulation32(0x00004000) == 1);
+ MOZ_RELEASE_ASSERT(CountPopulation32(0x00000080) == 1);
+ MOZ_RELEASE_ASSERT(CountPopulation32(0x00000040) == 1);
+ MOZ_RELEASE_ASSERT(CountPopulation32(0x00000001) == 1);
+ MOZ_RELEASE_ASSERT(CountPopulation32(0x00000000) == 0);
+}
+
+int
+main()
+{
+ TestCountPopulation32();
+ return 0;
+}
diff --git a/mfbt/tests/TestCountZeroes.cpp b/mfbt/tests/TestCountZeroes.cpp
new file mode 100644
index 000000000..799ee8421
--- /dev/null
+++ b/mfbt/tests/TestCountZeroes.cpp
@@ -0,0 +1,102 @@
+/* -*- 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/MathAlgorithms.h"
+
+using mozilla::CountLeadingZeroes32;
+using mozilla::CountLeadingZeroes64;
+using mozilla::CountTrailingZeroes32;
+using mozilla::CountTrailingZeroes64;
+
+static void
+TestLeadingZeroes32()
+{
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0xF0FF1000) == 0);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0x7F8F0001) == 1);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0x3FFF0100) == 2);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0x1FF50010) == 3);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0x00800000) == 8);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0x00400000) == 9);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0x00008000) == 16);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0x00004000) == 17);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0x00000080) == 24);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0x00000040) == 25);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0x00000001) == 31);
+}
+
+static void
+TestLeadingZeroes64()
+{
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0xF000F0F010000000) == 0);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x70F080F000000001) == 1);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x30F0F0F000100000) == 2);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x10F0F05000000100) == 3);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0080000000000001) == 8);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0040000010001000) == 9);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x000080F010000000) == 16);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x000040F010000000) == 17);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000008000100100) == 24);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000004100010010) == 25);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000000080100100) == 32);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000000041001010) == 33);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000000000800100) == 40);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000000000411010) == 41);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000000000008001) == 48);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000000000004010) == 49);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000000000000081) == 56);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000000000000040) == 57);
+ MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000000000000001) == 63);
+}
+
+static void
+TestTrailingZeroes32()
+{
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0x0100FFFF) == 0);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0x7000FFFE) == 1);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0x0080FFFC) == 2);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0x0080FFF8) == 3);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0x010FFF00) == 8);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0x7000FE00) == 9);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0x10CF0000) == 16);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0x0BDE0000) == 17);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0x0F000000) == 24);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0xDE000000) == 25);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0x80000000) == 31);
+}
+
+static void
+TestTrailingZeroes64()
+{
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x000100000F0F0F0F) == 0);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x070000000F0F0F0E) == 1);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x000008000F0F0F0C) == 2);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x000008000F0F0F08) == 3);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0xC001000F0F0F0F00) == 8);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x0200000F0F0F0E00) == 9);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0xB0C10F0FEFDF0000) == 16);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x0AAA00F0FF0E0000) == 17);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0xD010F0FEDF000000) == 24);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x7AAF0CF0BE000000) == 25);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x20F0A5D100000000) == 32);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x489BF0B200000000) == 33);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0xE0F0D10000000000) == 40);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x97F0B20000000000) == 41);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x2C07000000000000) == 48);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x1FBA000000000000) == 49);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x0100000000000000) == 56);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x0200000000000000) == 57);
+ MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x8000000000000000) == 63);
+}
+
+int
+main()
+{
+ TestLeadingZeroes32();
+ TestLeadingZeroes64();
+ TestTrailingZeroes32();
+ TestTrailingZeroes64();
+ return 0;
+}
diff --git a/mfbt/tests/TestEndian.cpp b/mfbt/tests/TestEndian.cpp
new file mode 100644
index 000000000..1fb8ac98f
--- /dev/null
+++ b/mfbt/tests/TestEndian.cpp
@@ -0,0 +1,472 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/EndianUtils.h"
+
+#include <stddef.h>
+
+using mozilla::BigEndian;
+using mozilla::LittleEndian;
+using mozilla::NativeEndian;
+
+template<typename T>
+void
+TestSingleSwap(T aValue, T aSwappedValue)
+{
+#if MOZ_LITTLE_ENDIAN
+ MOZ_RELEASE_ASSERT(NativeEndian::swapToBigEndian(aValue) == aSwappedValue);
+ MOZ_RELEASE_ASSERT(NativeEndian::swapFromBigEndian(aValue) == aSwappedValue);
+ MOZ_RELEASE_ASSERT(NativeEndian::swapToNetworkOrder(aValue) == aSwappedValue);
+ MOZ_RELEASE_ASSERT(NativeEndian::swapFromNetworkOrder(aValue) ==
+ aSwappedValue);
+#else
+ MOZ_RELEASE_ASSERT(NativeEndian::swapToLittleEndian(aValue) == aSwappedValue);
+ MOZ_RELEASE_ASSERT(NativeEndian::swapFromLittleEndian(aValue) ==
+ aSwappedValue);
+#endif
+}
+
+template<typename T>
+void
+TestSingleNoSwap(T aValue, T aUnswappedValue)
+{
+#if MOZ_LITTLE_ENDIAN
+ MOZ_RELEASE_ASSERT(NativeEndian::swapToLittleEndian(aValue) ==
+ aUnswappedValue);
+ MOZ_RELEASE_ASSERT(NativeEndian::swapFromLittleEndian(aValue) ==
+ aUnswappedValue);
+#else
+ MOZ_RELEASE_ASSERT(NativeEndian::swapToBigEndian(aValue) == aUnswappedValue);
+ MOZ_RELEASE_ASSERT(NativeEndian::swapFromBigEndian(aValue) == aUnswappedValue);
+ MOZ_RELEASE_ASSERT(NativeEndian::swapToNetworkOrder(aValue) ==
+ aUnswappedValue);
+ MOZ_RELEASE_ASSERT(NativeEndian::swapFromNetworkOrder(aValue) ==
+ aUnswappedValue);
+#endif
+}
+
+// EndianUtils.h functions are declared as protected in a base class and
+// then re-exported as public in public derived classes. The
+// standardese around explicit instantiation of templates is not clear
+// in such cases. Provide these wrappers to make things more explicit.
+// For your own enlightenment, you may wish to peruse:
+// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56152 and subsequently
+// http://j.mp/XosS6S .
+#define WRAP_COPYTO(NAME) \
+ template<typename T> \
+ void \
+ NAME(void* aDst, const T* aSrc, size_t aCount) \
+ { \
+ NativeEndian::NAME<T>(aDst, aSrc, aCount); \
+ }
+
+WRAP_COPYTO(copyAndSwapToLittleEndian)
+WRAP_COPYTO(copyAndSwapToBigEndian)
+WRAP_COPYTO(copyAndSwapToNetworkOrder)
+
+#define WRAP_COPYFROM(NAME) \
+ template<typename T> \
+ void \
+ NAME(T* aDst, const void* aSrc, size_t aCount) \
+ { \
+ NativeEndian::NAME<T>(aDst, aSrc, aCount); \
+ }
+
+WRAP_COPYFROM(copyAndSwapFromLittleEndian)
+WRAP_COPYFROM(copyAndSwapFromBigEndian)
+WRAP_COPYFROM(copyAndSwapFromNetworkOrder)
+
+#define WRAP_IN_PLACE(NAME) \
+ template<typename T> \
+ void \
+ NAME(T* aP, size_t aCount) \
+ { \
+ NativeEndian::NAME<T>(aP, aCount); \
+ }
+WRAP_IN_PLACE(swapToLittleEndianInPlace)
+WRAP_IN_PLACE(swapFromLittleEndianInPlace)
+WRAP_IN_PLACE(swapToBigEndianInPlace)
+WRAP_IN_PLACE(swapFromBigEndianInPlace)
+WRAP_IN_PLACE(swapToNetworkOrderInPlace)
+WRAP_IN_PLACE(swapFromNetworkOrderInPlace)
+
+enum SwapExpectation
+{
+ Swap,
+ NoSwap
+};
+
+template<typename T, size_t Count>
+void
+TestBulkSwapToSub(enum SwapExpectation aExpectSwap,
+ const T (&aValues)[Count],
+ void (*aSwapperFunc)(void*, const T*, size_t),
+ T (*aReaderFunc)(const void*))
+{
+ const size_t arraySize = 2 * Count;
+ const size_t bufferSize = arraySize * sizeof(T);
+ static uint8_t buffer[bufferSize];
+ const uint8_t fillValue = 0xa5;
+ static uint8_t checkBuffer[bufferSize];
+
+ MOZ_RELEASE_ASSERT(bufferSize > 2 * sizeof(T));
+
+ memset(checkBuffer, fillValue, bufferSize);
+
+ for (size_t startPosition = 0; startPosition < sizeof(T); ++startPosition) {
+ for (size_t nValues = 0; nValues < Count; ++nValues) {
+ memset(buffer, fillValue, bufferSize);
+ aSwapperFunc(buffer + startPosition, aValues, nValues);
+
+ MOZ_RELEASE_ASSERT(memcmp(buffer, checkBuffer, startPosition) == 0);
+ size_t valuesEndPosition = startPosition + sizeof(T) * nValues;
+ MOZ_RELEASE_ASSERT(memcmp(buffer + valuesEndPosition,
+ checkBuffer + valuesEndPosition,
+ bufferSize - valuesEndPosition) == 0);
+ if (aExpectSwap == NoSwap) {
+ MOZ_RELEASE_ASSERT(memcmp(buffer + startPosition, aValues,
+ nValues * sizeof(T)) == 0);
+ }
+ for (size_t i = 0; i < nValues; ++i) {
+ MOZ_RELEASE_ASSERT(
+ aReaderFunc(buffer + startPosition + sizeof(T) * i) == aValues[i]);
+ }
+ }
+ }
+}
+
+template<typename T, size_t Count>
+void
+TestBulkSwapFromSub(enum SwapExpectation aExpectSwap,
+ const T (&aValues)[Count],
+ void (*aSwapperFunc)(T*, const void*, size_t),
+ T (*aReaderFunc)(const void*))
+{
+ const size_t arraySize = 2 * Count;
+ const size_t bufferSize = arraySize * sizeof(T);
+ static T buffer[arraySize];
+ const uint8_t fillValue = 0xa5;
+ static T checkBuffer[arraySize];
+
+ memset(checkBuffer, fillValue, bufferSize);
+
+ for (size_t startPosition = 0; startPosition < Count; ++startPosition) {
+ for (size_t nValues = 0; nValues < (Count - startPosition); ++nValues) {
+ memset(buffer, fillValue, bufferSize);
+ aSwapperFunc(buffer + startPosition, aValues, nValues);
+
+ MOZ_RELEASE_ASSERT(
+ memcmp(buffer, checkBuffer, startPosition * sizeof(T)) == 0);
+ size_t valuesEndPosition = startPosition + nValues;
+ MOZ_RELEASE_ASSERT(memcmp(buffer + valuesEndPosition,
+ checkBuffer + valuesEndPosition,
+ (arraySize - valuesEndPosition) * sizeof(T)) == 0);
+ if (aExpectSwap == NoSwap) {
+ MOZ_RELEASE_ASSERT(memcmp(buffer + startPosition, aValues,
+ nValues * sizeof(T)) == 0);
+ }
+ for (size_t i = 0; i < nValues; ++i) {
+ MOZ_RELEASE_ASSERT(aReaderFunc(buffer + startPosition + i) == aValues[i]);
+ }
+ }
+ }
+}
+
+
+template<typename T, size_t Count>
+void
+TestBulkInPlaceSub(enum SwapExpectation aExpectSwap,
+ const T (&aValues)[Count],
+ void (*aSwapperFunc)(T*, size_t),
+ T (*aReaderFunc)(const void*))
+{
+ const size_t bufferCount = 4 * Count;
+ const size_t bufferSize = bufferCount * sizeof(T);
+ static T buffer[bufferCount];
+ const T fillValue = 0xa5;
+ static T checkBuffer[bufferCount];
+
+ MOZ_RELEASE_ASSERT(bufferSize > 2 * sizeof(T));
+
+ memset(checkBuffer, fillValue, bufferSize);
+
+ for (size_t startPosition = 0; startPosition < Count; ++startPosition) {
+ for (size_t nValues = 0; nValues < Count; ++nValues) {
+ memset(buffer, fillValue, bufferSize);
+ memcpy(buffer + startPosition, aValues, nValues * sizeof(T));
+ aSwapperFunc(buffer + startPosition, nValues);
+
+ MOZ_RELEASE_ASSERT(
+ memcmp(buffer, checkBuffer, startPosition * sizeof(T)) == 0);
+ size_t valuesEndPosition = startPosition + nValues;
+ MOZ_RELEASE_ASSERT(memcmp(buffer + valuesEndPosition,
+ checkBuffer + valuesEndPosition,
+ bufferSize - valuesEndPosition * sizeof(T)) == 0);
+ if (aExpectSwap == NoSwap) {
+ MOZ_RELEASE_ASSERT(memcmp(buffer + startPosition, aValues,
+ nValues * sizeof(T)) == 0);
+ }
+ for (size_t i = 0; i < nValues; ++i) {
+ MOZ_RELEASE_ASSERT(aReaderFunc(buffer + startPosition + i) == aValues[i]);
+ }
+ }
+ }
+}
+
+template<typename T>
+struct Reader
+{
+};
+
+#define SPECIALIZE_READER(TYPE, READ_FUNC) \
+ template<> \
+ struct Reader<TYPE> \
+ { \
+ static TYPE readLE(const void* aP) { return LittleEndian::READ_FUNC(aP); }\
+ static TYPE readBE(const void* aP) { return BigEndian::READ_FUNC(aP); } \
+ };
+
+SPECIALIZE_READER(uint16_t, readUint16)
+SPECIALIZE_READER(uint32_t, readUint32)
+SPECIALIZE_READER(uint64_t, readUint64)
+SPECIALIZE_READER(int16_t, readInt16)
+SPECIALIZE_READER(int32_t, readInt32)
+SPECIALIZE_READER(int64_t, readInt64)
+
+template<typename T, size_t Count>
+void
+TestBulkSwap(const T (&aBytes)[Count])
+{
+#if MOZ_LITTLE_ENDIAN
+ TestBulkSwapToSub(Swap, aBytes, copyAndSwapToBigEndian<T>,
+ Reader<T>::readBE);
+ TestBulkSwapFromSub(Swap, aBytes, copyAndSwapFromBigEndian<T>,
+ Reader<T>::readBE);
+ TestBulkSwapToSub(Swap, aBytes, copyAndSwapToNetworkOrder<T>,
+ Reader<T>::readBE);
+ TestBulkSwapFromSub(Swap, aBytes, copyAndSwapFromNetworkOrder<T>,
+ Reader<T>::readBE);
+#else
+ TestBulkSwapToSub(Swap, aBytes, copyAndSwapToLittleEndian<T>,
+ Reader<T>::readLE);
+ TestBulkSwapFromSub(Swap, aBytes, copyAndSwapFromLittleEndian<T>,
+ Reader<T>::readLE);
+#endif
+}
+
+template<typename T, size_t Count>
+void
+TestBulkNoSwap(const T (&aBytes)[Count])
+{
+#if MOZ_LITTLE_ENDIAN
+ TestBulkSwapToSub(NoSwap, aBytes, copyAndSwapToLittleEndian<T>,
+ Reader<T>::readLE);
+ TestBulkSwapFromSub(NoSwap, aBytes, copyAndSwapFromLittleEndian<T>,
+ Reader<T>::readLE);
+#else
+ TestBulkSwapToSub(NoSwap, aBytes, copyAndSwapToBigEndian<T>,
+ Reader<T>::readBE);
+ TestBulkSwapFromSub(NoSwap, aBytes, copyAndSwapFromBigEndian<T>,
+ Reader<T>::readBE);
+ TestBulkSwapToSub(NoSwap, aBytes, copyAndSwapToNetworkOrder<T>,
+ Reader<T>::readBE);
+ TestBulkSwapFromSub(NoSwap, aBytes, copyAndSwapFromNetworkOrder<T>,
+ Reader<T>::readBE);
+#endif
+}
+
+template<typename T, size_t Count>
+void
+TestBulkInPlaceSwap(const T (&aBytes)[Count])
+{
+#if MOZ_LITTLE_ENDIAN
+ TestBulkInPlaceSub(Swap, aBytes, swapToBigEndianInPlace<T>,
+ Reader<T>::readBE);
+ TestBulkInPlaceSub(Swap, aBytes, swapFromBigEndianInPlace<T>,
+ Reader<T>::readBE);
+ TestBulkInPlaceSub(Swap, aBytes, swapToNetworkOrderInPlace<T>,
+ Reader<T>::readBE);
+ TestBulkInPlaceSub(Swap, aBytes, swapFromNetworkOrderInPlace<T>,
+ Reader<T>::readBE);
+#else
+ TestBulkInPlaceSub(Swap, aBytes, swapToLittleEndianInPlace<T>,
+ Reader<T>::readLE);
+ TestBulkInPlaceSub(Swap, aBytes, swapFromLittleEndianInPlace<T>,
+ Reader<T>::readLE);
+#endif
+}
+
+template<typename T, size_t Count>
+void
+TestBulkInPlaceNoSwap(const T (&aBytes)[Count])
+{
+#if MOZ_LITTLE_ENDIAN
+ TestBulkInPlaceSub(NoSwap, aBytes, swapToLittleEndianInPlace<T>,
+ Reader<T>::readLE);
+ TestBulkInPlaceSub(NoSwap, aBytes, swapFromLittleEndianInPlace<T>,
+ Reader<T>::readLE);
+#else
+ TestBulkInPlaceSub(NoSwap, aBytes, swapToBigEndianInPlace<T>,
+ Reader<T>::readBE);
+ TestBulkInPlaceSub(NoSwap, aBytes, swapFromBigEndianInPlace<T>,
+ Reader<T>::readBE);
+ TestBulkInPlaceSub(NoSwap, aBytes, swapToNetworkOrderInPlace<T>,
+ Reader<T>::readBE);
+ TestBulkInPlaceSub(NoSwap, aBytes, swapFromNetworkOrderInPlace<T>,
+ Reader<T>::readBE);
+#endif
+}
+
+int
+main()
+{
+ static const uint8_t unsigned_bytes[16] = {
+ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
+ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8
+ };
+ static const int8_t signed_bytes[16] = {
+ -0x0f, -0x0e, -0x0d, -0x0c, -0x0b, -0x0a, -0x09, -0x08,
+ -0x0f, -0x0e, -0x0d, -0x0c, -0x0b, -0x0a, -0x09, -0x08
+ };
+ static const uint16_t uint16_values[8] = {
+ 0x102, 0x304, 0x506, 0x708, 0x102, 0x304, 0x506, 0x708
+ };
+ static const int16_t int16_values[8] = {
+ int16_t(0xf1f2), int16_t(0xf3f4), int16_t(0xf5f6), int16_t(0xf7f8),
+ int16_t(0xf1f2), int16_t(0xf3f4), int16_t(0xf5f6), int16_t(0xf7f8)
+ };
+ static const uint32_t uint32_values[4] = {
+ 0x1020304, 0x5060708, 0x1020304, 0x5060708
+ };
+ static const int32_t int32_values[4] = {
+ int32_t(0xf1f2f3f4), int32_t(0xf5f6f7f8),
+ int32_t(0xf1f2f3f4), int32_t(0xf5f6f7f8)
+ };
+ static const uint64_t uint64_values[2] = {
+ 0x102030405060708, 0x102030405060708
+ };
+ static const int64_t int64_values[2] = {
+ int64_t(0xf1f2f3f4f5f6f7f8), int64_t(0xf1f2f3f4f5f6f7f8)
+ };
+ uint8_t buffer[8];
+
+ MOZ_RELEASE_ASSERT(LittleEndian::readUint16(&unsigned_bytes[0]) == 0x201);
+ MOZ_RELEASE_ASSERT(BigEndian::readUint16(&unsigned_bytes[0]) == 0x102);
+
+ MOZ_RELEASE_ASSERT(
+ LittleEndian::readUint32(&unsigned_bytes[0]) == 0x4030201U);
+ MOZ_RELEASE_ASSERT(
+ BigEndian::readUint32(&unsigned_bytes[0]) == 0x1020304U);
+
+ MOZ_RELEASE_ASSERT(
+ LittleEndian::readUint64(&unsigned_bytes[0]) == 0x807060504030201ULL);
+ MOZ_RELEASE_ASSERT(
+ BigEndian::readUint64(&unsigned_bytes[0]) == 0x102030405060708ULL);
+
+ LittleEndian::writeUint16(&buffer[0], 0x201);
+ MOZ_RELEASE_ASSERT(
+ memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint16_t)) == 0);
+ BigEndian::writeUint16(&buffer[0], 0x102);
+ MOZ_RELEASE_ASSERT(
+ memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint16_t)) == 0);
+
+ LittleEndian::writeUint32(&buffer[0], 0x4030201U);
+ MOZ_RELEASE_ASSERT(
+ memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint32_t)) == 0);
+ BigEndian::writeUint32(&buffer[0], 0x1020304U);
+ MOZ_RELEASE_ASSERT(
+ memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint32_t)) == 0);
+
+ LittleEndian::writeUint64(&buffer[0], 0x807060504030201ULL);
+ MOZ_RELEASE_ASSERT(
+ memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint64_t)) == 0);
+ BigEndian::writeUint64(&buffer[0], 0x102030405060708ULL);
+ MOZ_RELEASE_ASSERT(
+ memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint64_t)) == 0);
+
+ MOZ_RELEASE_ASSERT(
+ LittleEndian::readInt16(&signed_bytes[0]) == int16_t(0xf2f1));
+ MOZ_RELEASE_ASSERT(
+ BigEndian::readInt16(&signed_bytes[0]) == int16_t(0xf1f2));
+
+ MOZ_RELEASE_ASSERT(
+ LittleEndian::readInt32(&signed_bytes[0]) == int32_t(0xf4f3f2f1));
+ MOZ_RELEASE_ASSERT(
+ BigEndian::readInt32(&signed_bytes[0]) == int32_t(0xf1f2f3f4));
+
+ MOZ_RELEASE_ASSERT(
+ LittleEndian::readInt64(&signed_bytes[0]) == int64_t(0xf8f7f6f5f4f3f2f1LL));
+ MOZ_RELEASE_ASSERT(
+ BigEndian::readInt64(&signed_bytes[0]) == int64_t(0xf1f2f3f4f5f6f7f8LL));
+
+ LittleEndian::writeInt16(&buffer[0], int16_t(0xf2f1));
+ MOZ_RELEASE_ASSERT(
+ memcmp(&signed_bytes[0], &buffer[0], sizeof(int16_t)) == 0);
+ BigEndian::writeInt16(&buffer[0], int16_t(0xf1f2));
+ MOZ_RELEASE_ASSERT(
+ memcmp(&signed_bytes[0], &buffer[0], sizeof(int16_t)) == 0);
+
+ LittleEndian::writeInt32(&buffer[0], 0xf4f3f2f1);
+ MOZ_RELEASE_ASSERT(
+ memcmp(&signed_bytes[0], &buffer[0], sizeof(int32_t)) == 0);
+ BigEndian::writeInt32(&buffer[0], 0xf1f2f3f4);
+ MOZ_RELEASE_ASSERT(
+ memcmp(&signed_bytes[0], &buffer[0], sizeof(int32_t)) == 0);
+
+ LittleEndian::writeInt64(&buffer[0], 0xf8f7f6f5f4f3f2f1LL);
+ MOZ_RELEASE_ASSERT(
+ memcmp(&signed_bytes[0], &buffer[0], sizeof(int64_t)) == 0);
+ BigEndian::writeInt64(&buffer[0], 0xf1f2f3f4f5f6f7f8LL);
+ MOZ_RELEASE_ASSERT(
+ memcmp(&signed_bytes[0], &buffer[0], sizeof(int64_t)) == 0);
+
+ TestSingleSwap(uint16_t(0xf2f1), uint16_t(0xf1f2));
+ TestSingleSwap(uint32_t(0xf4f3f2f1), uint32_t(0xf1f2f3f4));
+ TestSingleSwap(uint64_t(0xf8f7f6f5f4f3f2f1), uint64_t(0xf1f2f3f4f5f6f7f8));
+
+ TestSingleSwap(int16_t(0xf2f1), int16_t(0xf1f2));
+ TestSingleSwap(int32_t(0xf4f3f2f1), int32_t(0xf1f2f3f4));
+ TestSingleSwap(int64_t(0xf8f7f6f5f4f3f2f1), int64_t(0xf1f2f3f4f5f6f7f8));
+
+ TestSingleNoSwap(uint16_t(0xf2f1), uint16_t(0xf2f1));
+ TestSingleNoSwap(uint32_t(0xf4f3f2f1), uint32_t(0xf4f3f2f1));
+ TestSingleNoSwap(uint64_t(0xf8f7f6f5f4f3f2f1), uint64_t(0xf8f7f6f5f4f3f2f1));
+
+ TestSingleNoSwap(int16_t(0xf2f1), int16_t(0xf2f1));
+ TestSingleNoSwap(int32_t(0xf4f3f2f1), int32_t(0xf4f3f2f1));
+ TestSingleNoSwap(int64_t(0xf8f7f6f5f4f3f2f1), int64_t(0xf8f7f6f5f4f3f2f1));
+
+ TestBulkSwap(uint16_values);
+ TestBulkSwap(int16_values);
+ TestBulkSwap(uint32_values);
+ TestBulkSwap(int32_values);
+ TestBulkSwap(uint64_values);
+ TestBulkSwap(int64_values);
+
+ TestBulkNoSwap(uint16_values);
+ TestBulkNoSwap(int16_values);
+ TestBulkNoSwap(uint32_values);
+ TestBulkNoSwap(int32_values);
+ TestBulkNoSwap(uint64_values);
+ TestBulkNoSwap(int64_values);
+
+ TestBulkInPlaceSwap(uint16_values);
+ TestBulkInPlaceSwap(int16_values);
+ TestBulkInPlaceSwap(uint32_values);
+ TestBulkInPlaceSwap(int32_values);
+ TestBulkInPlaceSwap(uint64_values);
+ TestBulkInPlaceSwap(int64_values);
+
+ TestBulkInPlaceNoSwap(uint16_values);
+ TestBulkInPlaceNoSwap(int16_values);
+ TestBulkInPlaceNoSwap(uint32_values);
+ TestBulkInPlaceNoSwap(int32_values);
+ TestBulkInPlaceNoSwap(uint64_values);
+ TestBulkInPlaceNoSwap(int64_values);
+
+ return 0;
+}
diff --git a/mfbt/tests/TestEnumSet.cpp b/mfbt/tests/TestEnumSet.cpp
new file mode 100644
index 000000000..801295fd6
--- /dev/null
+++ b/mfbt/tests/TestEnumSet.cpp
@@ -0,0 +1,289 @@
+/* -*- 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/EnumSet.h"
+#include "mozilla/Vector.h"
+
+using namespace mozilla;
+
+enum SeaBird
+{
+ PENGUIN,
+ ALBATROSS,
+ FULMAR,
+ PRION,
+ SHEARWATER,
+ GADFLY_PETREL,
+ TRUE_PETREL,
+ DIVING_PETREL,
+ STORM_PETREL,
+ PELICAN,
+ GANNET,
+ BOOBY,
+ CORMORANT,
+ FRIGATEBIRD,
+ TROPICBIRD,
+ SKUA,
+ GULL,
+ TERN,
+ SKIMMER,
+ AUK
+};
+
+class EnumSetSuite
+{
+public:
+ EnumSetSuite()
+ : mAlcidae()
+ , mDiomedeidae(ALBATROSS)
+ , mPetrelProcellariidae(GADFLY_PETREL, TRUE_PETREL)
+ , mNonPetrelProcellariidae(FULMAR, PRION, SHEARWATER)
+ , mPetrels(GADFLY_PETREL, TRUE_PETREL, DIVING_PETREL, STORM_PETREL)
+ { }
+
+ void runTests()
+ {
+ testSize();
+ testContains();
+ testAddTo();
+ testAdd();
+ testAddAll();
+ testUnion();
+ testRemoveFrom();
+ testRemove();
+ testRemoveAllFrom();
+ testRemoveAll();
+ testIntersect();
+ testInsersection();
+ testEquality();
+ testDuplicates();
+ testIteration();
+ testInitializerListConstuctor();
+ }
+
+private:
+ void testSize()
+ {
+ MOZ_RELEASE_ASSERT(mAlcidae.size() == 0);
+ MOZ_RELEASE_ASSERT(mDiomedeidae.size() == 1);
+ MOZ_RELEASE_ASSERT(mPetrelProcellariidae.size() == 2);
+ MOZ_RELEASE_ASSERT(mNonPetrelProcellariidae.size() == 3);
+ MOZ_RELEASE_ASSERT(mPetrels.size() == 4);
+ }
+
+ void testContains()
+ {
+ MOZ_RELEASE_ASSERT(!mPetrels.contains(PENGUIN));
+ MOZ_RELEASE_ASSERT(!mPetrels.contains(ALBATROSS));
+ MOZ_RELEASE_ASSERT(!mPetrels.contains(FULMAR));
+ MOZ_RELEASE_ASSERT(!mPetrels.contains(PRION));
+ MOZ_RELEASE_ASSERT(!mPetrels.contains(SHEARWATER));
+ MOZ_RELEASE_ASSERT(mPetrels.contains(GADFLY_PETREL));
+ MOZ_RELEASE_ASSERT(mPetrels.contains(TRUE_PETREL));
+ MOZ_RELEASE_ASSERT(mPetrels.contains(DIVING_PETREL));
+ MOZ_RELEASE_ASSERT(mPetrels.contains(STORM_PETREL));
+ MOZ_RELEASE_ASSERT(!mPetrels.contains(PELICAN));
+ MOZ_RELEASE_ASSERT(!mPetrels.contains(GANNET));
+ MOZ_RELEASE_ASSERT(!mPetrels.contains(BOOBY));
+ MOZ_RELEASE_ASSERT(!mPetrels.contains(CORMORANT));
+ MOZ_RELEASE_ASSERT(!mPetrels.contains(FRIGATEBIRD));
+ MOZ_RELEASE_ASSERT(!mPetrels.contains(TROPICBIRD));
+ MOZ_RELEASE_ASSERT(!mPetrels.contains(SKUA));
+ MOZ_RELEASE_ASSERT(!mPetrels.contains(GULL));
+ MOZ_RELEASE_ASSERT(!mPetrels.contains(TERN));
+ MOZ_RELEASE_ASSERT(!mPetrels.contains(SKIMMER));
+ MOZ_RELEASE_ASSERT(!mPetrels.contains(AUK));
+ }
+
+ void testCopy()
+ {
+ EnumSet<SeaBird> likes = mPetrels;
+ likes -= TRUE_PETREL;
+ MOZ_RELEASE_ASSERT(mPetrels.size() == 4);
+ MOZ_RELEASE_ASSERT(mPetrels.contains(TRUE_PETREL));
+
+ MOZ_RELEASE_ASSERT(likes.size() == 3);
+ MOZ_RELEASE_ASSERT(likes.contains(GADFLY_PETREL));
+ MOZ_RELEASE_ASSERT(likes.contains(DIVING_PETREL));
+ MOZ_RELEASE_ASSERT(likes.contains(STORM_PETREL));
+ }
+
+ void testAddTo()
+ {
+ EnumSet<SeaBird> seen = mPetrels;
+ seen += CORMORANT;
+ seen += TRUE_PETREL;
+ MOZ_RELEASE_ASSERT(mPetrels.size() == 4);
+ MOZ_RELEASE_ASSERT(!mPetrels.contains(CORMORANT));
+ MOZ_RELEASE_ASSERT(seen.size() == 5);
+ MOZ_RELEASE_ASSERT(seen.contains(GADFLY_PETREL));
+ MOZ_RELEASE_ASSERT(seen.contains(TRUE_PETREL));
+ MOZ_RELEASE_ASSERT(seen.contains(DIVING_PETREL));
+ MOZ_RELEASE_ASSERT(seen.contains(STORM_PETREL));
+ MOZ_RELEASE_ASSERT(seen.contains(CORMORANT));
+ }
+
+ void testAdd()
+ {
+ EnumSet<SeaBird> seen = mPetrels + CORMORANT + STORM_PETREL;
+ MOZ_RELEASE_ASSERT(mPetrels.size() == 4);
+ MOZ_RELEASE_ASSERT(!mPetrels.contains(CORMORANT));
+ MOZ_RELEASE_ASSERT(seen.size() == 5);
+ MOZ_RELEASE_ASSERT(seen.contains(GADFLY_PETREL));
+ MOZ_RELEASE_ASSERT(seen.contains(TRUE_PETREL));
+ MOZ_RELEASE_ASSERT(seen.contains(DIVING_PETREL));
+ MOZ_RELEASE_ASSERT(seen.contains(STORM_PETREL));
+ MOZ_RELEASE_ASSERT(seen.contains(CORMORANT));
+ }
+
+ void testAddAll()
+ {
+ EnumSet<SeaBird> procellariidae;
+ procellariidae += mPetrelProcellariidae;
+ procellariidae += mNonPetrelProcellariidae;
+ MOZ_RELEASE_ASSERT(procellariidae.size() == 5);
+
+ // Both procellariidae and mPetrels include GADFLY_PERTEL and TRUE_PETREL
+ EnumSet<SeaBird> procellariiformes;
+ procellariiformes += mDiomedeidae;
+ procellariiformes += procellariidae;
+ procellariiformes += mPetrels;
+ MOZ_RELEASE_ASSERT(procellariiformes.size() == 8);
+ }
+
+ void testUnion()
+ {
+ EnumSet<SeaBird> procellariidae = mPetrelProcellariidae +
+ mNonPetrelProcellariidae;
+ MOZ_RELEASE_ASSERT(procellariidae.size() == 5);
+
+ // Both procellariidae and mPetrels include GADFLY_PETREL and TRUE_PETREL
+ EnumSet<SeaBird> procellariiformes = mDiomedeidae + procellariidae +
+ mPetrels;
+ MOZ_RELEASE_ASSERT(procellariiformes.size() == 8);
+ }
+
+ void testRemoveFrom()
+ {
+ EnumSet<SeaBird> likes = mPetrels;
+ likes -= TRUE_PETREL;
+ likes -= DIVING_PETREL;
+ MOZ_RELEASE_ASSERT(likes.size() == 2);
+ MOZ_RELEASE_ASSERT(likes.contains(GADFLY_PETREL));
+ MOZ_RELEASE_ASSERT(likes.contains(STORM_PETREL));
+ }
+
+ void testRemove()
+ {
+ EnumSet<SeaBird> likes = mPetrels - TRUE_PETREL - DIVING_PETREL;
+ MOZ_RELEASE_ASSERT(likes.size() == 2);
+ MOZ_RELEASE_ASSERT(likes.contains(GADFLY_PETREL));
+ MOZ_RELEASE_ASSERT(likes.contains(STORM_PETREL));
+ }
+
+ void testRemoveAllFrom()
+ {
+ EnumSet<SeaBird> likes = mPetrels;
+ likes -= mPetrelProcellariidae;
+ MOZ_RELEASE_ASSERT(likes.size() == 2);
+ MOZ_RELEASE_ASSERT(likes.contains(DIVING_PETREL));
+ MOZ_RELEASE_ASSERT(likes.contains(STORM_PETREL));
+ }
+
+ void testRemoveAll()
+ {
+ EnumSet<SeaBird> likes = mPetrels - mPetrelProcellariidae;
+ MOZ_RELEASE_ASSERT(likes.size() == 2);
+ MOZ_RELEASE_ASSERT(likes.contains(DIVING_PETREL));
+ MOZ_RELEASE_ASSERT(likes.contains(STORM_PETREL));
+ }
+
+ void testIntersect()
+ {
+ EnumSet<SeaBird> likes = mPetrels;
+ likes &= mPetrelProcellariidae;
+ MOZ_RELEASE_ASSERT(likes.size() == 2);
+ MOZ_RELEASE_ASSERT(likes.contains(GADFLY_PETREL));
+ MOZ_RELEASE_ASSERT(likes.contains(TRUE_PETREL));
+ }
+
+ void testInsersection()
+ {
+ EnumSet<SeaBird> likes = mPetrels & mPetrelProcellariidae;
+ MOZ_RELEASE_ASSERT(likes.size() == 2);
+ MOZ_RELEASE_ASSERT(likes.contains(GADFLY_PETREL));
+ MOZ_RELEASE_ASSERT(likes.contains(TRUE_PETREL));
+ }
+
+ void testEquality()
+ {
+ EnumSet<SeaBird> likes = mPetrels & mPetrelProcellariidae;
+ MOZ_RELEASE_ASSERT(likes == EnumSet<SeaBird>(GADFLY_PETREL,
+ TRUE_PETREL));
+ }
+
+ void testDuplicates()
+ {
+ EnumSet<SeaBird> likes = mPetrels;
+ likes += GADFLY_PETREL;
+ likes += TRUE_PETREL;
+ likes += DIVING_PETREL;
+ likes += STORM_PETREL;
+ MOZ_RELEASE_ASSERT(likes.size() == 4);
+ MOZ_RELEASE_ASSERT(likes == mPetrels);
+ }
+
+ void testIteration()
+ {
+ EnumSet<SeaBird> birds;
+ Vector<SeaBird> vec;
+
+ for (auto bird : birds) {
+ MOZ_RELEASE_ASSERT(vec.append(bird));
+ }
+ MOZ_RELEASE_ASSERT(vec.length() == 0);
+
+ birds += DIVING_PETREL;
+ birds += GADFLY_PETREL;
+ birds += STORM_PETREL;
+ birds += TRUE_PETREL;
+ for (auto bird : birds) {
+ MOZ_RELEASE_ASSERT(vec.append(bird));
+ }
+
+ MOZ_RELEASE_ASSERT(vec.length() == 4);
+ MOZ_RELEASE_ASSERT(vec[0] == GADFLY_PETREL);
+ MOZ_RELEASE_ASSERT(vec[1] == TRUE_PETREL);
+ MOZ_RELEASE_ASSERT(vec[2] == DIVING_PETREL);
+ MOZ_RELEASE_ASSERT(vec[3] == STORM_PETREL);
+ }
+
+ void testInitializerListConstuctor()
+ {
+ EnumSet<SeaBird> empty {};
+ MOZ_RELEASE_ASSERT(empty.size() == 0);
+
+ EnumSet<SeaBird> someBirds { SKIMMER, GULL, BOOBY };
+ MOZ_RELEASE_ASSERT(someBirds.size() == 3);
+ MOZ_RELEASE_ASSERT(someBirds.contains(SKIMMER));
+ MOZ_RELEASE_ASSERT(someBirds.contains(GULL));
+ MOZ_RELEASE_ASSERT(someBirds.contains(BOOBY));
+ }
+
+ EnumSet<SeaBird> mAlcidae;
+ EnumSet<SeaBird> mDiomedeidae;
+ EnumSet<SeaBird> mPetrelProcellariidae;
+ EnumSet<SeaBird> mNonPetrelProcellariidae;
+ EnumSet<SeaBird> mPetrels;
+};
+
+int
+main()
+{
+ EnumSetSuite suite;
+ suite.runTests();
+ return 0;
+}
diff --git a/mfbt/tests/TestEnumTypeTraits.cpp b/mfbt/tests/TestEnumTypeTraits.cpp
new file mode 100644
index 000000000..879d40526
--- /dev/null
+++ b/mfbt/tests/TestEnumTypeTraits.cpp
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/IntegerTypeTraits.h"
+#include "mozilla/EnumTypeTraits.h"
+
+using namespace mozilla;
+
+/* Feature check for EnumTypeFitsWithin. */
+
+#define MAKE_FIXED_EMUM_FOR_TYPE(IntType) \
+ enum FixedEnumFor_##IntType : IntType { \
+ A_##IntType, \
+ B_##IntType, \
+ C_##IntType, \
+ };
+
+template<typename EnumType, typename IntType>
+static void
+TestShouldFit()
+{
+ static_assert(EnumTypeFitsWithin<EnumType, IntType>::value,
+ "Should fit within exact/promoted integral type");
+}
+
+template<typename EnumType, typename IntType>
+static void
+TestShouldNotFit()
+{
+ static_assert(!EnumTypeFitsWithin<EnumType, IntType>::value,
+ "Should not fit within");
+}
+
+int
+main()
+{
+ // check for int8_t
+ MAKE_FIXED_EMUM_FOR_TYPE(int8_t);
+ TestShouldFit<FixedEnumFor_int8_t, int8_t>();
+ TestShouldFit<FixedEnumFor_int8_t, int16_t>();
+ TestShouldFit<FixedEnumFor_int8_t, int32_t>();
+ TestShouldFit<FixedEnumFor_int8_t, int64_t>();
+
+ TestShouldNotFit<FixedEnumFor_int8_t, uint8_t>();
+ TestShouldNotFit<FixedEnumFor_int8_t, uint16_t>();
+ TestShouldNotFit<FixedEnumFor_int8_t, uint32_t>();
+ TestShouldNotFit<FixedEnumFor_int8_t, uint64_t>();
+
+ // check for uint8_t
+ MAKE_FIXED_EMUM_FOR_TYPE(uint8_t);
+ TestShouldFit<FixedEnumFor_uint8_t, uint8_t>();
+ TestShouldFit<FixedEnumFor_uint8_t, uint16_t>();
+ TestShouldFit<FixedEnumFor_uint8_t, uint32_t>();
+ TestShouldFit<FixedEnumFor_uint8_t, uint64_t>();
+
+ TestShouldNotFit<FixedEnumFor_uint8_t, int8_t>();
+ TestShouldFit<FixedEnumFor_uint8_t, int16_t>();
+ TestShouldFit<FixedEnumFor_uint8_t, int32_t>();
+ TestShouldFit<FixedEnumFor_uint8_t, int64_t>();
+
+ // check for int16_t
+ MAKE_FIXED_EMUM_FOR_TYPE(int16_t);
+ TestShouldNotFit<FixedEnumFor_int16_t, int8_t>();
+ TestShouldFit<FixedEnumFor_int16_t, int16_t>();
+ TestShouldFit<FixedEnumFor_int16_t, int32_t>();
+ TestShouldFit<FixedEnumFor_int16_t, int64_t>();
+
+ TestShouldNotFit<FixedEnumFor_int16_t, uint8_t>();
+ TestShouldNotFit<FixedEnumFor_int16_t, uint16_t>();
+ TestShouldNotFit<FixedEnumFor_int16_t, uint32_t>();
+ TestShouldNotFit<FixedEnumFor_int16_t, uint64_t>();
+
+ // check for uint16_t
+ MAKE_FIXED_EMUM_FOR_TYPE(uint16_t);
+ TestShouldNotFit<FixedEnumFor_uint16_t, uint8_t>();
+ TestShouldFit<FixedEnumFor_uint16_t, uint16_t>();
+ TestShouldFit<FixedEnumFor_uint16_t, uint32_t>();
+ TestShouldFit<FixedEnumFor_uint16_t, uint64_t>();
+
+ TestShouldNotFit<FixedEnumFor_uint16_t, int8_t>();
+ TestShouldNotFit<FixedEnumFor_uint16_t, int16_t>();
+ TestShouldFit<FixedEnumFor_uint16_t, int32_t>();
+ TestShouldFit<FixedEnumFor_uint16_t, int64_t>();
+
+ // check for int32_t
+ MAKE_FIXED_EMUM_FOR_TYPE(int32_t);
+ TestShouldNotFit<FixedEnumFor_int32_t, int8_t>();
+ TestShouldNotFit<FixedEnumFor_int32_t, int16_t>();
+ TestShouldFit<FixedEnumFor_int32_t, int32_t>();
+ TestShouldFit<FixedEnumFor_int32_t, int64_t>();
+
+ TestShouldNotFit<FixedEnumFor_int32_t, uint8_t>();
+ TestShouldNotFit<FixedEnumFor_int32_t, uint16_t>();
+ TestShouldNotFit<FixedEnumFor_int32_t, uint32_t>();
+ TestShouldNotFit<FixedEnumFor_int32_t, uint64_t>();
+
+ // check for uint32_t
+ MAKE_FIXED_EMUM_FOR_TYPE(uint32_t);
+ TestShouldNotFit<FixedEnumFor_uint32_t, uint8_t>();
+ TestShouldNotFit<FixedEnumFor_uint32_t, uint16_t>();
+ TestShouldFit<FixedEnumFor_uint32_t, uint32_t>();
+ TestShouldFit<FixedEnumFor_uint32_t, uint64_t>();
+
+ TestShouldNotFit<FixedEnumFor_uint32_t, int8_t>();
+ TestShouldNotFit<FixedEnumFor_uint32_t, int16_t>();
+ TestShouldNotFit<FixedEnumFor_uint32_t, int32_t>();
+ TestShouldFit<FixedEnumFor_uint32_t, int64_t>();
+
+ // check for int64_t
+ MAKE_FIXED_EMUM_FOR_TYPE(int64_t);
+ TestShouldNotFit<FixedEnumFor_int64_t, int8_t>();
+ TestShouldNotFit<FixedEnumFor_int64_t, int16_t>();
+ TestShouldNotFit<FixedEnumFor_int64_t, int32_t>();
+ TestShouldFit<FixedEnumFor_int64_t, int64_t>();
+
+ TestShouldNotFit<FixedEnumFor_int64_t, uint8_t>();
+ TestShouldNotFit<FixedEnumFor_int64_t, uint16_t>();
+ TestShouldNotFit<FixedEnumFor_int64_t, uint32_t>();
+ TestShouldNotFit<FixedEnumFor_int64_t, uint64_t>();
+
+ // check for uint64_t
+ MAKE_FIXED_EMUM_FOR_TYPE(uint64_t);
+ TestShouldNotFit<FixedEnumFor_uint64_t, uint8_t>();
+ TestShouldNotFit<FixedEnumFor_uint64_t, uint16_t>();
+ TestShouldNotFit<FixedEnumFor_uint64_t, uint32_t>();
+ TestShouldFit<FixedEnumFor_uint64_t, uint64_t>();
+
+ TestShouldNotFit<FixedEnumFor_uint64_t, int8_t>();
+ TestShouldNotFit<FixedEnumFor_uint64_t, int16_t>();
+ TestShouldNotFit<FixedEnumFor_uint64_t, int32_t>();
+ TestShouldNotFit<FixedEnumFor_uint64_t, int64_t>();
+
+ return 0;
+}
diff --git a/mfbt/tests/TestEnumeratedArray.cpp b/mfbt/tests/TestEnumeratedArray.cpp
new file mode 100644
index 000000000..bb73f21b7
--- /dev/null
+++ b/mfbt/tests/TestEnumeratedArray.cpp
@@ -0,0 +1,42 @@
+/* -*- 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/EnumeratedArray.h"
+
+enum class AnimalSpecies
+{
+ Cow,
+ Sheep,
+ Pig,
+ Count
+};
+
+void TestInitialValueByConstructor()
+{
+ using namespace mozilla;
+ // Style 1
+ EnumeratedArray<AnimalSpecies, AnimalSpecies::Count, int> headCount(1, 2, 3);
+ MOZ_RELEASE_ASSERT(headCount[AnimalSpecies::Cow] == 1);
+ MOZ_RELEASE_ASSERT(headCount[AnimalSpecies::Sheep] == 2);
+ MOZ_RELEASE_ASSERT(headCount[AnimalSpecies::Pig] == 3);
+ // Style 2
+ EnumeratedArray<AnimalSpecies, AnimalSpecies::Count, int> headCount2{5, 6, 7};
+ MOZ_RELEASE_ASSERT(headCount2[AnimalSpecies::Cow] == 5);
+ MOZ_RELEASE_ASSERT(headCount2[AnimalSpecies::Sheep] == 6);
+ MOZ_RELEASE_ASSERT(headCount2[AnimalSpecies::Pig] == 7);
+ // Style 3
+ EnumeratedArray<AnimalSpecies, AnimalSpecies::Count, int> headCount3({8, 9, 10});
+ MOZ_RELEASE_ASSERT(headCount3[AnimalSpecies::Cow] == 8);
+ MOZ_RELEASE_ASSERT(headCount3[AnimalSpecies::Sheep] == 9);
+ MOZ_RELEASE_ASSERT(headCount3[AnimalSpecies::Pig] == 10);
+}
+
+int
+main()
+{
+ TestInitialValueByConstructor();
+ return 0;
+} \ No newline at end of file
diff --git a/mfbt/tests/TestFastBernoulliTrial.cpp b/mfbt/tests/TestFastBernoulliTrial.cpp
new file mode 100644
index 000000000..62d4b51a7
--- /dev/null
+++ b/mfbt/tests/TestFastBernoulliTrial.cpp
@@ -0,0 +1,209 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/FastBernoulliTrial.h"
+
+#include <math.h>
+
+// Note that because we always provide FastBernoulliTrial with a fixed
+// pseudorandom seed in these tests, the results here are completely
+// deterministic.
+//
+// A non-optimized version of this test runs in .009s on my laptop. Using larger
+// sample sizes lets us meet tighter bounds on the counts.
+
+static void
+TestProportions()
+{
+ mozilla::FastBernoulliTrial bernoulli(1.0,
+ 698079309544035222ULL,
+ 6012389156611637584ULL);
+
+ for (size_t i = 0; i < 100; i++)
+ MOZ_RELEASE_ASSERT(bernoulli.trial());
+
+ {
+ bernoulli.setProbability(0.5);
+ size_t count = 0;
+ for (size_t i = 0; i < 1000; i++)
+ count += bernoulli.trial();
+ MOZ_RELEASE_ASSERT(count == 496);
+ }
+
+ {
+ bernoulli.setProbability(0.001);
+ size_t count = 0;
+ for (size_t i = 0; i < 1000; i++)
+ count += bernoulli.trial();
+ MOZ_RELEASE_ASSERT(count == 2);
+ }
+
+ {
+ bernoulli.setProbability(0.85);
+ size_t count = 0;
+ for (size_t i = 0; i < 1000; i++)
+ count += bernoulli.trial();
+ MOZ_RELEASE_ASSERT(count == 852);
+ }
+
+ bernoulli.setProbability(0.0);
+ for (size_t i = 0; i < 100; i++)
+ MOZ_RELEASE_ASSERT(!bernoulli.trial());
+}
+
+static void
+TestHarmonics()
+{
+ mozilla::FastBernoulliTrial bernoulli(0.1,
+ 698079309544035222ULL,
+ 6012389156611637584ULL);
+
+ const size_t n = 100000;
+ bool trials[n];
+ for (size_t i = 0; i < n; i++)
+ trials[i] = bernoulli.trial();
+
+ // For each harmonic and phase, check that the proportion sampled is
+ // within acceptable bounds.
+ for (size_t harmonic = 1; harmonic < 20; harmonic++) {
+ size_t expected = n / harmonic / 10;
+ size_t low_expected = expected * 85 / 100;
+ size_t high_expected = expected * 115 / 100;
+
+ for (size_t phase = 0; phase < harmonic; phase++) {
+ size_t count = 0;
+ for (size_t i = phase; i < n; i += harmonic)
+ count += trials[i];
+
+ MOZ_RELEASE_ASSERT(low_expected <= count && count <= high_expected);
+ }
+ }
+}
+
+static void
+TestTrialN()
+{
+ mozilla::FastBernoulliTrial bernoulli(0.01,
+ 0x67ff17e25d855942ULL,
+ 0x74f298193fe1c5b1ULL);
+
+ {
+ size_t count = 0;
+ for (size_t i = 0; i < 10000; i++)
+ count += bernoulli.trial(1);
+
+ // Expected value: 0.01 * 10000 == 100
+ MOZ_RELEASE_ASSERT(count == 97);
+ }
+
+ {
+ size_t count = 0;
+ for (size_t i = 0; i < 10000; i++)
+ count += bernoulli.trial(3);
+
+ // Expected value: (1 - (1 - 0.01) ** 3) == 0.0297,
+ // 0.0297 * 10000 == 297
+ MOZ_RELEASE_ASSERT(count == 304);
+ }
+
+ {
+ size_t count = 0;
+ for (size_t i = 0; i < 10000; i++)
+ count += bernoulli.trial(10);
+
+ // Expected value: (1 - (1 - 0.01) ** 10) == 0.0956,
+ // 0.0956 * 10000 == 956
+ MOZ_RELEASE_ASSERT(count == 936);
+ }
+
+ {
+ size_t count = 0;
+ for (size_t i = 0; i < 10000; i++)
+ count += bernoulli.trial(100);
+
+ // Expected value: (1 - (1 - 0.01) ** 100) == 0.6339
+ // 0.6339 * 10000 == 6339
+ MOZ_RELEASE_ASSERT(count == 6372);
+ }
+
+ {
+ size_t count = 0;
+ for (size_t i = 0; i < 10000; i++)
+ count += bernoulli.trial(1000);
+
+ // Expected value: (1 - (1 - 0.01) ** 1000) == 0.9999
+ // 0.9999 * 10000 == 9999
+ MOZ_RELEASE_ASSERT(count == 9998);
+ }
+}
+
+static void
+TestChangeProbability()
+{
+ mozilla::FastBernoulliTrial bernoulli(1.0,
+ 0x67ff17e25d855942ULL,
+ 0x74f298193fe1c5b1ULL);
+
+ // Establish a very high skip count.
+ bernoulli.setProbability(0.0);
+
+ // This should re-establish a zero skip count.
+ bernoulli.setProbability(1.0);
+
+ // So this should return true.
+ MOZ_RELEASE_ASSERT(bernoulli.trial());
+}
+
+static void
+TestCuspProbabilities()
+{
+ /*
+ * FastBernoulliTrial takes care to avoid screwing up on edge cases. The
+ * checks here all look pretty dumb, but they exercise paths in the code that
+ * could exhibit undefined behavior if coded naïvely.
+ */
+
+ /*
+ * This should not be perceptibly different from 1; for 64-bit doubles, this
+ * is a one in ten trillion chance of the trial not succeeding. Overflows
+ * converting doubles to size_t skip counts may change this, though.
+ */
+ mozilla::FastBernoulliTrial bernoulli(nextafter(1, 0),
+ 0x67ff17e25d855942ULL,
+ 0x74f298193fe1c5b1ULL);
+
+ for (size_t i = 0; i < 1000; i++)
+ MOZ_RELEASE_ASSERT(bernoulli.trial());
+
+ /*
+ * This should not be perceptibly different from 0; for 64-bit doubles,
+ * the FastBernoulliTrial will actually treat this as exactly zero.
+ */
+ bernoulli.setProbability(nextafter(0, 1));
+ for (size_t i = 0; i < 1000; i++)
+ MOZ_RELEASE_ASSERT(!bernoulli.trial());
+
+ /*
+ * This should be a vanishingly low probability which FastBernoulliTrial does
+ * *not* treat as exactly zero.
+ */
+ bernoulli.setProbability(1 - nextafter(1, 0));
+ for (size_t i = 0; i < 1000; i++)
+ MOZ_RELEASE_ASSERT(!bernoulli.trial());
+}
+
+int
+main()
+{
+ TestProportions();
+ TestHarmonics();
+ TestTrialN();
+ TestChangeProbability();
+ TestCuspProbabilities();
+
+ return 0;
+}
diff --git a/mfbt/tests/TestFloatingPoint.cpp b/mfbt/tests/TestFloatingPoint.cpp
new file mode 100644
index 000000000..f32e698de
--- /dev/null
+++ b/mfbt/tests/TestFloatingPoint.cpp
@@ -0,0 +1,592 @@
+/* -*- 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/FloatingPoint.h"
+
+#include <math.h>
+
+using mozilla::ExponentComponent;
+using mozilla::FloatingPoint;
+using mozilla::FuzzyEqualsAdditive;
+using mozilla::FuzzyEqualsMultiplicative;
+using mozilla::IsFinite;
+using mozilla::IsInfinite;
+using mozilla::IsNaN;
+using mozilla::IsNegative;
+using mozilla::IsNegativeZero;
+using mozilla::IsPositiveZero;
+using mozilla::NegativeInfinity;
+using mozilla::NumberEqualsInt32;
+using mozilla::NumberIsInt32;
+using mozilla::NumbersAreIdentical;
+using mozilla::PositiveInfinity;
+using mozilla::SpecificNaN;
+using mozilla::UnspecifiedNaN;
+
+#define A(a) MOZ_RELEASE_ASSERT(a)
+
+template<typename T>
+static void
+ShouldBeIdentical(T aD1, T aD2)
+{
+ A(NumbersAreIdentical(aD1, aD2));
+ A(NumbersAreIdentical(aD2, aD1));
+}
+
+template<typename T>
+static void
+ShouldNotBeIdentical(T aD1, T aD2)
+{
+ A(!NumbersAreIdentical(aD1, aD2));
+ A(!NumbersAreIdentical(aD2, aD1));
+}
+
+static void
+TestDoublesAreIdentical()
+{
+ ShouldBeIdentical(+0.0, +0.0);
+ ShouldBeIdentical(-0.0, -0.0);
+ ShouldNotBeIdentical(+0.0, -0.0);
+
+ ShouldBeIdentical(1.0, 1.0);
+ ShouldNotBeIdentical(-1.0, 1.0);
+ ShouldBeIdentical(4294967295.0, 4294967295.0);
+ ShouldNotBeIdentical(-4294967295.0, 4294967295.0);
+ ShouldBeIdentical(4294967296.0, 4294967296.0);
+ ShouldBeIdentical(4294967297.0, 4294967297.0);
+ ShouldBeIdentical(1e300, 1e300);
+
+ ShouldBeIdentical(PositiveInfinity<double>(), PositiveInfinity<double>());
+ ShouldBeIdentical(NegativeInfinity<double>(), NegativeInfinity<double>());
+ ShouldNotBeIdentical(PositiveInfinity<double>(), NegativeInfinity<double>());
+
+ ShouldNotBeIdentical(-0.0, NegativeInfinity<double>());
+ ShouldNotBeIdentical(+0.0, NegativeInfinity<double>());
+ ShouldNotBeIdentical(1e300, NegativeInfinity<double>());
+ ShouldNotBeIdentical(3.141592654, NegativeInfinity<double>());
+
+ ShouldBeIdentical(UnspecifiedNaN<double>(), UnspecifiedNaN<double>());
+ ShouldBeIdentical(-UnspecifiedNaN<double>(), UnspecifiedNaN<double>());
+ ShouldBeIdentical(UnspecifiedNaN<double>(), -UnspecifiedNaN<double>());
+
+ ShouldBeIdentical(SpecificNaN<double>(0, 17), SpecificNaN<double>(0, 42));
+ ShouldBeIdentical(SpecificNaN<double>(1, 17), SpecificNaN<double>(1, 42));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17), SpecificNaN<double>(1, 42));
+ ShouldBeIdentical(SpecificNaN<double>(1, 17), SpecificNaN<double>(0, 42));
+
+ const uint64_t Mask = 0xfffffffffffffULL;
+ for (unsigned i = 0; i < 52; i++) {
+ for (unsigned j = 0; j < 52; j++) {
+ for (unsigned sign = 0; i < 2; i++) {
+ ShouldBeIdentical(SpecificNaN<double>(0, 1ULL << i),
+ SpecificNaN<double>(sign, 1ULL << j));
+ ShouldBeIdentical(SpecificNaN<double>(1, 1ULL << i),
+ SpecificNaN<double>(sign, 1ULL << j));
+
+ ShouldBeIdentical(SpecificNaN<double>(0, Mask & ~(1ULL << i)),
+ SpecificNaN<double>(sign, Mask & ~(1ULL << j)));
+ ShouldBeIdentical(SpecificNaN<double>(1, Mask & ~(1ULL << i)),
+ SpecificNaN<double>(sign, Mask & ~(1ULL << j)));
+ }
+ }
+ }
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x8000000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x4000000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x2000000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x1000000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x0800000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x0400000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x0200000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x0100000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x0080000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x0040000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x0020000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(0, 17),
+ SpecificNaN<double>(0, 0x0010000000000ULL));
+ ShouldBeIdentical(SpecificNaN<double>(1, 17),
+ SpecificNaN<double>(0, 0xff0ffffffffffULL));
+ ShouldBeIdentical(SpecificNaN<double>(1, 17),
+ SpecificNaN<double>(0, 0xfffffffffff0fULL));
+
+ ShouldNotBeIdentical(UnspecifiedNaN<double>(), +0.0);
+ ShouldNotBeIdentical(UnspecifiedNaN<double>(), -0.0);
+ ShouldNotBeIdentical(UnspecifiedNaN<double>(), 1.0);
+ ShouldNotBeIdentical(UnspecifiedNaN<double>(), -1.0);
+ ShouldNotBeIdentical(UnspecifiedNaN<double>(), PositiveInfinity<double>());
+ ShouldNotBeIdentical(UnspecifiedNaN<double>(), NegativeInfinity<double>());
+}
+
+static void
+TestFloatsAreIdentical()
+{
+ ShouldBeIdentical(+0.0f, +0.0f);
+ ShouldBeIdentical(-0.0f, -0.0f);
+ ShouldNotBeIdentical(+0.0f, -0.0f);
+
+ ShouldBeIdentical(1.0f, 1.0f);
+ ShouldNotBeIdentical(-1.0f, 1.0f);
+ ShouldBeIdentical(8388607.0f, 8388607.0f);
+ ShouldNotBeIdentical(-8388607.0f, 8388607.0f);
+ ShouldBeIdentical(8388608.0f, 8388608.0f);
+ ShouldBeIdentical(8388609.0f, 8388609.0f);
+ ShouldBeIdentical(1e36f, 1e36f);
+
+ ShouldBeIdentical(PositiveInfinity<float>(), PositiveInfinity<float>());
+ ShouldBeIdentical(NegativeInfinity<float>(), NegativeInfinity<float>());
+ ShouldNotBeIdentical(PositiveInfinity<float>(), NegativeInfinity<float>());
+
+ ShouldNotBeIdentical(-0.0f, NegativeInfinity<float>());
+ ShouldNotBeIdentical(+0.0f, NegativeInfinity<float>());
+ ShouldNotBeIdentical(1e36f, NegativeInfinity<float>());
+ ShouldNotBeIdentical(3.141592654f, NegativeInfinity<float>());
+
+ ShouldBeIdentical(UnspecifiedNaN<float>(), UnspecifiedNaN<float>());
+ ShouldBeIdentical(-UnspecifiedNaN<float>(), UnspecifiedNaN<float>());
+ ShouldBeIdentical(UnspecifiedNaN<float>(), -UnspecifiedNaN<float>());
+
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 42));
+ ShouldBeIdentical(SpecificNaN<float>(1, 17), SpecificNaN<float>(1, 42));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(1, 42));
+ ShouldBeIdentical(SpecificNaN<float>(1, 17), SpecificNaN<float>(0, 42));
+
+ const uint32_t Mask = 0x7fffffUL;
+ for (unsigned i = 0; i < 23; i++) {
+ for (unsigned j = 0; j < 23; j++) {
+ for (unsigned sign = 0; i < 2; i++) {
+ ShouldBeIdentical(SpecificNaN<float>(0, 1UL << i),
+ SpecificNaN<float>(sign, 1UL << j));
+ ShouldBeIdentical(SpecificNaN<float>(1, 1UL << i),
+ SpecificNaN<float>(sign, 1UL << j));
+
+ ShouldBeIdentical(SpecificNaN<float>(0, Mask & ~(1UL << i)),
+ SpecificNaN<float>(sign, Mask & ~(1UL << j)));
+ ShouldBeIdentical(SpecificNaN<float>(1, Mask & ~(1UL << i)),
+ SpecificNaN<float>(sign, Mask & ~(1UL << j)));
+ }
+ }
+ }
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x700000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x400000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x200000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x100000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x080000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x040000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x020000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x010000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x008000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x004000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x002000));
+ ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x001000));
+ ShouldBeIdentical(SpecificNaN<float>(1, 17), SpecificNaN<float>(0, 0x7f0fff));
+ ShouldBeIdentical(SpecificNaN<float>(1, 17), SpecificNaN<float>(0, 0x7fff0f));
+
+ ShouldNotBeIdentical(UnspecifiedNaN<float>(), +0.0f);
+ ShouldNotBeIdentical(UnspecifiedNaN<float>(), -0.0f);
+ ShouldNotBeIdentical(UnspecifiedNaN<float>(), 1.0f);
+ ShouldNotBeIdentical(UnspecifiedNaN<float>(), -1.0f);
+ ShouldNotBeIdentical(UnspecifiedNaN<float>(), PositiveInfinity<float>());
+ ShouldNotBeIdentical(UnspecifiedNaN<float>(), NegativeInfinity<float>());
+}
+
+static void
+TestAreIdentical()
+{
+ TestDoublesAreIdentical();
+ TestFloatsAreIdentical();
+}
+
+static void
+TestDoubleExponentComponent()
+{
+ A(ExponentComponent(0.0) ==
+ -int_fast16_t(FloatingPoint<double>::kExponentBias));
+ A(ExponentComponent(-0.0) ==
+ -int_fast16_t(FloatingPoint<double>::kExponentBias));
+ A(ExponentComponent(0.125) == -3);
+ A(ExponentComponent(0.5) == -1);
+ A(ExponentComponent(1.0) == 0);
+ A(ExponentComponent(1.5) == 0);
+ A(ExponentComponent(2.0) == 1);
+ A(ExponentComponent(7.0) == 2);
+ A(ExponentComponent(PositiveInfinity<double>()) ==
+ FloatingPoint<double>::kExponentBias + 1);
+ A(ExponentComponent(NegativeInfinity<double>()) ==
+ FloatingPoint<double>::kExponentBias + 1);
+ A(ExponentComponent(UnspecifiedNaN<double>()) ==
+ FloatingPoint<double>::kExponentBias + 1);
+}
+
+static void
+TestFloatExponentComponent()
+{
+ A(ExponentComponent(0.0f) ==
+ -int_fast16_t(FloatingPoint<float>::kExponentBias));
+ A(ExponentComponent(-0.0f) ==
+ -int_fast16_t(FloatingPoint<float>::kExponentBias));
+ A(ExponentComponent(0.125f) == -3);
+ A(ExponentComponent(0.5f) == -1);
+ A(ExponentComponent(1.0f) == 0);
+ A(ExponentComponent(1.5f) == 0);
+ A(ExponentComponent(2.0f) == 1);
+ A(ExponentComponent(7.0f) == 2);
+ A(ExponentComponent(PositiveInfinity<float>()) ==
+ FloatingPoint<float>::kExponentBias + 1);
+ A(ExponentComponent(NegativeInfinity<float>()) ==
+ FloatingPoint<float>::kExponentBias + 1);
+ A(ExponentComponent(UnspecifiedNaN<float>()) ==
+ FloatingPoint<float>::kExponentBias + 1);
+}
+
+static void
+TestExponentComponent()
+{
+ TestDoubleExponentComponent();
+ TestFloatExponentComponent();
+}
+
+static void
+TestDoublesPredicates()
+{
+ A(IsNaN(UnspecifiedNaN<double>()));
+ A(IsNaN(SpecificNaN<double>(1, 17)));;
+ A(IsNaN(SpecificNaN<double>(0, 0xfffffffffff0fULL)));
+ A(!IsNaN(0.0));
+ A(!IsNaN(-0.0));
+ A(!IsNaN(1.0));
+ A(!IsNaN(PositiveInfinity<double>()));
+ A(!IsNaN(NegativeInfinity<double>()));
+
+ A(IsInfinite(PositiveInfinity<double>()));
+ A(IsInfinite(NegativeInfinity<double>()));
+ A(!IsInfinite(UnspecifiedNaN<double>()));
+ A(!IsInfinite(0.0));
+ A(!IsInfinite(-0.0));
+ A(!IsInfinite(1.0));
+
+ A(!IsFinite(PositiveInfinity<double>()));
+ A(!IsFinite(NegativeInfinity<double>()));
+ A(!IsFinite(UnspecifiedNaN<double>()));
+ A(IsFinite(0.0));
+ A(IsFinite(-0.0));
+ A(IsFinite(1.0));
+
+ A(!IsNegative(PositiveInfinity<double>()));
+ A(IsNegative(NegativeInfinity<double>()));
+ A(IsNegative(-0.0));
+ A(!IsNegative(0.0));
+ A(IsNegative(-1.0));
+ A(!IsNegative(1.0));
+
+ A(!IsNegativeZero(PositiveInfinity<double>()));
+ A(!IsNegativeZero(NegativeInfinity<double>()));
+ A(!IsNegativeZero(SpecificNaN<double>(1, 17)));;
+ A(!IsNegativeZero(SpecificNaN<double>(1, 0xfffffffffff0fULL)));
+ A(!IsNegativeZero(SpecificNaN<double>(0, 17)));;
+ A(!IsNegativeZero(SpecificNaN<double>(0, 0xfffffffffff0fULL)));
+ A(!IsNegativeZero(UnspecifiedNaN<double>()));
+ A(IsNegativeZero(-0.0));
+ A(!IsNegativeZero(0.0));
+ A(!IsNegativeZero(-1.0));
+ A(!IsNegativeZero(1.0));
+
+ int32_t i;
+ A(NumberIsInt32(0.0, &i));
+ A(i == 0);
+ A(!NumberIsInt32(-0.0, &i));
+ A(NumberEqualsInt32(0.0, &i));
+ A(i == 0);
+ A(NumberEqualsInt32(-0.0, &i));
+ A(i == 0);
+ A(NumberIsInt32(double(INT32_MIN), &i));
+ A(i == INT32_MIN);
+ A(NumberIsInt32(double(INT32_MAX), &i));
+ A(i == INT32_MAX);
+ A(NumberEqualsInt32(double(INT32_MIN), &i));
+ A(i == INT32_MIN);
+ A(NumberEqualsInt32(double(INT32_MAX), &i));
+ A(i == INT32_MAX);
+ A(!NumberIsInt32(0.5, &i));
+ A(!NumberIsInt32(double(INT32_MAX) + 0.1, &i));
+ A(!NumberIsInt32(double(INT32_MIN) - 0.1, &i));
+ A(!NumberIsInt32(NegativeInfinity<double>(), &i));
+ A(!NumberIsInt32(PositiveInfinity<double>(), &i));
+ A(!NumberIsInt32(UnspecifiedNaN<double>(), &i));
+ A(!NumberEqualsInt32(0.5, &i));
+ A(!NumberEqualsInt32(double(INT32_MAX) + 0.1, &i));
+ A(!NumberEqualsInt32(double(INT32_MIN) - 0.1, &i));
+ A(!NumberEqualsInt32(NegativeInfinity<double>(), &i));
+ A(!NumberEqualsInt32(PositiveInfinity<double>(), &i));
+ A(!NumberEqualsInt32(UnspecifiedNaN<double>(), &i));
+}
+
+static void
+TestFloatsPredicates()
+{
+ A(IsNaN(UnspecifiedNaN<float>()));
+ A(IsNaN(SpecificNaN<float>(1, 17)));;
+ A(IsNaN(SpecificNaN<float>(0, 0x7fff0fUL)));
+ A(!IsNaN(0.0f));
+ A(!IsNaN(-0.0f));
+ A(!IsNaN(1.0f));
+ A(!IsNaN(PositiveInfinity<float>()));
+ A(!IsNaN(NegativeInfinity<float>()));
+
+ A(IsInfinite(PositiveInfinity<float>()));
+ A(IsInfinite(NegativeInfinity<float>()));
+ A(!IsInfinite(UnspecifiedNaN<float>()));
+ A(!IsInfinite(0.0f));
+ A(!IsInfinite(-0.0f));
+ A(!IsInfinite(1.0f));
+
+ A(!IsFinite(PositiveInfinity<float>()));
+ A(!IsFinite(NegativeInfinity<float>()));
+ A(!IsFinite(UnspecifiedNaN<float>()));
+ A(IsFinite(0.0f));
+ A(IsFinite(-0.0f));
+ A(IsFinite(1.0f));
+
+ A(!IsNegative(PositiveInfinity<float>()));
+ A(IsNegative(NegativeInfinity<float>()));
+ A(IsNegative(-0.0f));
+ A(!IsNegative(0.0f));
+ A(IsNegative(-1.0f));
+ A(!IsNegative(1.0f));
+
+ A(!IsNegativeZero(PositiveInfinity<float>()));
+ A(!IsNegativeZero(NegativeInfinity<float>()));
+ A(!IsNegativeZero(SpecificNaN<float>(1, 17)));;
+ A(!IsNegativeZero(SpecificNaN<float>(1, 0x7fff0fUL)));
+ A(!IsNegativeZero(SpecificNaN<float>(0, 17)));;
+ A(!IsNegativeZero(SpecificNaN<float>(0, 0x7fff0fUL)));
+ A(!IsNegativeZero(UnspecifiedNaN<float>()));
+ A(IsNegativeZero(-0.0f));
+ A(!IsNegativeZero(0.0f));
+ A(!IsNegativeZero(-1.0f));
+ A(!IsNegativeZero(1.0f));
+
+ A(!IsPositiveZero(PositiveInfinity<float>()));
+ A(!IsPositiveZero(NegativeInfinity<float>()));
+ A(!IsPositiveZero(SpecificNaN<float>(1, 17)));;
+ A(!IsPositiveZero(SpecificNaN<float>(1, 0x7fff0fUL)));
+ A(!IsPositiveZero(SpecificNaN<float>(0, 17)));;
+ A(!IsPositiveZero(SpecificNaN<float>(0, 0x7fff0fUL)));
+ A(!IsPositiveZero(UnspecifiedNaN<float>()));
+ A(IsPositiveZero(0.0f));
+ A(!IsPositiveZero(-0.0f));
+ A(!IsPositiveZero(-1.0f));
+ A(!IsPositiveZero(1.0f));
+
+ int32_t i;
+ const int32_t BIG = 2097151;
+ A(NumberIsInt32(0.0f, &i));
+ A(i == 0);
+ A(!NumberIsInt32(-0.0f, &i));
+ A(NumberEqualsInt32(0.0f, &i));
+ A(i == 0);
+ A(NumberEqualsInt32(-0.0f, &i));
+ A(i == 0);
+ A(NumberIsInt32(float(INT32_MIN), &i));
+ A(i == INT32_MIN);
+ A(NumberIsInt32(float(BIG), &i));
+ A(i == BIG);
+ A(NumberEqualsInt32(float(INT32_MIN), &i));
+ A(i == INT32_MIN);
+ A(NumberEqualsInt32(float(BIG), &i));
+ A(i == BIG);
+ A(!NumberIsInt32(0.5f, &i));
+ A(!NumberIsInt32(float(BIG) + 0.1f, &i));
+ A(!NumberIsInt32(NegativeInfinity<float>(), &i));
+ A(!NumberIsInt32(PositiveInfinity<float>(), &i));
+ A(!NumberIsInt32(UnspecifiedNaN<float>(), &i));
+ A(!NumberEqualsInt32(0.5f, &i));
+ A(!NumberEqualsInt32(float(BIG) + 0.1f, &i));
+ A(!NumberEqualsInt32(NegativeInfinity<float>(), &i));
+ A(!NumberEqualsInt32(PositiveInfinity<float>(), &i));
+ A(!NumberEqualsInt32(UnspecifiedNaN<float>(), &i));
+}
+
+static void
+TestPredicates()
+{
+ TestFloatsPredicates();
+ TestDoublesPredicates();
+}
+
+static void
+TestFloatsAreApproximatelyEqual()
+{
+ float epsilon = mozilla::detail::FuzzyEqualsEpsilon<float>::value();
+ float lessThanEpsilon = epsilon / 2.0f;
+ float moreThanEpsilon = epsilon * 2.0f;
+
+ // Additive tests using the default epsilon
+ // ... around 1.0
+ A(FuzzyEqualsAdditive(1.0f, 1.0f + lessThanEpsilon));
+ A(FuzzyEqualsAdditive(1.0f, 1.0f - lessThanEpsilon));
+ A(FuzzyEqualsAdditive(1.0f, 1.0f + epsilon));
+ A(FuzzyEqualsAdditive(1.0f, 1.0f - epsilon));
+ A(!FuzzyEqualsAdditive(1.0f, 1.0f + moreThanEpsilon));
+ A(!FuzzyEqualsAdditive(1.0f, 1.0f - moreThanEpsilon));
+ // ... around 1.0e2 (this is near the upper bound of the range where
+ // adding moreThanEpsilon will still be representable and return false)
+ A(FuzzyEqualsAdditive(1.0e2f, 1.0e2f + lessThanEpsilon));
+ A(FuzzyEqualsAdditive(1.0e2f, 1.0e2f + epsilon));
+ A(!FuzzyEqualsAdditive(1.0e2f, 1.0e2f + moreThanEpsilon));
+ // ... around 1.0e-10
+ A(FuzzyEqualsAdditive(1.0e-10f, 1.0e-10f + lessThanEpsilon));
+ A(FuzzyEqualsAdditive(1.0e-10f, 1.0e-10f + epsilon));
+ A(!FuzzyEqualsAdditive(1.0e-10f, 1.0e-10f + moreThanEpsilon));
+ // ... straddling 0
+ A(FuzzyEqualsAdditive(1.0e-6f, -1.0e-6f));
+ A(!FuzzyEqualsAdditive(1.0e-5f, -1.0e-5f));
+ // Using a small epsilon
+ A(FuzzyEqualsAdditive(1.0e-5f, 1.0e-5f + 1.0e-10f, 1.0e-9f));
+ A(!FuzzyEqualsAdditive(1.0e-5f, 1.0e-5f + 1.0e-10f, 1.0e-11f));
+ // Using a big epsilon
+ A(FuzzyEqualsAdditive(1.0e20f, 1.0e20f + 1.0e15f, 1.0e16f));
+ A(!FuzzyEqualsAdditive(1.0e20f, 1.0e20f + 1.0e15f, 1.0e14f));
+
+ // Multiplicative tests using the default epsilon
+ // ... around 1.0
+ A(FuzzyEqualsMultiplicative(1.0f, 1.0f + lessThanEpsilon));
+ A(FuzzyEqualsMultiplicative(1.0f, 1.0f - lessThanEpsilon));
+ A(FuzzyEqualsMultiplicative(1.0f, 1.0f + epsilon));
+ A(!FuzzyEqualsMultiplicative(1.0f, 1.0f - epsilon));
+ A(!FuzzyEqualsMultiplicative(1.0f, 1.0f + moreThanEpsilon));
+ A(!FuzzyEqualsMultiplicative(1.0f, 1.0f - moreThanEpsilon));
+ // ... around 1.0e10
+ A(FuzzyEqualsMultiplicative(1.0e10f, 1.0e10f + (lessThanEpsilon * 1.0e10f)));
+ A(!FuzzyEqualsMultiplicative(1.0e10f, 1.0e10f + (moreThanEpsilon * 1.0e10f)));
+ // ... around 1.0e-10
+ A(FuzzyEqualsMultiplicative(1.0e-10f,
+ 1.0e-10f + (lessThanEpsilon * 1.0e-10f)));
+ A(!FuzzyEqualsMultiplicative(1.0e-10f,
+ 1.0e-10f + (moreThanEpsilon * 1.0e-10f)));
+ // ... straddling 0
+ A(!FuzzyEqualsMultiplicative(1.0e-6f, -1.0e-6f));
+ A(FuzzyEqualsMultiplicative(1.0e-6f, -1.0e-6f, 1.0e2f));
+ // Using a small epsilon
+ A(FuzzyEqualsMultiplicative(1.0e-5f, 1.0e-5f + 1.0e-10f, 1.0e-4f));
+ A(!FuzzyEqualsMultiplicative(1.0e-5f, 1.0e-5f + 1.0e-10f, 1.0e-5f));
+ // Using a big epsilon
+ A(FuzzyEqualsMultiplicative(1.0f, 2.0f, 1.0f));
+ A(!FuzzyEqualsMultiplicative(1.0f, 2.0f, 0.1f));
+
+ // "real world case"
+ float oneThird = 10.0f / 3.0f;
+ A(FuzzyEqualsAdditive(10.0f, 3.0f * oneThird));
+ A(FuzzyEqualsMultiplicative(10.0f, 3.0f * oneThird));
+ // NaN check
+ A(!FuzzyEqualsAdditive(SpecificNaN<float>(1, 1), SpecificNaN<float>(1, 1)));
+ A(!FuzzyEqualsAdditive(SpecificNaN<float>(1, 2), SpecificNaN<float>(0, 8)));
+ A(!FuzzyEqualsMultiplicative(SpecificNaN<float>(1, 1),
+ SpecificNaN<float>(1, 1)));
+ A(!FuzzyEqualsMultiplicative(SpecificNaN<float>(1, 2),
+ SpecificNaN<float>(0, 200)));
+}
+
+static void
+TestDoublesAreApproximatelyEqual()
+{
+ double epsilon = mozilla::detail::FuzzyEqualsEpsilon<double>::value();
+ double lessThanEpsilon = epsilon / 2.0;
+ double moreThanEpsilon = epsilon * 2.0;
+
+ // Additive tests using the default epsilon
+ // ... around 1.0
+ A(FuzzyEqualsAdditive(1.0, 1.0 + lessThanEpsilon));
+ A(FuzzyEqualsAdditive(1.0, 1.0 - lessThanEpsilon));
+ A(FuzzyEqualsAdditive(1.0, 1.0 + epsilon));
+ A(FuzzyEqualsAdditive(1.0, 1.0 - epsilon));
+ A(!FuzzyEqualsAdditive(1.0, 1.0 + moreThanEpsilon));
+ A(!FuzzyEqualsAdditive(1.0, 1.0 - moreThanEpsilon));
+ // ... around 1.0e4 (this is near the upper bound of the range where
+ // adding moreThanEpsilon will still be representable and return false)
+ A(FuzzyEqualsAdditive(1.0e4, 1.0e4 + lessThanEpsilon));
+ A(FuzzyEqualsAdditive(1.0e4, 1.0e4 + epsilon));
+ A(!FuzzyEqualsAdditive(1.0e4, 1.0e4 + moreThanEpsilon));
+ // ... around 1.0e-25
+ A(FuzzyEqualsAdditive(1.0e-25, 1.0e-25 + lessThanEpsilon));
+ A(FuzzyEqualsAdditive(1.0e-25, 1.0e-25 + epsilon));
+ A(!FuzzyEqualsAdditive(1.0e-25, 1.0e-25 + moreThanEpsilon));
+ // ... straddling 0
+ A(FuzzyEqualsAdditive(1.0e-13, -1.0e-13));
+ A(!FuzzyEqualsAdditive(1.0e-12, -1.0e-12));
+ // Using a small epsilon
+ A(FuzzyEqualsAdditive(1.0e-15, 1.0e-15 + 1.0e-30, 1.0e-29));
+ A(!FuzzyEqualsAdditive(1.0e-15, 1.0e-15 + 1.0e-30, 1.0e-31));
+ // Using a big epsilon
+ A(FuzzyEqualsAdditive(1.0e40, 1.0e40 + 1.0e25, 1.0e26));
+ A(!FuzzyEqualsAdditive(1.0e40, 1.0e40 + 1.0e25, 1.0e24));
+
+ // Multiplicative tests using the default epsilon
+ // ... around 1.0
+ A(FuzzyEqualsMultiplicative(1.0, 1.0 + lessThanEpsilon));
+ A(FuzzyEqualsMultiplicative(1.0, 1.0 - lessThanEpsilon));
+ A(FuzzyEqualsMultiplicative(1.0, 1.0 + epsilon));
+ A(!FuzzyEqualsMultiplicative(1.0, 1.0 - epsilon));
+ A(!FuzzyEqualsMultiplicative(1.0, 1.0 + moreThanEpsilon));
+ A(!FuzzyEqualsMultiplicative(1.0, 1.0 - moreThanEpsilon));
+ // ... around 1.0e30
+ A(FuzzyEqualsMultiplicative(1.0e30, 1.0e30 + (lessThanEpsilon * 1.0e30)));
+ A(!FuzzyEqualsMultiplicative(1.0e30, 1.0e30 + (moreThanEpsilon * 1.0e30)));
+ // ... around 1.0e-30
+ A(FuzzyEqualsMultiplicative(1.0e-30, 1.0e-30 + (lessThanEpsilon * 1.0e-30)));
+ A(!FuzzyEqualsMultiplicative(1.0e-30, 1.0e-30 + (moreThanEpsilon * 1.0e-30)));
+ // ... straddling 0
+ A(!FuzzyEqualsMultiplicative(1.0e-6, -1.0e-6));
+ A(FuzzyEqualsMultiplicative(1.0e-6, -1.0e-6, 1.0e2));
+ // Using a small epsilon
+ A(FuzzyEqualsMultiplicative(1.0e-15, 1.0e-15 + 1.0e-30, 1.0e-15));
+ A(!FuzzyEqualsMultiplicative(1.0e-15, 1.0e-15 + 1.0e-30, 1.0e-16));
+ // Using a big epsilon
+ A(FuzzyEqualsMultiplicative(1.0e40, 2.0e40, 1.0));
+ A(!FuzzyEqualsMultiplicative(1.0e40, 2.0e40, 0.1));
+
+ // "real world case"
+ double oneThird = 10.0 / 3.0;
+ A(FuzzyEqualsAdditive(10.0, 3.0 * oneThird));
+ A(FuzzyEqualsMultiplicative(10.0, 3.0 * oneThird));
+ // NaN check
+ A(!FuzzyEqualsAdditive(SpecificNaN<double>(1, 1),
+ SpecificNaN<double>(1, 1)));
+ A(!FuzzyEqualsAdditive(SpecificNaN<double>(1, 2),
+ SpecificNaN<double>(0, 8)));
+ A(!FuzzyEqualsMultiplicative(SpecificNaN<double>(1, 1),
+ SpecificNaN<double>(1, 1)));
+ A(!FuzzyEqualsMultiplicative(SpecificNaN<double>(1, 2),
+ SpecificNaN<double>(0, 200)));
+}
+
+static void
+TestAreApproximatelyEqual()
+{
+ TestFloatsAreApproximatelyEqual();
+ TestDoublesAreApproximatelyEqual();
+}
+
+#undef A
+
+int
+main()
+{
+ TestAreIdentical();
+ TestExponentComponent();
+ TestPredicates();
+ TestAreApproximatelyEqual();
+ return 0;
+}
diff --git a/mfbt/tests/TestFunction.cpp b/mfbt/tests/TestFunction.cpp
new file mode 100644
index 000000000..f9f36ce27
--- /dev/null
+++ b/mfbt/tests/TestFunction.cpp
@@ -0,0 +1,115 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/Function.h"
+
+using mozilla::function;
+
+#define CHECK(c) \
+ do { \
+ bool cond = !!(c); \
+ MOZ_RELEASE_ASSERT(cond, "Failed assertion: " #c); \
+ } while (false)
+
+struct ConvertibleToInt
+{
+ operator int() const { return 42; }
+};
+
+int increment(int arg) { return arg + 1; }
+
+struct S {
+ S() {}
+ static int increment(int arg) { return arg + 1; }
+ int decrement(int arg) { return arg - 1;}
+ int sum(int arg1, int arg2) const { return arg1 + arg2;}
+};
+
+struct Incrementor {
+ int operator()(int arg) { return arg + 1; }
+};
+
+static void
+TestNonmemberFunction()
+{
+ function<int(int)> f = &increment;
+ CHECK(f(42) == 43);
+}
+
+static void
+TestStaticMemberFunction()
+{
+ function<int(int)> f = &S::increment;
+ CHECK(f(42) == 43);
+}
+
+static void
+TestFunctionObject()
+{
+ function<int(int)> f = Incrementor();
+ CHECK(f(42) == 43);
+}
+
+static void
+TestLambda()
+{
+ // Test non-capturing lambda
+ function<int(int)> f = [](int arg){ return arg + 1; };
+ CHECK(f(42) == 43);
+
+ // Test capturing lambda
+ int one = 1;
+ function<int(int)> g = [one](int arg){ return arg + one; };
+ CHECK(g(42) == 43);
+}
+
+static void
+TestDefaultConstructionAndAssignmentLater()
+{
+ function<int(int)> f; // allowed
+ // Would get an assertion if we tried calling f now.
+ f = &increment;
+ CHECK(f(42) == 43);
+}
+
+static void
+TestReassignment()
+{
+ function<int(int)> f = &increment;
+ CHECK(f(42) == 43);
+ f = [](int arg){ return arg + 2; };
+ CHECK(f(42) == 44);
+}
+
+static void
+TestMemberFunction()
+{
+ function<int(S&, int)> f = &S::decrement;
+ S s;
+ CHECK((f(s, 1) == 0));
+}
+
+static void
+TestConstMemberFunction()
+{
+ function<int(const S*, int, int)> f = &S::sum;
+ const S s;
+ CHECK((f(&s, 1, 1) == 2));
+}
+int
+main()
+{
+ TestNonmemberFunction();
+ TestStaticMemberFunction();
+ TestFunctionObject();
+ TestLambda();
+ TestDefaultConstructionAndAssignmentLater();
+ TestReassignment();
+ TestMemberFunction();
+ TestConstMemberFunction();
+ return 0;
+}
diff --git a/mfbt/tests/TestIntegerPrintfMacros.cpp b/mfbt/tests/TestIntegerPrintfMacros.cpp
new file mode 100644
index 000000000..475a88b63
--- /dev/null
+++ b/mfbt/tests/TestIntegerPrintfMacros.cpp
@@ -0,0 +1,1269 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/IntegerPrintfMacros.h" // this must pick up <stdint.h>
+#include "mozilla/Sprintf.h"
+
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+/* Output array and poisoning method shared by all tests. */
+static char gOutput[32];
+
+static void
+PoisonOutput()
+{
+ memset(gOutput, 0xDA, sizeof(gOutput));
+}
+
+/*
+ * The fprintf macros for signed integers are:
+ *
+ * PRIdN PRIdLEASTN PRIdFASTN PRIdMAX PRIdPTR
+ * PRIiN PRIiLEASTN PRIiFASTN PRIiMAX PRIiPTR
+ *
+ * In these names N is the width of the type as described in C99 7.18.1.
+ */
+
+static void
+TestPrintSigned8()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRId8, int8_t(-17));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-17"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIi8, int8_t(42));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "42"));
+}
+
+static void
+TestPrintSigned16()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRId16, int16_t(-289));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-289"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIi16, int16_t(728));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "728"));
+}
+
+static void
+TestPrintSigned32()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRId32, int32_t(-342178));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-342178"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIi32, int32_t(5719283));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "5719283"));
+}
+
+static void
+TestPrintSigned64()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRId64, int64_t(-INT64_C(432157943248732)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-432157943248732"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIi64, int64_t(INT64_C(325719232983)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "325719232983"));
+}
+
+static void
+TestPrintSignedN()
+{
+ TestPrintSigned8();
+ TestPrintSigned16();
+ TestPrintSigned32();
+ TestPrintSigned64();
+}
+
+static void
+TestPrintSignedLeast8()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIdLEAST8, int_least8_t(-17));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-17"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIiLEAST8, int_least8_t(42));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "42"));
+}
+
+static void
+TestPrintSignedLeast16()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIdLEAST16, int_least16_t(-289));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-289"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIiLEAST16, int_least16_t(728));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "728"));
+}
+
+static void
+TestPrintSignedLeast32()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIdLEAST32, int_least32_t(-342178));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-342178"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIiLEAST32, int_least32_t(5719283));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "5719283"));
+}
+
+static void
+TestPrintSignedLeast64()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIdLEAST64, int_least64_t(-INT64_C(432157943248732)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-432157943248732"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIiLEAST64, int_least64_t(INT64_C(325719232983)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "325719232983"));
+}
+
+static void
+TestPrintSignedLeastN()
+{
+ TestPrintSignedLeast8();
+ TestPrintSignedLeast16();
+ TestPrintSignedLeast32();
+ TestPrintSignedLeast64();
+}
+
+static void
+TestPrintSignedFast8()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIdFAST8, int_fast8_t(-17));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-17"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIiFAST8, int_fast8_t(42));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "42"));
+}
+
+static void
+TestPrintSignedFast16()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIdFAST16, int_fast16_t(-289));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-289"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIiFAST16, int_fast16_t(728));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "728"));
+}
+
+static void
+TestPrintSignedFast32()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIdFAST32, int_fast32_t(-342178));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-342178"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIiFAST32, int_fast32_t(5719283));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "5719283"));
+}
+
+static void
+TestPrintSignedFast64()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIdFAST64, int_fast64_t(-INT64_C(432157943248732)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-432157943248732"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIiFAST64, int_fast64_t(INT64_C(325719232983)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "325719232983"));
+}
+
+static void
+TestPrintSignedFastN()
+{
+ TestPrintSignedFast8();
+ TestPrintSignedFast16();
+ TestPrintSignedFast32();
+ TestPrintSignedFast64();
+}
+
+static void
+TestPrintSignedMax()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIdMAX, intmax_t(-INTMAX_C(432157943248732)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-432157943248732"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIiMAX, intmax_t(INTMAX_C(325719232983)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "325719232983"));
+}
+
+static void
+TestPrintSignedPtr()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIdPTR, intptr_t(reinterpret_cast<void*>(12345678)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "12345678"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIiPTR, intptr_t(reinterpret_cast<void*>(87654321)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "87654321"));
+}
+
+static void
+TestPrintSigned()
+{
+ TestPrintSignedN();
+ TestPrintSignedLeastN();
+ TestPrintSignedFastN();
+ TestPrintSignedMax();
+ TestPrintSignedPtr();
+}
+
+/*
+ * The fprintf macros for unsigned integers are:
+ *
+ * PRIoN PRIoLEASTN PRIoFASTN PRIoMAX PRIoPTR
+ * PRIuN PRIuLEASTN PRIuFASTN PRIuMAX PRIuPTR
+ * PRIxN PRIxLEASTN PRIxFASTN PRIxMAX PRIxPTR
+ * PRIXN PRIXLEASTN PRIXFASTN PRIXMAX PRIXPTR
+ *
+ * In these names N is the width of the type as described in C99 7.18.1.
+ */
+
+static void
+TestPrintUnsigned8()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIo8, uint8_t(042));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "42"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIu8, uint8_t(17));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "17"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIx8, uint8_t(0x2a));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIX8, uint8_t(0xCD));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CD"));
+}
+
+static void
+TestPrintUnsigned16()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIo16, uint16_t(04242));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "4242"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIu16, uint16_t(1717));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "1717"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIx16, uint16_t(0x2a2a));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIX16, uint16_t(0xCDCD));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCD"));
+}
+
+static void
+TestPrintUnsigned32()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIo32, uint32_t(0424242));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "424242"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIu32, uint32_t(171717));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "171717"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIx32, uint32_t(0x2a2a2a));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a2a"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIX32, uint32_t(0xCDCDCD));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCDCD"));
+}
+
+static void
+TestPrintUnsigned64()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIo64, uint64_t(UINT64_C(0424242424242)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "424242424242"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIu64, uint64_t(UINT64_C(17171717171717171717)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "17171717171717171717"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIx64, uint64_t(UINT64_C(0x2a2a2a2a2a2a2a)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a2a2a2a2a2a"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIX64, uint64_t(UINT64_C(0xCDCDCDCDCDCD)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCDCDCDCDCD"));
+}
+
+static void
+TestPrintUnsignedN()
+{
+ TestPrintUnsigned8();
+ TestPrintUnsigned16();
+ TestPrintUnsigned32();
+ TestPrintUnsigned64();
+}
+
+static void
+TestPrintUnsignedLeast8()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIoLEAST8, uint_least8_t(042));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "42"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIuLEAST8, uint_least8_t(17));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "17"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIxLEAST8, uint_least8_t(0x2a));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIXLEAST8, uint_least8_t(0xCD));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CD"));
+}
+
+static void
+TestPrintUnsignedLeast16()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIoLEAST16, uint_least16_t(04242));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "4242"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIuLEAST16, uint_least16_t(1717));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "1717"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIxLEAST16, uint_least16_t(0x2a2a));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIXLEAST16, uint_least16_t(0xCDCD));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCD"));
+}
+
+static void
+TestPrintUnsignedLeast32()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIoLEAST32, uint_least32_t(0424242));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "424242"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIuLEAST32, uint_least32_t(171717));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "171717"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIxLEAST32, uint_least32_t(0x2a2a2a));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a2a"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIXLEAST32, uint_least32_t(0xCDCDCD));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCDCD"));
+}
+
+static void
+TestPrintUnsignedLeast64()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIoLEAST64, uint_least64_t(UINT64_C(0424242424242)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "424242424242"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIuLEAST64,
+ uint_least64_t(UINT64_C(17171717171717171717)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "17171717171717171717"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIxLEAST64, uint_least64_t(UINT64_C(0x2a2a2a2a2a2a2a)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a2a2a2a2a2a"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIXLEAST64, uint_least64_t(UINT64_C(0xCDCDCDCDCDCD)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCDCDCDCDCD"));
+}
+
+static void
+TestPrintUnsignedLeastN()
+{
+ TestPrintUnsignedLeast8();
+ TestPrintUnsignedLeast16();
+ TestPrintUnsignedLeast32();
+ TestPrintUnsignedLeast64();
+}
+
+static void
+TestPrintUnsignedFast8()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIoFAST8, uint_fast8_t(042));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "42"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIuFAST8, uint_fast8_t(17));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "17"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIxFAST8, uint_fast8_t(0x2a));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIXFAST8, uint_fast8_t(0xCD));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CD"));
+}
+
+static void
+TestPrintUnsignedFast16()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIoFAST16, uint_fast16_t(04242));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "4242"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIuFAST16, uint_fast16_t(1717));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "1717"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIxFAST16, uint_fast16_t(0x2a2a));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIXFAST16, uint_fast16_t(0xCDCD));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCD"));
+}
+
+static void
+TestPrintUnsignedFast32()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIoFAST32, uint_fast32_t(0424242));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "424242"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIuFAST32, uint_fast32_t(171717));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "171717"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIxFAST32, uint_fast32_t(0x2a2a2a));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a2a"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIXFAST32, uint_fast32_t(0xCDCDCD));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCDCD"));
+}
+
+static void
+TestPrintUnsignedFast64()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIoFAST64, uint_fast64_t(UINT64_C(0424242424242)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "424242424242"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIuFAST64,
+ uint_fast64_t(UINT64_C(17171717171717171717)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "17171717171717171717"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIxFAST64, uint_fast64_t(UINT64_C(0x2a2a2a2a2a2a2a)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a2a2a2a2a2a"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIXFAST64, uint_fast64_t(UINT64_C(0xCDCDCDCDCDCD)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCDCDCDCDCD"));
+}
+
+static void
+TestPrintUnsignedFastN()
+{
+ TestPrintUnsignedFast8();
+ TestPrintUnsignedFast16();
+ TestPrintUnsignedFast32();
+ TestPrintUnsignedFast64();
+}
+
+static void
+TestPrintUnsignedMax()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIoMAX, uintmax_t(UINTMAX_C(432157943248732)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "14220563454333534"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIuMAX, uintmax_t(UINTMAX_C(325719232983)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "325719232983"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIxMAX, uintmax_t(UINTMAX_C(327281321873)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "4c337ca791"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIXMAX, uintmax_t(UINTMAX_C(912389523743523)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "33DD03D75A323"));
+}
+
+static void
+TestPrintUnsignedPtr()
+{
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIoPTR, uintptr_t(reinterpret_cast<void*>(12345678)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "57060516"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIuPTR, uintptr_t(reinterpret_cast<void*>(87654321)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "87654321"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIxPTR, uintptr_t(reinterpret_cast<void*>(0x4c3a791)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "4c3a791"));
+
+ PoisonOutput();
+ SprintfLiteral(gOutput, "%" PRIXPTR, uintptr_t(reinterpret_cast<void*>(0xF328DB)));
+ MOZ_RELEASE_ASSERT(!strcmp(gOutput, "F328DB"));
+}
+
+static void
+TestPrintUnsigned()
+{
+ TestPrintUnsignedN();
+ TestPrintUnsignedLeastN();
+ TestPrintUnsignedFastN();
+ TestPrintUnsignedMax();
+ TestPrintUnsignedPtr();
+}
+
+static void
+TestPrint()
+{
+ TestPrintSigned();
+ TestPrintUnsigned();
+}
+
+/*
+ * The fscanf macros for signed integers are:
+ *
+ * SCNdN SCNdLEASTN SCNdFASTN SCNdMAX SCNdPTR
+ * SCNiN SCNiLEASTN SCNiFASTN SCNiMAX SCNiPTR
+ *
+ * In these names N is the width of the type as described in C99 7.18.1.
+ */
+
+/*
+ * MSVC's scanf is insufficiently powerful to implement all the SCN* macros.
+ * Rather than support some subset of them, we instead support none of them.
+ * See the comment at the top of IntegerPrintfMacros.h. But in case we ever do
+ * support them, the following tests should adequately test implementation
+ * correctness. (Indeed, these tests *revealed* MSVC's limitations.)
+ *
+ * That said, even if MSVC ever picks up complete support, we still probably
+ * don't want to support these, because of the undefined-behavior issue noted
+ * further down in the comment atop IntegerPrintfMacros.h.
+ */
+#define SHOULD_TEST_SCANF_MACROS 0
+
+#if SHOULD_TEST_SCANF_MACROS
+
+/*
+ * glibc header definitions for SCN{d,i,o,u,x}{,LEAST,FAST}8 use the "hh" length
+ * modifier, which is new in C99 (and C++11, by reference). We compile this
+ * file as C++11, so if "hh" is used in these macros, it's standard. But some
+ * versions of gcc wrongly think it isn't and warn about a "non-standard"
+ * modifier. And since these tests mostly exist to verify format-macro/type
+ * consistency (particularly through compiler warnings about incorrect formats),
+ * these warnings are unacceptable. So for now, compile tests for those macros
+ * only if we aren't compiling with gcc.
+ */
+#define SHOULD_TEST_8BIT_FORMAT_MACROS (!(MOZ_IS_GCC))
+
+template<typename T>
+union Input
+{
+ T mI;
+ unsigned char mPun[16];
+};
+
+template<typename T>
+static void
+PoisonInput(Input<T>& aInput)
+{
+ memset(aInput.mPun, 0xDA, sizeof(aInput.mPun));
+}
+
+template<typename T>
+static bool
+ExtraBitsUntouched(const Input<T>& aInput)
+{
+ for (size_t i = sizeof(aInput.mI); i < sizeof(aInput); i++) {
+ if (aInput.mPun[i] != 0xDA) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void
+TestScanSigned8()
+{
+#if SHOULD_TEST_8BIT_FORMAT_MACROS
+ Input<int8_t> u;
+
+ PoisonInput(u);
+ sscanf("-17", "%" SCNd8, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == -17);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("042", "%" SCNi8, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 042);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+#endif
+}
+
+static void
+TestScanSigned16()
+{
+ Input<int16_t> u;
+
+ PoisonInput(u);
+ sscanf("-1742", "%" SCNd16, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == -1742);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("04217", "%" SCNi16, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 04217);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanSigned32()
+{
+ Input<int32_t> u;
+
+ PoisonInput(u);
+ sscanf("-174257", "%" SCNd32, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == -174257);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("0423571", "%" SCNi32, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 0423571);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanSigned64()
+{
+ Input<int64_t> u;
+
+ PoisonInput(u);
+ sscanf("-17425238927232", "%" SCNd64, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == -INT64_C(17425238927232));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("042333576571", "%" SCNi64, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == INT64_C(042333576571));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanSignedN()
+{
+ TestScanSigned8();
+ TestScanSigned16();
+ TestScanSigned32();
+ TestScanSigned64();
+}
+
+static void
+TestScanSignedLeast8()
+{
+#if SHOULD_TEST_8BIT_FORMAT_MACROS
+ Input<int_least8_t> u;
+
+ PoisonInput(u);
+ sscanf("-17", "%" SCNdLEAST8, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == -17);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("042", "%" SCNiLEAST8, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 042);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+#endif
+}
+
+static void
+TestScanSignedLeast16()
+{
+ Input<int_least16_t> u;
+
+ PoisonInput(u);
+ sscanf("-1742", "%" SCNdLEAST16, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == -1742);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("04217", "%" SCNiLEAST16, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 04217);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanSignedLeast32()
+{
+ Input<int_least32_t> u;
+
+ PoisonInput(u);
+ sscanf("-174257", "%" SCNdLEAST32, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == -174257);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("0423571", "%" SCNiLEAST32, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 0423571);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanSignedLeast64()
+{
+ Input<int_least64_t> u;
+
+ PoisonInput(u);
+ sscanf("-17425238927232", "%" SCNdLEAST64, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == -INT64_C(17425238927232));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("042333576571", "%" SCNiLEAST64, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == INT64_C(042333576571));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanSignedLeastN()
+{
+ TestScanSignedLeast8();
+ TestScanSignedLeast16();
+ TestScanSignedLeast32();
+ TestScanSignedLeast64();
+}
+
+static void
+TestScanSignedFast8()
+{
+#if SHOULD_TEST_8BIT_FORMAT_MACROS
+ Input<int_fast8_t> u;
+
+ PoisonInput(u);
+ sscanf("-17", "%" SCNdFAST8, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == -17);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("042", "%" SCNiFAST8, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 042);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+#endif
+}
+
+static void
+TestScanSignedFast16()
+{
+ Input<int_fast16_t> u;
+
+ PoisonInput(u);
+ sscanf("-1742", "%" SCNdFAST16, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == -1742);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("04217", "%" SCNiFAST16, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 04217);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanSignedFast32()
+{
+ Input<int_fast32_t> u;
+
+ PoisonInput(u);
+ sscanf("-174257", "%" SCNdFAST32, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == -174257);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("0423571", "%" SCNiFAST32, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 0423571);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanSignedFast64()
+{
+ Input<int_fast64_t> u;
+
+ PoisonInput(u);
+ sscanf("-17425238927232", "%" SCNdFAST64, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == -INT64_C(17425238927232));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("042333576571", "%" SCNiFAST64, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == INT64_C(042333576571));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanSignedFastN()
+{
+ TestScanSignedFast8();
+ TestScanSignedFast16();
+ TestScanSignedFast32();
+ TestScanSignedFast64();
+}
+
+static void
+TestScanSignedMax()
+{
+ Input<intmax_t> u;
+
+ PoisonInput(u);
+ sscanf("-432157943248732", "%" SCNdMAX, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == -INTMAX_C(432157943248732));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("04233357236571", "%" SCNiMAX, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == INTMAX_C(04233357236571));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanSignedPtr()
+{
+ Input<intptr_t> u;
+
+ PoisonInput(u);
+ sscanf("12345678", "%" SCNdPTR, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == intptr_t(reinterpret_cast<void*>(12345678)));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("04233357236", "%" SCNiPTR, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == intptr_t(reinterpret_cast<void*>(04233357236)));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanSigned()
+{
+ TestScanSignedN();
+ TestScanSignedLeastN();
+ TestScanSignedFastN();
+ TestScanSignedMax();
+ TestScanSignedPtr();
+}
+
+/*
+ * The fscanf macros for unsigned integers are:
+ *
+ * SCNoN SCNoLEASTN SCNoFASTN SCNoMAX SCNoPTR
+ * SCNuN SCNuLEASTN SCNuFASTN SCNuMAX SCNuPTR
+ * SCNxN SCNxLEASTN SCNxFASTN SCNxMAX SCNxPTR
+ *
+ * In these names N is the width of the type as described in C99 7.18.1.
+ */
+
+static void
+TestScanUnsigned8()
+{
+#if SHOULD_TEST_8BIT_FORMAT_MACROS
+ Input<uint8_t> u;
+
+ PoisonInput(u);
+ sscanf("17", "%" SCNo8, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 017);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("42", "%" SCNu8, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 42);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("2A", "%" SCNx8, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 0x2A);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+#endif
+}
+
+static void
+TestScanUnsigned16()
+{
+ Input<uint16_t> u;
+
+ PoisonInput(u);
+ sscanf("1742", "%" SCNo16, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 01742);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("4217", "%" SCNu16, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 4217);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("2ABC", "%" SCNx16, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 0x2ABC);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanUnsigned32()
+{
+ Input<uint32_t> u;
+
+ PoisonInput(u);
+ sscanf("17421742", "%" SCNo32, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 017421742);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("4217867", "%" SCNu32, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 4217867);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("2ABCBEEF", "%" SCNx32, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 0x2ABCBEEF);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanUnsigned64()
+{
+ Input<uint64_t> u;
+
+ PoisonInput(u);
+ sscanf("17421742173", "%" SCNo64, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == UINT64_C(017421742173));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("421786713579", "%" SCNu64, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == UINT64_C(421786713579));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("DEADBEEF7457E", "%" SCNx64, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == UINT64_C(0xDEADBEEF7457E));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanUnsignedN()
+{
+ TestScanUnsigned8();
+ TestScanUnsigned16();
+ TestScanUnsigned32();
+ TestScanUnsigned64();
+}
+
+static void
+TestScanUnsignedLeast8()
+{
+#if SHOULD_TEST_8BIT_FORMAT_MACROS
+ Input<uint_least8_t> u;
+
+ PoisonInput(u);
+ sscanf("17", "%" SCNoLEAST8, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 017);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("42", "%" SCNuLEAST8, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 42);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("2A", "%" SCNxLEAST8, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 0x2A);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+#endif
+}
+
+static void
+TestScanUnsignedLeast16()
+{
+ Input<uint_least16_t> u;
+
+ PoisonInput(u);
+ sscanf("1742", "%" SCNoLEAST16, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 01742);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("4217", "%" SCNuLEAST16, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 4217);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("2ABC", "%" SCNxLEAST16, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 0x2ABC);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanUnsignedLeast32()
+{
+ Input<uint_least32_t> u;
+
+ PoisonInput(u);
+ sscanf("17421742", "%" SCNoLEAST32, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 017421742);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("4217867", "%" SCNuLEAST32, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 4217867);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("2ABCBEEF", "%" SCNxLEAST32, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 0x2ABCBEEF);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanUnsignedLeast64()
+{
+ Input<uint_least64_t> u;
+
+ PoisonInput(u);
+ sscanf("17421742173", "%" SCNoLEAST64, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == UINT64_C(017421742173));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("421786713579", "%" SCNuLEAST64, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == UINT64_C(421786713579));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("DEADBEEF7457E", "%" SCNxLEAST64, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == UINT64_C(0xDEADBEEF7457E));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanUnsignedLeastN()
+{
+ TestScanUnsignedLeast8();
+ TestScanUnsignedLeast16();
+ TestScanUnsignedLeast32();
+ TestScanUnsignedLeast64();
+}
+
+static void
+TestScanUnsignedFast8()
+{
+#if SHOULD_TEST_8BIT_FORMAT_MACROS
+ Input<uint_fast8_t> u;
+
+ PoisonInput(u);
+ sscanf("17", "%" SCNoFAST8, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 017);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("42", "%" SCNuFAST8, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 42);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("2A", "%" SCNxFAST8, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 0x2A);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+#endif
+}
+
+static void
+TestScanUnsignedFast16()
+{
+ Input<uint_fast16_t> u;
+
+ PoisonInput(u);
+ sscanf("1742", "%" SCNoFAST16, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 01742);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("4217", "%" SCNuFAST16, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 4217);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("2ABC", "%" SCNxFAST16, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 0x2ABC);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanUnsignedFast32()
+{
+ Input<uint_fast32_t> u;
+
+ PoisonInput(u);
+ sscanf("17421742", "%" SCNoFAST32, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 017421742);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("4217867", "%" SCNuFAST32, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 4217867);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("2ABCBEEF", "%" SCNxFAST32, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == 0x2ABCBEEF);
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanUnsignedFast64()
+{
+ Input<uint_fast64_t> u;
+
+ PoisonInput(u);
+ sscanf("17421742173", "%" SCNoFAST64, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == UINT64_C(017421742173));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("421786713579", "%" SCNuFAST64, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == UINT64_C(421786713579));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("DEADBEEF7457E", "%" SCNxFAST64, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == UINT64_C(0xDEADBEEF7457E));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanUnsignedFastN()
+{
+ TestScanUnsignedFast8();
+ TestScanUnsignedFast16();
+ TestScanUnsignedFast32();
+ TestScanUnsignedFast64();
+}
+
+static void
+TestScanUnsignedMax()
+{
+ Input<uintmax_t> u;
+
+ PoisonInput(u);
+ sscanf("14220563454333534", "%" SCNoMAX, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == UINTMAX_C(432157943248732));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("432157943248732", "%" SCNuMAX, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == UINTMAX_C(432157943248732));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("4c337ca791", "%" SCNxMAX, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == UINTMAX_C(327281321873));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanUnsignedPtr()
+{
+ Input<uintptr_t> u;
+
+ PoisonInput(u);
+ sscanf("57060516", "%" SCNoPTR, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == uintptr_t(reinterpret_cast<void*>(12345678)));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("87654321", "%" SCNuPTR, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == uintptr_t(reinterpret_cast<void*>(87654321)));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+
+ PoisonInput(u);
+ sscanf("4c3a791", "%" SCNxPTR, &u.i);
+ MOZ_RELEASE_ASSERT(u.i == uintptr_t(reinterpret_cast<void*>(0x4c3a791)));
+ MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u));
+}
+
+static void
+TestScanUnsigned()
+{
+ TestScanUnsignedN();
+ TestScanUnsignedLeastN();
+ TestScanUnsignedFastN();
+ TestScanUnsignedMax();
+ TestScanUnsignedPtr();
+}
+
+static void
+TestScan()
+{
+ TestScanSigned();
+ TestScanUnsigned();
+}
+
+#endif /* SHOULD_TEST_SCANF_MACROS */
+
+int
+main()
+{
+ TestPrint();
+#if SHOULD_TEST_SCANF_MACROS
+ TestScan();
+#endif
+ return 0;
+}
diff --git a/mfbt/tests/TestIntegerRange.cpp b/mfbt/tests/TestIntegerRange.cpp
new file mode 100644
index 000000000..c2691a26c
--- /dev/null
+++ b/mfbt/tests/TestIntegerRange.cpp
@@ -0,0 +1,163 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/IntegerRange.h"
+
+#include <stddef.h>
+
+using mozilla::IsSame;
+using mozilla::MakeRange;
+using mozilla::Reversed;
+
+const size_t kMaxNumber = 50;
+const size_t kArraySize = 256;
+
+template<typename IntType>
+static IntType
+GenerateNumber()
+{
+ return static_cast<IntType>(rand() % kMaxNumber + 1);
+}
+
+template<typename IntType>
+static void
+TestSingleParamRange(const IntType aN)
+{
+ IntType array[kArraySize];
+ IntType* ptr = array;
+ for (auto i : MakeRange(aN)) {
+ static_assert(IsSame<decltype(i), IntType>::value,
+ "type of the loop var and the param should be the same");
+ *ptr++ = i;
+ }
+
+ MOZ_RELEASE_ASSERT(ptr - array == static_cast<ptrdiff_t>(aN),
+ "Should iterates N items");
+ for (size_t i = 0; i < static_cast<size_t>(aN); i++) {
+ MOZ_RELEASE_ASSERT(array[i] == static_cast<IntType>(i),
+ "Values should equal to the index");
+ }
+}
+
+template<typename IntType>
+static void
+TestSingleParamReverseRange(const IntType aN)
+{
+ IntType array[kArraySize];
+ IntType* ptr = array;
+ for (auto i : Reversed(MakeRange(aN))) {
+ static_assert(IsSame<decltype(i), IntType>::value,
+ "type of the loop var and the param should be the same");
+ *ptr++ = i;
+ }
+
+ MOZ_RELEASE_ASSERT(ptr - array == static_cast<ptrdiff_t>(aN),
+ "Should iterates N items");
+ for (size_t i = 0; i < static_cast<size_t>(aN); i++) {
+ MOZ_RELEASE_ASSERT(array[i] == static_cast<IntType>(aN - i - 1),
+ "Values should be the reverse of their index");
+ }
+}
+
+template<typename IntType>
+static void
+TestSingleParamIntegerRange()
+{
+ const auto kN = GenerateNumber<IntType>();
+ TestSingleParamRange<IntType>(0);
+ TestSingleParamReverseRange<IntType>(0);
+ TestSingleParamRange<IntType>(kN);
+ TestSingleParamReverseRange<IntType>(kN);
+}
+
+template<typename IntType1, typename IntType2>
+static void
+TestDoubleParamRange(const IntType1 aBegin, const IntType2 aEnd)
+{
+ IntType2 array[kArraySize];
+ IntType2* ptr = array;
+ for (auto i : MakeRange(aBegin, aEnd)) {
+ static_assert(IsSame<decltype(i), IntType2>::value, "type of the loop var "
+ "should be same as that of the second param");
+ *ptr++ = i;
+ }
+
+ MOZ_RELEASE_ASSERT(ptr - array == static_cast<ptrdiff_t>(aEnd - aBegin),
+ "Should iterates (aEnd - aBegin) times");
+ for (size_t i = 0; i < static_cast<size_t>(aEnd - aBegin); i++) {
+ MOZ_RELEASE_ASSERT(array[i] == static_cast<IntType2>(aBegin + i),
+ "Should iterate integers in [aBegin, aEnd) in order");
+ }
+}
+
+template<typename IntType1, typename IntType2>
+static void
+TestDoubleParamReverseRange(const IntType1 aBegin, const IntType2 aEnd)
+{
+ IntType2 array[kArraySize];
+ IntType2* ptr = array;
+ for (auto i : Reversed(MakeRange(aBegin, aEnd))) {
+ static_assert(IsSame<decltype(i), IntType2>::value, "type of the loop var "
+ "should be same as that of the second param");
+ *ptr++ = i;
+ }
+
+ MOZ_RELEASE_ASSERT(ptr - array == static_cast<ptrdiff_t>(aEnd - aBegin),
+ "Should iterates (aEnd - aBegin) times");
+ for (size_t i = 0; i < static_cast<size_t>(aEnd - aBegin); i++) {
+ MOZ_RELEASE_ASSERT(array[i] == static_cast<IntType2>(aEnd - i - 1),
+ "Should iterate integers in [aBegin, aEnd) in reverse order");
+ }
+}
+
+template<typename IntType1, typename IntType2>
+static void
+TestDoubleParamIntegerRange()
+{
+ const auto kStart = GenerateNumber<IntType1>();
+ const auto kEnd = static_cast<IntType2>(kStart + GenerateNumber<IntType2>());
+ TestDoubleParamRange(kStart, static_cast<IntType2>(kStart));
+ TestDoubleParamReverseRange(kStart, static_cast<IntType2>(kStart));
+ TestDoubleParamRange(kStart, kEnd);
+ TestDoubleParamReverseRange(kStart, kEnd);
+}
+
+int
+main()
+{
+ TestSingleParamIntegerRange<int8_t>();
+ TestSingleParamIntegerRange<int16_t>();
+ TestSingleParamIntegerRange<int32_t>();
+ TestSingleParamIntegerRange<int64_t>();
+
+ TestSingleParamIntegerRange<uint8_t>();
+ TestSingleParamIntegerRange<uint16_t>();
+ TestSingleParamIntegerRange<uint32_t>();
+ TestSingleParamIntegerRange<uint64_t>();
+
+ TestDoubleParamIntegerRange<int8_t, int8_t>();
+ TestDoubleParamIntegerRange<int16_t, int16_t>();
+ TestDoubleParamIntegerRange<int32_t, int32_t>();
+ TestDoubleParamIntegerRange<int64_t, int64_t>();
+
+ TestDoubleParamIntegerRange<uint8_t, uint8_t>();
+ TestDoubleParamIntegerRange<uint16_t, uint16_t>();
+ TestDoubleParamIntegerRange<uint32_t, uint32_t>();
+ TestDoubleParamIntegerRange<uint64_t, uint64_t>();
+
+ TestDoubleParamIntegerRange<int8_t, int16_t>();
+ TestDoubleParamIntegerRange<int16_t, int32_t>();
+ TestDoubleParamIntegerRange<int32_t, int64_t>();
+ TestDoubleParamIntegerRange<int64_t, int8_t>();
+
+ TestDoubleParamIntegerRange<uint8_t, uint64_t>();
+ TestDoubleParamIntegerRange<uint16_t, uint8_t>();
+ TestDoubleParamIntegerRange<uint32_t, uint16_t>();
+ TestDoubleParamIntegerRange<uint64_t, uint32_t>();
+
+ return 0;
+}
diff --git a/mfbt/tests/TestJSONWriter.cpp b/mfbt/tests/TestJSONWriter.cpp
new file mode 100644
index 000000000..6c081d170
--- /dev/null
+++ b/mfbt/tests/TestJSONWriter.cpp
@@ -0,0 +1,539 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/JSONWriter.h"
+#include "mozilla/UniquePtr.h"
+#include <stdio.h>
+#include <string.h>
+
+using mozilla::JSONWriteFunc;
+using mozilla::JSONWriter;
+using mozilla::MakeUnique;
+
+// This writes all the output into a big buffer.
+struct StringWriteFunc : public JSONWriteFunc
+{
+ const static size_t kLen = 100000;
+ char mBuf[kLen];
+ char* mPtr;
+
+ StringWriteFunc() : mPtr(mBuf) {}
+
+ void Write(const char* aStr)
+ {
+ char* last = mPtr + strlen(aStr); // where the nul will be added
+
+ // If you change this test and this assertion fails, just make kLen bigger.
+ MOZ_RELEASE_ASSERT(last < mBuf + kLen);
+ sprintf(mPtr, "%s", aStr);
+ mPtr = last;
+ }
+};
+
+void Check(JSONWriteFunc* aFunc, const char* aExpected)
+{
+ const char* actual = static_cast<StringWriteFunc*>(aFunc)->mBuf;
+ if (strcmp(aExpected, actual) != 0) {
+ fprintf(stderr,
+ "---- EXPECTED ----\n<<<%s>>>\n"
+ "---- ACTUAL ----\n<<<%s>>>\n",
+ aExpected, actual);
+ MOZ_RELEASE_ASSERT(false, "expected and actual output don't match");
+ }
+}
+
+// Note: to convert actual output into |expected| strings that C++ can handle,
+// apply the following substitutions, in order, to each line.
+// - s/\\/\\\\/g # escapes backslashes
+// - s/"/\\"/g # escapes quotes
+// - s/$/\\n\\/ # adds a newline and string continuation char to each line
+
+void TestBasicProperties()
+{
+ const char* expected = "\
+{\n\
+ \"null\": null,\n\
+ \"bool1\": true,\n\
+ \"bool2\": false,\n\
+ \"int1\": 123,\n\
+ \"int2\": -123,\n\
+ \"int3\": -123456789000,\n\
+ \"double1\": 1.2345,\n\
+ \"double2\": -3,\n\
+ \"double3\": 1e-7,\n\
+ \"double4\": 1.1111111111111111e+21,\n\
+ \"string1\": \"\",\n\
+ \"string2\": \"1234\",\n\
+ \"string3\": \"hello\",\n\
+ \"string4\": \"\\\" \\\\ \\u0007 \\b \\t \\n \\u000b \\f \\r\",\n\
+ \"len 0 array, multi-line\": [\n\
+ ],\n\
+ \"len 0 array, single-line\": [],\n\
+ \"len 1 array\": [\n\
+ 1\n\
+ ],\n\
+ \"len 5 array, multi-line\": [\n\
+ 1,\n\
+ 2,\n\
+ 3,\n\
+ 4,\n\
+ 5\n\
+ ],\n\
+ \"len 3 array, single-line\": [1, [{}, 2, []], 3],\n\
+ \"len 0 object, multi-line\": {\n\
+ },\n\
+ \"len 0 object, single-line\": {},\n\
+ \"len 1 object\": {\n\
+ \"one\": 1\n\
+ },\n\
+ \"len 5 object\": {\n\
+ \"one\": 1,\n\
+ \"two\": 2,\n\
+ \"three\": 3,\n\
+ \"four\": 4,\n\
+ \"five\": 5\n\
+ },\n\
+ \"len 3 object, single-line\": {\"a\": 1, \"b\": [{}, 2, []], \"c\": 3}\n\
+}\n\
+";
+
+ JSONWriter w(MakeUnique<StringWriteFunc>());
+
+ w.Start();
+ {
+ w.NullProperty("null");
+
+ w.BoolProperty("bool1", true);
+ w.BoolProperty("bool2", false);
+
+ w.IntProperty("int1", 123);
+ w.IntProperty("int2", -0x7b);
+ w.IntProperty("int3", -123456789000ll);
+
+ w.DoubleProperty("double1", 1.2345);
+ w.DoubleProperty("double2", -3);
+ w.DoubleProperty("double3", 1e-7);
+ w.DoubleProperty("double4", 1.1111111111111111e+21);
+
+ w.StringProperty("string1", "");
+ w.StringProperty("string2", "1234");
+ w.StringProperty("string3", "hello");
+ w.StringProperty("string4", "\" \\ \a \b \t \n \v \f \r");
+
+ w.StartArrayProperty("len 0 array, multi-line", w.MultiLineStyle);
+ w.EndArray();
+
+ w.StartArrayProperty("len 0 array, single-line", w.SingleLineStyle);
+ w.EndArray();
+
+ w.StartArrayProperty("len 1 array");
+ {
+ w.IntElement(1);
+ }
+ w.EndArray();
+
+ w.StartArrayProperty("len 5 array, multi-line", w.MultiLineStyle);
+ {
+ w.IntElement(1);
+ w.IntElement(2);
+ w.IntElement(3);
+ w.IntElement(4);
+ w.IntElement(5);
+ }
+ w.EndArray();
+
+ w.StartArrayProperty("len 3 array, single-line", w.SingleLineStyle);
+ {
+ w.IntElement(1);
+ w.StartArrayElement();
+ {
+ w.StartObjectElement(w.SingleLineStyle);
+ w.EndObject();
+
+ w.IntElement(2);
+
+ w.StartArrayElement(w.MultiLineStyle); // style overridden from above
+ w.EndArray();
+ }
+ w.EndArray();
+ w.IntElement(3);
+ }
+ w.EndArray();
+
+ w.StartObjectProperty("len 0 object, multi-line");
+ w.EndObject();
+
+ w.StartObjectProperty("len 0 object, single-line", w.SingleLineStyle);
+ w.EndObject();
+
+ w.StartObjectProperty("len 1 object");
+ {
+ w.IntProperty("one", 1);
+ }
+ w.EndObject();
+
+ w.StartObjectProperty("len 5 object");
+ {
+ w.IntProperty("one", 1);
+ w.IntProperty("two", 2);
+ w.IntProperty("three", 3);
+ w.IntProperty("four", 4);
+ w.IntProperty("five", 5);
+ }
+ w.EndObject();
+
+ w.StartObjectProperty("len 3 object, single-line", w.SingleLineStyle);
+ {
+ w.IntProperty("a", 1);
+ w.StartArrayProperty("b");
+ {
+ w.StartObjectElement();
+ w.EndObject();
+
+ w.IntElement(2);
+
+ w.StartArrayElement(w.SingleLineStyle);
+ w.EndArray();
+ }
+ w.EndArray();
+ w.IntProperty("c", 3);
+ }
+ w.EndObject();
+ }
+ w.End();
+
+ Check(w.WriteFunc(), expected);
+}
+
+void TestBasicElements()
+{
+ const char* expected = "\
+{\n\
+ \"array\": [\n\
+ null,\n\
+ true,\n\
+ false,\n\
+ 123,\n\
+ -123,\n\
+ -123456789000,\n\
+ 1.2345,\n\
+ -3,\n\
+ 1e-7,\n\
+ 1.1111111111111111e+21,\n\
+ \"\",\n\
+ \"1234\",\n\
+ \"hello\",\n\
+ \"\\\" \\\\ \\u0007 \\b \\t \\n \\u000b \\f \\r\",\n\
+ [\n\
+ ],\n\
+ [],\n\
+ [\n\
+ 1\n\
+ ],\n\
+ [\n\
+ 1,\n\
+ 2,\n\
+ 3,\n\
+ 4,\n\
+ 5\n\
+ ],\n\
+ [1, [{}, 2, []], 3],\n\
+ {\n\
+ },\n\
+ {},\n\
+ {\n\
+ \"one\": 1\n\
+ },\n\
+ {\n\
+ \"one\": 1,\n\
+ \"two\": 2,\n\
+ \"three\": 3,\n\
+ \"four\": 4,\n\
+ \"five\": 5\n\
+ },\n\
+ {\"a\": 1, \"b\": [{}, 2, []], \"c\": 3}\n\
+ ]\n\
+}\n\
+";
+
+ JSONWriter w(MakeUnique<StringWriteFunc>());
+
+ w.Start();
+ w.StartArrayProperty("array");
+ {
+ w.NullElement();
+
+ w.BoolElement(true);
+ w.BoolElement(false);
+
+ w.IntElement(123);
+ w.IntElement(-0x7b);
+ w.IntElement(-123456789000ll);
+
+ w.DoubleElement(1.2345);
+ w.DoubleElement(-3);
+ w.DoubleElement(1e-7);
+ w.DoubleElement(1.1111111111111111e+21);
+
+ w.StringElement("");
+ w.StringElement("1234");
+ w.StringElement("hello");
+ w.StringElement("\" \\ \a \b \t \n \v \f \r");
+
+ w.StartArrayElement();
+ w.EndArray();
+
+ w.StartArrayElement(w.SingleLineStyle);
+ w.EndArray();
+
+ w.StartArrayElement();
+ {
+ w.IntElement(1);
+ }
+ w.EndArray();
+
+ w.StartArrayElement();
+ {
+ w.IntElement(1);
+ w.IntElement(2);
+ w.IntElement(3);
+ w.IntElement(4);
+ w.IntElement(5);
+ }
+ w.EndArray();
+
+ w.StartArrayElement(w.SingleLineStyle);
+ {
+ w.IntElement(1);
+ w.StartArrayElement();
+ {
+ w.StartObjectElement(w.SingleLineStyle);
+ w.EndObject();
+
+ w.IntElement(2);
+
+ w.StartArrayElement(w.MultiLineStyle); // style overridden from above
+ w.EndArray();
+ }
+ w.EndArray();
+ w.IntElement(3);
+ }
+ w.EndArray();
+
+ w.StartObjectElement();
+ w.EndObject();
+
+ w.StartObjectElement(w.SingleLineStyle);
+ w.EndObject();
+
+ w.StartObjectElement();
+ {
+ w.IntProperty("one", 1);
+ }
+ w.EndObject();
+
+ w.StartObjectElement();
+ {
+ w.IntProperty("one", 1);
+ w.IntProperty("two", 2);
+ w.IntProperty("three", 3);
+ w.IntProperty("four", 4);
+ w.IntProperty("five", 5);
+ }
+ w.EndObject();
+
+ w.StartObjectElement(w.SingleLineStyle);
+ {
+ w.IntProperty("a", 1);
+ w.StartArrayProperty("b");
+ {
+ w.StartObjectElement();
+ w.EndObject();
+
+ w.IntElement(2);
+
+ w.StartArrayElement(w.SingleLineStyle);
+ w.EndArray();
+ }
+ w.EndArray();
+ w.IntProperty("c", 3);
+ }
+ w.EndObject();
+ }
+ w.EndArray();
+ w.End();
+
+ Check(w.WriteFunc(), expected);
+}
+
+void TestOneLineObject()
+{
+ const char* expected = "\
+{\"i\": 1, \"array\": [null, [{}], {\"o\": {}}, \"s\"], \"d\": 3.33}\n\
+";
+
+ JSONWriter w(MakeUnique<StringWriteFunc>());
+
+ w.Start(w.SingleLineStyle);
+
+ w.IntProperty("i", 1);
+
+ w.StartArrayProperty("array");
+ {
+ w.NullElement();
+
+ w.StartArrayElement(w.MultiLineStyle); // style overridden from above
+ {
+ w.StartObjectElement();
+ w.EndObject();
+ }
+ w.EndArray();
+
+ w.StartObjectElement();
+ {
+ w.StartObjectProperty("o");
+ w.EndObject();
+ }
+ w.EndObject();
+
+ w.StringElement("s");
+ }
+ w.EndArray();
+
+ w.DoubleProperty("d", 3.33);
+
+ w.End();
+
+ Check(w.WriteFunc(), expected);
+}
+
+void TestStringEscaping()
+{
+ // This test uses hexadecimal character escapes because UTF8 literals cause
+ // problems for some compilers (see bug 1069726).
+ const char* expected = "\
+{\n\
+ \"ascii\": \"\x7F~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\\\"! \\u001f\\u001e\\u001d\\u001c\\u001b\\u001a\\u0019\\u0018\\u0017\\u0016\\u0015\\u0014\\u0013\\u0012\\u0011\\u0010\\u000f\\u000e\\r\\f\\u000b\\n\\t\\b\\u0007\\u0006\\u0005\\u0004\\u0003\\u0002\\u0001\",\n\
+ \"\xD9\x85\xD8\xB1\xD8\xAD\xD8\xA8\xD8\xA7 \xD9\x87\xD9\x86\xD8\xA7\xD9\x83\": true,\n\
+ \"\xD5\xA2\xD5\xA1\xD6\x80\xD5\xA5\xD6\x82 \xD5\xB9\xD5\xAF\xD5\xA1\": -123,\n\
+ \"\xE4\xBD\xA0\xE5\xA5\xBD\": 1.234,\n\
+ \"\xCE\xB3\xCE\xB5\xCE\xB9\xCE\xB1 \xCE\xB5\xCE\xBA\xCE\xB5\xCE\xAF\": \"\xD8\xB3\xD9\x84\xD8\xA7\xD9\x85\",\n\
+ \"hall\xC3\xB3 \xC3\xBE" "arna\": 4660,\n\
+ \"\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1\xE3\x81\xAF\": {\n\
+ \"\xD0\xBF\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82\": [\n\
+ ]\n\
+ }\n\
+}\n\
+";
+
+ JSONWriter w(MakeUnique<StringWriteFunc>());
+
+ // Test the string escaping behaviour.
+ w.Start();
+ {
+ // Test all 127 ascii values. Do it in reverse order so that the 0
+ // at the end serves as the null char.
+ char buf[128];
+ for (int i = 0; i < 128; i++) {
+ buf[i] = 127 - i;
+ }
+ w.StringProperty("ascii", buf);
+
+ // Test lots of unicode stuff. Note that this file is encoded as UTF-8.
+ w.BoolProperty("\xD9\x85\xD8\xB1\xD8\xAD\xD8\xA8\xD8\xA7 \xD9\x87\xD9\x86\xD8\xA7\xD9\x83", true);
+ w.IntProperty("\xD5\xA2\xD5\xA1\xD6\x80\xD5\xA5\xD6\x82 \xD5\xB9\xD5\xAF\xD5\xA1", -123);
+ w.DoubleProperty("\xE4\xBD\xA0\xE5\xA5\xBD", 1.234);
+ w.StringProperty("\xCE\xB3\xCE\xB5\xCE\xB9\xCE\xB1 \xCE\xB5\xCE\xBA\xCE\xB5\xCE\xAF", "\xD8\xB3\xD9\x84\xD8\xA7\xD9\x85");
+ w.IntProperty("hall\xC3\xB3 \xC3\xBE" "arna", 0x1234);
+ w.StartObjectProperty("\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1\xE3\x81\xAF");
+ {
+ w.StartArrayProperty("\xD0\xBF\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82");
+ w.EndArray();
+ }
+ w.EndObject();
+ }
+ w.End();
+
+ Check(w.WriteFunc(), expected);
+}
+
+void TestDeepNesting()
+{
+ const char* expected = "\
+{\n\
+ \"a\": [\n\
+ {\n\
+ \"a\": [\n\
+ {\n\
+ \"a\": [\n\
+ {\n\
+ \"a\": [\n\
+ {\n\
+ \"a\": [\n\
+ {\n\
+ \"a\": [\n\
+ {\n\
+ \"a\": [\n\
+ {\n\
+ \"a\": [\n\
+ {\n\
+ \"a\": [\n\
+ {\n\
+ \"a\": [\n\
+ {\n\
+ }\n\
+ ]\n\
+ }\n\
+ ]\n\
+ }\n\
+ ]\n\
+ }\n\
+ ]\n\
+ }\n\
+ ]\n\
+ }\n\
+ ]\n\
+ }\n\
+ ]\n\
+ }\n\
+ ]\n\
+ }\n\
+ ]\n\
+ }\n\
+ ]\n\
+}\n\
+";
+
+ JSONWriter w(MakeUnique<StringWriteFunc>());
+
+ w.Start();
+ {
+ static const int n = 10;
+ for (int i = 0; i < n; i++) {
+ w.StartArrayProperty("a");
+ w.StartObjectElement();
+ }
+ for (int i = 0; i < n; i++) {
+ w.EndObject();
+ w.EndArray();
+ }
+ }
+ w.End();
+
+ Check(w.WriteFunc(), expected);
+}
+
+int main(void)
+{
+ TestBasicProperties();
+ TestBasicElements();
+ TestOneLineObject();
+ TestStringEscaping();
+ TestDeepNesting();
+
+ return 0;
+}
diff --git a/mfbt/tests/TestLinkedList.cpp b/mfbt/tests/TestLinkedList.cpp
new file mode 100644
index 000000000..0421668b9
--- /dev/null
+++ b/mfbt/tests/TestLinkedList.cpp
@@ -0,0 +1,268 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/LinkedList.h"
+
+using mozilla::LinkedList;
+using mozilla::LinkedListElement;
+
+struct SomeClass : public LinkedListElement<SomeClass> {
+ unsigned int mValue;
+ explicit SomeClass(int aValue = 0) : mValue(aValue) {}
+ void incr() { ++mValue; }
+};
+
+template <size_t N>
+static void
+CheckListValues(LinkedList<SomeClass>& list, unsigned int (&values)[N])
+{
+ size_t count = 0;
+ for (SomeClass* x : list) {
+ MOZ_RELEASE_ASSERT(x->mValue == values[count]);
+ ++count;
+ }
+ MOZ_RELEASE_ASSERT(count == N);
+}
+
+static void
+TestList()
+{
+ LinkedList<SomeClass> list;
+
+ SomeClass one(1), two(2), three(3);
+
+ MOZ_RELEASE_ASSERT(list.isEmpty());
+ MOZ_RELEASE_ASSERT(!list.getFirst());
+ MOZ_RELEASE_ASSERT(!list.getLast());
+ MOZ_RELEASE_ASSERT(!list.popFirst());
+ MOZ_RELEASE_ASSERT(!list.popLast());
+
+ for (SomeClass* x : list) {
+ MOZ_RELEASE_ASSERT(x);
+ MOZ_RELEASE_ASSERT(false);
+ }
+
+ list.insertFront(&one);
+ { unsigned int check[] { 1 }; CheckListValues(list, check); }
+
+ MOZ_RELEASE_ASSERT(one.isInList());
+ MOZ_RELEASE_ASSERT(!two.isInList());
+ MOZ_RELEASE_ASSERT(!three.isInList());
+
+ MOZ_RELEASE_ASSERT(!list.isEmpty());
+ MOZ_RELEASE_ASSERT(list.getFirst()->mValue == 1);
+ MOZ_RELEASE_ASSERT(list.getLast()->mValue == 1);
+
+ list.insertFront(&two);
+ { unsigned int check[] { 2, 1 }; CheckListValues(list, check); }
+
+ MOZ_RELEASE_ASSERT(list.getFirst()->mValue == 2);
+ MOZ_RELEASE_ASSERT(list.getLast()->mValue == 1);
+
+ list.insertBack(&three);
+ { unsigned int check[] { 2, 1, 3 }; CheckListValues(list, check); }
+
+ MOZ_RELEASE_ASSERT(list.getFirst()->mValue == 2);
+ MOZ_RELEASE_ASSERT(list.getLast()->mValue == 3);
+
+ one.removeFrom(list);
+ { unsigned int check[] { 2, 3 }; CheckListValues(list, check); }
+
+ three.setPrevious(&one);
+ { unsigned int check[] { 2, 1, 3 }; CheckListValues(list, check); }
+
+ three.removeFrom(list);
+ { unsigned int check[] { 2, 1 }; CheckListValues(list, check); }
+
+ two.setPrevious(&three);
+ { unsigned int check[] { 3, 2, 1 }; CheckListValues(list, check); }
+
+ three.removeFrom(list);
+ { unsigned int check[] { 2, 1 }; CheckListValues(list, check); }
+
+ two.setNext(&three);
+ { unsigned int check[] { 2, 3, 1 }; CheckListValues(list, check); }
+
+ one.remove();
+ { unsigned int check[] { 2, 3 }; CheckListValues(list, check); }
+
+ two.remove();
+ { unsigned int check[] { 3 }; CheckListValues(list, check); }
+
+ three.setPrevious(&two);
+ { unsigned int check[] { 2, 3 }; CheckListValues(list, check); }
+
+ three.remove();
+ { unsigned int check[] { 2 }; CheckListValues(list, check); }
+
+ two.remove();
+
+ list.insertBack(&three);
+ { unsigned int check[] { 3 }; CheckListValues(list, check); }
+
+ list.insertFront(&two);
+ { unsigned int check[] { 2, 3 }; CheckListValues(list, check); }
+
+ for (SomeClass* x : list) {
+ x->incr();
+ }
+
+ MOZ_RELEASE_ASSERT(list.getFirst() == &two);
+ MOZ_RELEASE_ASSERT(list.getLast() == &three);
+ MOZ_RELEASE_ASSERT(list.getFirst()->mValue == 3);
+ MOZ_RELEASE_ASSERT(list.getLast()->mValue == 4);
+}
+
+static void
+TestMove()
+{
+ auto MakeSomeClass =
+ [] (unsigned int aValue) -> SomeClass { return SomeClass(aValue); };
+
+ LinkedList<SomeClass> list1;
+
+ // Test move constructor for LinkedListElement.
+ SomeClass c1(MakeSomeClass(1));
+ list1.insertBack(&c1);
+
+ // Test move assignment for LinkedListElement from an element not in a
+ // list.
+ SomeClass c2;
+ c2 = MakeSomeClass(2);
+ list1.insertBack(&c2);
+
+ // Test move assignment of LinkedListElement from an element already in a
+ // list.
+ SomeClass c3;
+ c3 = Move(c2);
+ MOZ_RELEASE_ASSERT(!c2.isInList());
+ MOZ_RELEASE_ASSERT(c3.isInList());
+
+ // Test move constructor for LinkedList.
+ LinkedList<SomeClass> list2(Move(list1));
+ { unsigned int check[] { 1, 2 }; CheckListValues(list2, check); }
+ MOZ_RELEASE_ASSERT(list1.isEmpty());
+
+ // Test move assignment for LinkedList.
+ LinkedList<SomeClass> list3;
+ list3 = Move(list2);
+ { unsigned int check[] { 1, 2 }; CheckListValues(list3, check); }
+ MOZ_RELEASE_ASSERT(list2.isEmpty());
+
+ list3.clear();
+}
+
+static void
+TestRemoveAndGet()
+{
+ LinkedList<SomeClass> list;
+
+ SomeClass one(1), two(2), three(3);
+ list.insertBack(&one);
+ list.insertBack(&two);
+ list.insertBack(&three);
+ { unsigned int check[] { 1, 2, 3 }; CheckListValues(list, check); }
+
+ MOZ_RELEASE_ASSERT(two.removeAndGetNext() == &three);
+ { unsigned int check[] { 1, 3 }; CheckListValues(list, check); }
+
+ MOZ_RELEASE_ASSERT(three.removeAndGetPrevious() == &one);
+ { unsigned int check[] { 1 }; CheckListValues(list, check); }
+}
+
+struct PrivateClass : private LinkedListElement<PrivateClass> {
+ friend class mozilla::LinkedList<PrivateClass>;
+ friend class mozilla::LinkedListElement<PrivateClass>;
+};
+
+static void
+TestPrivate()
+{
+ LinkedList<PrivateClass> list;
+ PrivateClass one, two;
+ list.insertBack(&one);
+ list.insertBack(&two);
+
+ size_t count = 0;
+ for (PrivateClass* p : list) {
+ MOZ_RELEASE_ASSERT(p, "cannot have null elements in list");
+ count++;
+ }
+ MOZ_RELEASE_ASSERT(count == 2);
+}
+
+struct CountedClass : public LinkedListElement<RefPtr<CountedClass>>
+{
+ int mCount;
+ void AddRef() { mCount++; }
+ void Release() { mCount--; }
+
+ CountedClass() : mCount(0) {}
+ ~CountedClass() { MOZ_RELEASE_ASSERT(mCount == 0); }
+};
+
+static void
+TestRefPtrList()
+{
+ LinkedList<RefPtr<CountedClass>> list;
+ CountedClass* elt1 = new CountedClass;
+ CountedClass* elt2 = new CountedClass;
+
+ list.insertBack(elt1);
+ list.insertBack(elt2);
+
+ MOZ_RELEASE_ASSERT(elt1->mCount == 1);
+ MOZ_RELEASE_ASSERT(elt2->mCount == 1);
+
+ for (RefPtr<CountedClass> p : list) {
+ MOZ_RELEASE_ASSERT(p->mCount == 2);
+ }
+
+ RefPtr<CountedClass> ptr = list.getFirst();
+ while (ptr) {
+ MOZ_RELEASE_ASSERT(ptr->mCount == 2);
+ RefPtr<CountedClass> next = ptr->getNext();
+ ptr->remove();
+ ptr = Move(next);
+ }
+ ptr = nullptr;
+
+ MOZ_RELEASE_ASSERT(elt1->mCount == 0);
+ MOZ_RELEASE_ASSERT(elt2->mCount == 0);
+
+ list.insertBack(elt1);
+ elt1->setNext(elt2);
+
+ MOZ_RELEASE_ASSERT(elt1->mCount == 1);
+ MOZ_RELEASE_ASSERT(elt2->mCount == 1);
+
+ RefPtr<CountedClass> first = list.popFirst();
+
+ MOZ_RELEASE_ASSERT(elt1->mCount == 1);
+ MOZ_RELEASE_ASSERT(elt2->mCount == 1);
+
+ RefPtr<CountedClass> second = list.popFirst();
+
+ MOZ_RELEASE_ASSERT(elt1->mCount == 1);
+ MOZ_RELEASE_ASSERT(elt2->mCount == 1);
+
+ first = second = nullptr;
+
+ delete elt1;
+ delete elt2;
+}
+
+int
+main()
+{
+ TestList();
+ TestPrivate();
+ TestMove();
+ TestRemoveAndGet();
+ TestRefPtrList();
+ return 0;
+}
diff --git a/mfbt/tests/TestMacroArgs.cpp b/mfbt/tests/TestMacroArgs.cpp
new file mode 100644
index 000000000..0d9760463
--- /dev/null
+++ b/mfbt/tests/TestMacroArgs.cpp
@@ -0,0 +1,31 @@
+/* -*- 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/MacroArgs.h"
+
+MOZ_STATIC_ASSERT_VALID_ARG_COUNT(1);
+MOZ_STATIC_ASSERT_VALID_ARG_COUNT(1, 2);
+
+static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(100, a, b, c) == 1003, "");
+
+static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(, a, b, c) == 3, "");
+static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(, a) == 1, "");
+static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(, !a) == 1, "");
+static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(, (a, b)) == 1, "");
+
+static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(, MOZ_ARGS_AFTER_1(a, b, c)) == 2,
+ "MOZ_ARGS_AFTER_1(a, b, c) should expand to 'b, c'");
+static_assert(MOZ_ARGS_AFTER_2(a, b, 3) == 3,
+ "MOZ_ARGS_AFTER_2(a, b, 3) should expand to '3'");
+
+static_assert(MOZ_ARG_1(10, 20, 30) == 10, "");
+static_assert(MOZ_ARG_2(10, 20, 30) == 20, "");
+
+int
+main()
+{
+ return 0;
+}
diff --git a/mfbt/tests/TestMacroForEach.cpp b/mfbt/tests/TestMacroForEach.cpp
new file mode 100644
index 000000000..04a78d4b3
--- /dev/null
+++ b/mfbt/tests/TestMacroForEach.cpp
@@ -0,0 +1,35 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/MacroForEach.h"
+
+#define HELPER_IDENTITY_PLUS(x) x +
+static_assert(MOZ_FOR_EACH(HELPER_IDENTITY_PLUS, (), (10)) 0 == 10, "");
+static_assert(MOZ_FOR_EACH(HELPER_IDENTITY_PLUS, (), (1, 1, 1)) 0 == 3, "");
+
+#define HELPER_DEFINE_VAR(x) const int test1_##x = x;
+MOZ_FOR_EACH(HELPER_DEFINE_VAR, (), (10, 20))
+static_assert(test1_10 == 10 && test1_20 == 20, "");
+
+#define HELPER_DEFINE_VAR2(k, x) const int test2_##x = k + x;
+MOZ_FOR_EACH(HELPER_DEFINE_VAR2, (5,), (10, 20))
+static_assert(test2_10 == 15 && test2_20 == 25, "");
+
+#define HELPER_IDENTITY_COMMA(k1, k2, x) k1, k2, x,
+
+int
+main()
+{
+ const int a[] = {
+ MOZ_FOR_EACH(HELPER_IDENTITY_COMMA, (1, 2,), (10, 20, 30))
+ };
+ MOZ_RELEASE_ASSERT(a[0] == 1 && a[1] == 2 && a[2] == 10 &&
+ a[3] == 1 && a[4] == 2 && a[5] == 20 &&
+ a[6] == 1 && a[7] == 2 && a[8] == 30,
+ "MOZ_FOR_EACH args enumerated in incorrect order");
+ return 0;
+}
diff --git a/mfbt/tests/TestMathAlgorithms.cpp b/mfbt/tests/TestMathAlgorithms.cpp
new file mode 100644
index 000000000..4e2629cba
--- /dev/null
+++ b/mfbt/tests/TestMathAlgorithms.cpp
@@ -0,0 +1,87 @@
+/* -*- 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/MathAlgorithms.h"
+
+using mozilla::Clamp;
+using mozilla::IsPowerOfTwo;
+
+static void
+TestClamp()
+{
+ MOZ_RELEASE_ASSERT(Clamp(0, 0, 0) == 0);
+ MOZ_RELEASE_ASSERT(Clamp(1, 0, 0) == 0);
+ MOZ_RELEASE_ASSERT(Clamp(-1, 0, 0) == 0);
+
+ MOZ_RELEASE_ASSERT(Clamp(0, 1, 1) == 1);
+ MOZ_RELEASE_ASSERT(Clamp(0, 1, 2) == 1);
+
+ MOZ_RELEASE_ASSERT(Clamp(0, -1, -1) == -1);
+ MOZ_RELEASE_ASSERT(Clamp(0, -2, -1) == -1);
+
+ MOZ_RELEASE_ASSERT(Clamp(0, 1, 3) == 1);
+ MOZ_RELEASE_ASSERT(Clamp(1, 1, 3) == 1);
+ MOZ_RELEASE_ASSERT(Clamp(2, 1, 3) == 2);
+ MOZ_RELEASE_ASSERT(Clamp(3, 1, 3) == 3);
+ MOZ_RELEASE_ASSERT(Clamp(4, 1, 3) == 3);
+ MOZ_RELEASE_ASSERT(Clamp(5, 1, 3) == 3);
+
+ MOZ_RELEASE_ASSERT(Clamp<uint8_t>(UINT8_MAX, 0, UINT8_MAX) == UINT8_MAX);
+ MOZ_RELEASE_ASSERT(Clamp<uint8_t>(0, 0, UINT8_MAX) == 0);
+
+ MOZ_RELEASE_ASSERT(Clamp<int8_t>(INT8_MIN, INT8_MIN, INT8_MAX) == INT8_MIN);
+ MOZ_RELEASE_ASSERT(Clamp<int8_t>(INT8_MIN, 0, INT8_MAX) == 0);
+ MOZ_RELEASE_ASSERT(Clamp<int8_t>(INT8_MAX, INT8_MIN, INT8_MAX) == INT8_MAX);
+ MOZ_RELEASE_ASSERT(Clamp<int8_t>(INT8_MAX, INT8_MIN, 0) == 0);
+}
+
+static void
+TestIsPowerOfTwo()
+{
+ static_assert(!IsPowerOfTwo(0u), "0 isn't a power of two");
+ static_assert(IsPowerOfTwo(1u), "1 is a power of two");
+ static_assert(IsPowerOfTwo(2u), "2 is a power of two");
+ static_assert(!IsPowerOfTwo(3u), "3 isn't a power of two");
+ static_assert(IsPowerOfTwo(4u), "4 is a power of two");
+ static_assert(!IsPowerOfTwo(5u), "5 isn't a power of two");
+ static_assert(!IsPowerOfTwo(6u), "6 isn't a power of two");
+ static_assert(!IsPowerOfTwo(7u), "7 isn't a power of two");
+ static_assert(IsPowerOfTwo(8u), "8 is a power of two");
+ static_assert(!IsPowerOfTwo(9u), "9 isn't a power of two");
+
+ static_assert(!IsPowerOfTwo(uint8_t(UINT8_MAX/2)), "127, 0x7f isn't a power of two");
+ static_assert(IsPowerOfTwo(uint8_t(UINT8_MAX/2 + 1)), "128, 0x80 is a power of two");
+ static_assert(!IsPowerOfTwo(uint8_t(UINT8_MAX/2 + 2)), "129, 0x81 isn't a power of two");
+ static_assert(!IsPowerOfTwo(uint8_t(UINT8_MAX - 1)), "254, 0xfe isn't a power of two");
+ static_assert(!IsPowerOfTwo(uint8_t(UINT8_MAX)), "255, 0xff isn't a power of two");
+
+ static_assert(!IsPowerOfTwo(uint16_t(UINT16_MAX/2)), "0x7fff isn't a power of two");
+ static_assert(IsPowerOfTwo(uint16_t(UINT16_MAX/2 + 1)), "0x8000 is a power of two");
+ static_assert(!IsPowerOfTwo(uint16_t(UINT16_MAX/2 + 2)), "0x8001 isn't a power of two");
+ static_assert(!IsPowerOfTwo(uint16_t(UINT16_MAX - 1)), "0xfffe isn't a power of two");
+ static_assert(!IsPowerOfTwo(uint16_t(UINT16_MAX)), "0xffff isn't a power of two");
+
+ static_assert(!IsPowerOfTwo(uint32_t(UINT32_MAX/2)), "0x7fffffff isn't a power of two");
+ static_assert(IsPowerOfTwo(uint32_t(UINT32_MAX/2 + 1)), "0x80000000 is a power of two");
+ static_assert(!IsPowerOfTwo(uint32_t(UINT32_MAX/2 + 2)), "0x80000001 isn't a power of two");
+ static_assert(!IsPowerOfTwo(uint32_t(UINT32_MAX - 1)), "0xfffffffe isn't a power of two");
+ static_assert(!IsPowerOfTwo(uint32_t(UINT32_MAX)), "0xffffffff isn't a power of two");
+
+ static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX/2)), "0x7fffffffffffffff isn't a power of two");
+ static_assert(IsPowerOfTwo(uint64_t(UINT64_MAX/2 + 1)), "0x8000000000000000 is a power of two");
+ static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX/2 + 2)), "0x8000000000000001 isn't a power of two");
+ static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX - 1)), "0xfffffffffffffffe isn't a power of two");
+ static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX)), "0xffffffffffffffff isn't a power of two");
+}
+
+int
+main()
+{
+ TestIsPowerOfTwo();
+ TestClamp();
+
+ return 0;
+}
diff --git a/mfbt/tests/TestMaybe.cpp b/mfbt/tests/TestMaybe.cpp
new file mode 100644
index 000000000..5817ab428
--- /dev/null
+++ b/mfbt/tests/TestMaybe.cpp
@@ -0,0 +1,860 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 <utility>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Compiler.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Move.h"
+#include "mozilla/Types.h"
+#include "mozilla/TypeTraits.h"
+#include "mozilla/UniquePtr.h"
+
+using mozilla::IsSame;
+using mozilla::Maybe;
+using mozilla::Move;
+using mozilla::Nothing;
+using mozilla::Some;
+using mozilla::Swap;
+using mozilla::ToMaybe;
+using mozilla::UniquePtr;
+
+#if MOZ_IS_MSVC
+ template<typename T> struct Identity { typedef T type; };
+# define DECLTYPE(EXPR) Identity<decltype(EXPR)>::type
+#else
+# define DECLTYPE(EXPR) decltype(EXPR)
+#endif
+
+#define RUN_TEST(t) \
+ do { \
+ bool cond = (t()); \
+ if (!cond) \
+ return 1; \
+ cond = AllDestructorsWereCalled(); \
+ MOZ_ASSERT(cond, "Failed to destroy all objects during test: " #t); \
+ if (!cond) \
+ return 1; \
+ } while (false)
+
+enum Status
+{
+ eWasDefaultConstructed,
+ eWasConstructed,
+ eWasCopyConstructed,
+ eWasMoveConstructed,
+ eWasCopyAssigned,
+ eWasMoveAssigned,
+ eWasMovedFrom
+};
+
+static size_t sUndestroyedObjects = 0;
+
+static bool AllDestructorsWereCalled()
+{
+ return sUndestroyedObjects == 0;
+}
+
+struct BasicValue
+{
+ BasicValue()
+ : mStatus(eWasDefaultConstructed)
+ , mTag(0)
+ {
+ ++sUndestroyedObjects;
+ }
+
+ explicit BasicValue(int aTag)
+ : mStatus(eWasConstructed)
+ , mTag(aTag)
+ {
+ ++sUndestroyedObjects;
+ }
+
+ BasicValue(const BasicValue& aOther)
+ : mStatus(eWasCopyConstructed)
+ , mTag(aOther.mTag)
+ {
+ ++sUndestroyedObjects;
+ }
+
+ BasicValue(BasicValue&& aOther)
+ : mStatus(eWasMoveConstructed)
+ , mTag(aOther.mTag)
+ {
+ ++sUndestroyedObjects;
+ aOther.mStatus = eWasMovedFrom;
+ aOther.mTag = 0;
+ }
+
+ ~BasicValue() { --sUndestroyedObjects; }
+
+ BasicValue& operator=(const BasicValue& aOther)
+ {
+ mStatus = eWasCopyAssigned;
+ mTag = aOther.mTag;
+ return *this;
+ }
+
+ BasicValue& operator=(BasicValue&& aOther)
+ {
+ mStatus = eWasMoveAssigned;
+ mTag = aOther.mTag;
+ aOther.mStatus = eWasMovedFrom;
+ aOther.mTag = 0;
+ return *this;
+ }
+
+ bool operator==(const BasicValue& aOther) const
+ {
+ return mTag == aOther.mTag;
+ }
+
+ bool operator<(const BasicValue& aOther) const
+ {
+ return mTag < aOther.mTag;
+ }
+
+ Status GetStatus() const { return mStatus; }
+ void SetTag(int aValue) { mTag = aValue; }
+ int GetTag() const { return mTag; }
+
+private:
+ Status mStatus;
+ int mTag;
+};
+
+struct UncopyableValue
+{
+ UncopyableValue()
+ : mStatus(eWasDefaultConstructed)
+ {
+ ++sUndestroyedObjects;
+ }
+
+ UncopyableValue(UncopyableValue&& aOther)
+ : mStatus(eWasMoveConstructed)
+ {
+ ++sUndestroyedObjects;
+ aOther.mStatus = eWasMovedFrom;
+ }
+
+ ~UncopyableValue() { --sUndestroyedObjects; }
+
+ UncopyableValue& operator=(UncopyableValue&& aOther)
+ {
+ mStatus = eWasMoveAssigned;
+ aOther.mStatus = eWasMovedFrom;
+ return *this;
+ }
+
+ Status GetStatus() { return mStatus; }
+
+private:
+ UncopyableValue(const UncopyableValue& aOther) = delete;
+ UncopyableValue& operator=(const UncopyableValue& aOther) = delete;
+
+ Status mStatus;
+};
+
+struct UnmovableValue
+{
+ UnmovableValue()
+ : mStatus(eWasDefaultConstructed)
+ {
+ ++sUndestroyedObjects;
+ }
+
+ UnmovableValue(const UnmovableValue& aOther)
+ : mStatus(eWasCopyConstructed)
+ {
+ ++sUndestroyedObjects;
+ }
+
+ ~UnmovableValue() { --sUndestroyedObjects; }
+
+ UnmovableValue& operator=(const UnmovableValue& aOther)
+ {
+ mStatus = eWasCopyAssigned;
+ return *this;
+ }
+
+ Status GetStatus() { return mStatus; }
+
+private:
+ UnmovableValue(UnmovableValue&& aOther) = delete;
+ UnmovableValue& operator=(UnmovableValue&& aOther) = delete;
+
+ Status mStatus;
+};
+
+struct UncopyableUnmovableValue
+{
+ UncopyableUnmovableValue()
+ : mStatus(eWasDefaultConstructed)
+ {
+ ++sUndestroyedObjects;
+ }
+
+ explicit UncopyableUnmovableValue(int)
+ : mStatus(eWasConstructed)
+ {
+ ++sUndestroyedObjects;
+ }
+
+ ~UncopyableUnmovableValue() { --sUndestroyedObjects; }
+
+ Status GetStatus() { return mStatus; }
+
+private:
+ UncopyableUnmovableValue(const UncopyableUnmovableValue& aOther) = delete;
+ UncopyableUnmovableValue& operator=(const UncopyableUnmovableValue& aOther) = delete;
+ UncopyableUnmovableValue(UncopyableUnmovableValue&& aOther) = delete;
+ UncopyableUnmovableValue& operator=(UncopyableUnmovableValue&& aOther) = delete;
+
+ Status mStatus;
+};
+
+static bool
+TestBasicFeatures()
+{
+ // Check that a Maybe<T> is initialized to Nothing.
+ Maybe<BasicValue> mayValue;
+ static_assert(IsSame<BasicValue, DECLTYPE(mayValue)::ValueType>::value,
+ "Should have BasicValue ValueType");
+ MOZ_RELEASE_ASSERT(!mayValue);
+ MOZ_RELEASE_ASSERT(!mayValue.isSome());
+ MOZ_RELEASE_ASSERT(mayValue.isNothing());
+
+ // Check that emplace() default constructs and the accessors work.
+ mayValue.emplace();
+ MOZ_RELEASE_ASSERT(mayValue);
+ MOZ_RELEASE_ASSERT(mayValue.isSome());
+ MOZ_RELEASE_ASSERT(!mayValue.isNothing());
+ MOZ_RELEASE_ASSERT(*mayValue == BasicValue());
+ MOZ_RELEASE_ASSERT(mayValue.value() == BasicValue());
+ static_assert(IsSame<BasicValue, DECLTYPE(mayValue.value())>::value,
+ "value() should return a BasicValue");
+ MOZ_RELEASE_ASSERT(mayValue.ref() == BasicValue());
+ static_assert(IsSame<BasicValue&, DECLTYPE(mayValue.ref())>::value,
+ "ref() should return a BasicValue&");
+ MOZ_RELEASE_ASSERT(mayValue.ptr() != nullptr);
+ static_assert(IsSame<BasicValue*, DECLTYPE(mayValue.ptr())>::value,
+ "ptr() should return a BasicValue*");
+ MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasDefaultConstructed);
+
+ // Check that reset() works.
+ mayValue.reset();
+ MOZ_RELEASE_ASSERT(!mayValue);
+ MOZ_RELEASE_ASSERT(!mayValue.isSome());
+ MOZ_RELEASE_ASSERT(mayValue.isNothing());
+
+ // Check that emplace(T1) calls the correct constructor.
+ mayValue.emplace(1);
+ MOZ_RELEASE_ASSERT(mayValue);
+ MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasConstructed);
+ MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1);
+ mayValue.reset();
+ MOZ_RELEASE_ASSERT(!mayValue);
+
+ // Check that Some() and Nothing() work.
+ mayValue = Some(BasicValue(2));
+ MOZ_RELEASE_ASSERT(mayValue);
+ MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasMoveConstructed);
+ MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2);
+ mayValue = Nothing();
+ MOZ_RELEASE_ASSERT(!mayValue);
+
+ // Check that the accessors work through a const ref.
+ mayValue.emplace();
+ const Maybe<BasicValue>& mayValueCRef = mayValue;
+ MOZ_RELEASE_ASSERT(mayValueCRef);
+ MOZ_RELEASE_ASSERT(mayValueCRef.isSome());
+ MOZ_RELEASE_ASSERT(!mayValueCRef.isNothing());
+ MOZ_RELEASE_ASSERT(*mayValueCRef == BasicValue());
+ MOZ_RELEASE_ASSERT(mayValueCRef.value() == BasicValue());
+ static_assert(IsSame<BasicValue, DECLTYPE(mayValueCRef.value())>::value,
+ "value() should return a BasicValue");
+ MOZ_RELEASE_ASSERT(mayValueCRef.ref() == BasicValue());
+ static_assert(IsSame<const BasicValue&,
+ DECLTYPE(mayValueCRef.ref())>::value,
+ "ref() should return a const BasicValue&");
+ MOZ_RELEASE_ASSERT(mayValueCRef.ptr() != nullptr);
+ static_assert(IsSame<const BasicValue*,
+ DECLTYPE(mayValueCRef.ptr())>::value,
+ "ptr() should return a const BasicValue*");
+ MOZ_RELEASE_ASSERT(mayValueCRef->GetStatus() == eWasDefaultConstructed);
+ mayValue.reset();
+
+ return true;
+}
+
+static bool
+TestCopyAndMove()
+{
+ // Check that we get moves when possible for types that can support both moves
+ // and copies.
+ Maybe<BasicValue> mayBasicValue = Some(BasicValue(1));
+ MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMoveConstructed);
+ MOZ_RELEASE_ASSERT(mayBasicValue->GetTag() == 1);
+ mayBasicValue = Some(BasicValue(2));
+ MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMoveAssigned);
+ MOZ_RELEASE_ASSERT(mayBasicValue->GetTag() == 2);
+ mayBasicValue.reset();
+ mayBasicValue.emplace(BasicValue(3));
+ MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMoveConstructed);
+ MOZ_RELEASE_ASSERT(mayBasicValue->GetTag() == 3);
+
+ // Check that we get copies when moves aren't possible.
+ Maybe<BasicValue> mayBasicValue2 = Some(*mayBasicValue);
+ MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasCopyConstructed);
+ MOZ_RELEASE_ASSERT(mayBasicValue2->GetTag() == 3);
+ mayBasicValue->SetTag(4);
+ mayBasicValue2 = mayBasicValue;
+ // This test should work again when we fix bug 1052940.
+ //MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasCopyAssigned);
+ MOZ_RELEASE_ASSERT(mayBasicValue2->GetTag() == 4);
+ mayBasicValue->SetTag(5);
+ mayBasicValue2.reset();
+ mayBasicValue2.emplace(*mayBasicValue);
+ MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasCopyConstructed);
+ MOZ_RELEASE_ASSERT(mayBasicValue2->GetTag() == 5);
+
+ // Check that Move() works. (Another sanity check for move support.)
+ Maybe<BasicValue> mayBasicValue3 = Some(Move(*mayBasicValue));
+ MOZ_RELEASE_ASSERT(mayBasicValue3->GetStatus() == eWasMoveConstructed);
+ MOZ_RELEASE_ASSERT(mayBasicValue3->GetTag() == 5);
+ MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMovedFrom);
+ mayBasicValue2->SetTag(6);
+ mayBasicValue3 = Some(Move(*mayBasicValue2));
+ MOZ_RELEASE_ASSERT(mayBasicValue3->GetStatus() == eWasMoveAssigned);
+ MOZ_RELEASE_ASSERT(mayBasicValue3->GetTag() == 6);
+ MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasMovedFrom);
+ Maybe<BasicValue> mayBasicValue4;
+ mayBasicValue4.emplace(Move(*mayBasicValue3));
+ MOZ_RELEASE_ASSERT(mayBasicValue4->GetStatus() == eWasMoveConstructed);
+ MOZ_RELEASE_ASSERT(mayBasicValue4->GetTag() == 6);
+ MOZ_RELEASE_ASSERT(mayBasicValue3->GetStatus() == eWasMovedFrom);
+
+ // Check that we always get copies for types that don't support moves.
+ // XXX(seth): These tests fail but probably shouldn't. For now we'll just
+ // consider using Maybe with types that allow copies but have deleted or
+ // private move constructors, or which do not support copy assignment, to
+ // be supported only to the extent that we need for existing code to work.
+ // These tests should work again when we fix bug 1052940.
+ /*
+ Maybe<UnmovableValue> mayUnmovableValue = Some(UnmovableValue());
+ MOZ_RELEASE_ASSERT(mayUnmovableValue->GetStatus() == eWasCopyConstructed);
+ mayUnmovableValue = Some(UnmovableValue());
+ MOZ_RELEASE_ASSERT(mayUnmovableValue->GetStatus() == eWasCopyAssigned);
+ mayUnmovableValue.reset();
+ mayUnmovableValue.emplace(UnmovableValue());
+ MOZ_RELEASE_ASSERT(mayUnmovableValue->GetStatus() == eWasCopyConstructed);
+ */
+
+ // Check that types that only support moves, but not copies, work.
+ Maybe<UncopyableValue> mayUncopyableValue = Some(UncopyableValue());
+ MOZ_RELEASE_ASSERT(mayUncopyableValue->GetStatus() == eWasMoveConstructed);
+ mayUncopyableValue = Some(UncopyableValue());
+ MOZ_RELEASE_ASSERT(mayUncopyableValue->GetStatus() == eWasMoveAssigned);
+ mayUncopyableValue.reset();
+ mayUncopyableValue.emplace(UncopyableValue());
+ MOZ_RELEASE_ASSERT(mayUncopyableValue->GetStatus() == eWasMoveConstructed);
+
+ // Check that types that support neither moves or copies work.
+ Maybe<UncopyableUnmovableValue> mayUncopyableUnmovableValue;
+ mayUncopyableUnmovableValue.emplace();
+ MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValue->GetStatus() == eWasDefaultConstructed);
+ mayUncopyableUnmovableValue.reset();
+ mayUncopyableUnmovableValue.emplace(0);
+ MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValue->GetStatus() == eWasConstructed);
+
+ return true;
+}
+
+static BasicValue* sStaticBasicValue = nullptr;
+
+static BasicValue
+MakeBasicValue()
+{
+ return BasicValue(9);
+}
+
+static BasicValue&
+MakeBasicValueRef()
+{
+ return *sStaticBasicValue;
+}
+
+static BasicValue*
+MakeBasicValuePtr()
+{
+ return sStaticBasicValue;
+}
+
+static bool
+TestFunctionalAccessors()
+{
+ BasicValue value(9);
+ sStaticBasicValue = new BasicValue(9);
+
+ // Check that the 'some' case of functional accessors works.
+ Maybe<BasicValue> someValue = Some(BasicValue(3));
+ MOZ_RELEASE_ASSERT(someValue.valueOr(value) == BasicValue(3));
+ static_assert(IsSame<BasicValue,
+ DECLTYPE(someValue.valueOr(value))>::value,
+ "valueOr should return a BasicValue");
+ MOZ_RELEASE_ASSERT(someValue.valueOrFrom(&MakeBasicValue) == BasicValue(3));
+ static_assert(IsSame<BasicValue,
+ DECLTYPE(someValue.valueOrFrom(&MakeBasicValue))>::value,
+ "valueOrFrom should return a BasicValue");
+ MOZ_RELEASE_ASSERT(someValue.ptrOr(&value) != &value);
+ static_assert(IsSame<BasicValue*,
+ DECLTYPE(someValue.ptrOr(&value))>::value,
+ "ptrOr should return a BasicValue*");
+ MOZ_RELEASE_ASSERT(*someValue.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(3));
+ static_assert(IsSame<BasicValue*,
+ DECLTYPE(someValue.ptrOrFrom(&MakeBasicValuePtr))>::value,
+ "ptrOrFrom should return a BasicValue*");
+ MOZ_RELEASE_ASSERT(someValue.refOr(value) == BasicValue(3));
+ static_assert(IsSame<BasicValue&,
+ DECLTYPE(someValue.refOr(value))>::value,
+ "refOr should return a BasicValue&");
+ MOZ_RELEASE_ASSERT(someValue.refOrFrom(&MakeBasicValueRef) == BasicValue(3));
+ static_assert(IsSame<BasicValue&,
+ DECLTYPE(someValue.refOrFrom(&MakeBasicValueRef))>::value,
+ "refOrFrom should return a BasicValue&");
+
+ // Check that the 'some' case works through a const reference.
+ const Maybe<BasicValue>& someValueCRef = someValue;
+ MOZ_RELEASE_ASSERT(someValueCRef.valueOr(value) == BasicValue(3));
+ static_assert(IsSame<BasicValue,
+ DECLTYPE(someValueCRef.valueOr(value))>::value,
+ "valueOr should return a BasicValue");
+ MOZ_RELEASE_ASSERT(someValueCRef.valueOrFrom(&MakeBasicValue) == BasicValue(3));
+ static_assert(IsSame<BasicValue,
+ DECLTYPE(someValueCRef.valueOrFrom(&MakeBasicValue))>::value,
+ "valueOrFrom should return a BasicValue");
+ MOZ_RELEASE_ASSERT(someValueCRef.ptrOr(&value) != &value);
+ static_assert(IsSame<const BasicValue*,
+ DECLTYPE(someValueCRef.ptrOr(&value))>::value,
+ "ptrOr should return a const BasicValue*");
+ MOZ_RELEASE_ASSERT(*someValueCRef.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(3));
+ static_assert(IsSame<const BasicValue*,
+ DECLTYPE(someValueCRef.ptrOrFrom(&MakeBasicValuePtr))>::value,
+ "ptrOrFrom should return a const BasicValue*");
+ MOZ_RELEASE_ASSERT(someValueCRef.refOr(value) == BasicValue(3));
+ static_assert(IsSame<const BasicValue&,
+ DECLTYPE(someValueCRef.refOr(value))>::value,
+ "refOr should return a const BasicValue&");
+ MOZ_RELEASE_ASSERT(someValueCRef.refOrFrom(&MakeBasicValueRef) == BasicValue(3));
+ static_assert(IsSame<const BasicValue&,
+ DECLTYPE(someValueCRef.refOrFrom(&MakeBasicValueRef))>::value,
+ "refOrFrom should return a const BasicValue&");
+
+ // Check that the 'none' case of functional accessors works.
+ Maybe<BasicValue> noneValue;
+ MOZ_RELEASE_ASSERT(noneValue.valueOr(value) == BasicValue(9));
+ static_assert(IsSame<BasicValue,
+ DECLTYPE(noneValue.valueOr(value))>::value,
+ "valueOr should return a BasicValue");
+ MOZ_RELEASE_ASSERT(noneValue.valueOrFrom(&MakeBasicValue) == BasicValue(9));
+ static_assert(IsSame<BasicValue,
+ DECLTYPE(noneValue.valueOrFrom(&MakeBasicValue))>::value,
+ "valueOrFrom should return a BasicValue");
+ MOZ_RELEASE_ASSERT(noneValue.ptrOr(&value) == &value);
+ static_assert(IsSame<BasicValue*,
+ DECLTYPE(noneValue.ptrOr(&value))>::value,
+ "ptrOr should return a BasicValue*");
+ MOZ_RELEASE_ASSERT(*noneValue.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(9));
+ static_assert(IsSame<BasicValue*,
+ DECLTYPE(noneValue.ptrOrFrom(&MakeBasicValuePtr))>::value,
+ "ptrOrFrom should return a BasicValue*");
+ MOZ_RELEASE_ASSERT(noneValue.refOr(value) == BasicValue(9));
+ static_assert(IsSame<BasicValue&,
+ DECLTYPE(noneValue.refOr(value))>::value,
+ "refOr should return a BasicValue&");
+ MOZ_RELEASE_ASSERT(noneValue.refOrFrom(&MakeBasicValueRef) == BasicValue(9));
+ static_assert(IsSame<BasicValue&,
+ DECLTYPE(noneValue.refOrFrom(&MakeBasicValueRef))>::value,
+ "refOrFrom should return a BasicValue&");
+
+ // Check that the 'none' case works through a const reference.
+ const Maybe<BasicValue>& noneValueCRef = noneValue;
+ MOZ_RELEASE_ASSERT(noneValueCRef.valueOr(value) == BasicValue(9));
+ static_assert(IsSame<BasicValue,
+ DECLTYPE(noneValueCRef.valueOr(value))>::value,
+ "valueOr should return a BasicValue");
+ MOZ_RELEASE_ASSERT(noneValueCRef.valueOrFrom(&MakeBasicValue) == BasicValue(9));
+ static_assert(IsSame<BasicValue,
+ DECLTYPE(noneValueCRef.valueOrFrom(&MakeBasicValue))>::value,
+ "valueOrFrom should return a BasicValue");
+ MOZ_RELEASE_ASSERT(noneValueCRef.ptrOr(&value) == &value);
+ static_assert(IsSame<const BasicValue*,
+ DECLTYPE(noneValueCRef.ptrOr(&value))>::value,
+ "ptrOr should return a const BasicValue*");
+ MOZ_RELEASE_ASSERT(*noneValueCRef.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(9));
+ static_assert(IsSame<const BasicValue*,
+ DECLTYPE(noneValueCRef.ptrOrFrom(&MakeBasicValuePtr))>::value,
+ "ptrOrFrom should return a const BasicValue*");
+ MOZ_RELEASE_ASSERT(noneValueCRef.refOr(value) == BasicValue(9));
+ static_assert(IsSame<const BasicValue&,
+ DECLTYPE(noneValueCRef.refOr(value))>::value,
+ "refOr should return a const BasicValue&");
+ MOZ_RELEASE_ASSERT(noneValueCRef.refOrFrom(&MakeBasicValueRef) == BasicValue(9));
+ static_assert(IsSame<const BasicValue&,
+ DECLTYPE(noneValueCRef.refOrFrom(&MakeBasicValueRef))>::value,
+ "refOrFrom should return a const BasicValue&");
+
+ // Clean up so the undestroyed objects count stays accurate.
+ delete sStaticBasicValue;
+ sStaticBasicValue = nullptr;
+
+ return true;
+}
+
+static bool gFunctionWasApplied = false;
+
+static void
+IncrementTag(BasicValue& aValue)
+{
+ gFunctionWasApplied = true;
+ aValue.SetTag(aValue.GetTag() + 1);
+}
+
+static void
+AccessValue(const BasicValue&)
+{
+ gFunctionWasApplied = true;
+}
+
+struct IncrementTagFunctor
+{
+ IncrementTagFunctor() : mBy(1) { }
+
+ void operator()(BasicValue& aValue)
+ {
+ aValue.SetTag(aValue.GetTag() + mBy.GetTag());
+ }
+
+ BasicValue mBy;
+};
+
+static bool
+TestApply()
+{
+ // Check that apply handles the 'Nothing' case.
+ gFunctionWasApplied = false;
+ Maybe<BasicValue> mayValue;
+ mayValue.apply(&IncrementTag);
+ mayValue.apply(&AccessValue);
+ MOZ_RELEASE_ASSERT(!gFunctionWasApplied);
+
+ // Check that apply handles the 'Some' case.
+ mayValue = Some(BasicValue(1));
+ mayValue.apply(&IncrementTag);
+ MOZ_RELEASE_ASSERT(gFunctionWasApplied);
+ MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2);
+ gFunctionWasApplied = false;
+ mayValue.apply(&AccessValue);
+ MOZ_RELEASE_ASSERT(gFunctionWasApplied);
+
+ // Check that apply works with a const reference.
+ const Maybe<BasicValue>& mayValueCRef = mayValue;
+ gFunctionWasApplied = false;
+ mayValueCRef.apply(&AccessValue);
+ MOZ_RELEASE_ASSERT(gFunctionWasApplied);
+
+ // Check that apply works with functors.
+ IncrementTagFunctor tagIncrementer;
+ MOZ_RELEASE_ASSERT(tagIncrementer.mBy.GetStatus() == eWasConstructed);
+ mayValue = Some(BasicValue(1));
+ mayValue.apply(tagIncrementer);
+ MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2);
+ MOZ_RELEASE_ASSERT(tagIncrementer.mBy.GetStatus() == eWasConstructed);
+
+ // Check that apply works with lambda expressions.
+ int32_t two = 2;
+ gFunctionWasApplied = false;
+ mayValue = Some(BasicValue(2));
+ mayValue.apply([&](BasicValue& aVal) { aVal.SetTag(aVal.GetTag() * two); });
+ MOZ_RELEASE_ASSERT(mayValue->GetTag() == 4);
+ mayValue.apply([=](BasicValue& aVal) { aVal.SetTag(aVal.GetTag() * two); });
+ MOZ_RELEASE_ASSERT(mayValue->GetTag() == 8);
+ mayValueCRef.apply([&](const BasicValue& aVal) { gFunctionWasApplied = true; });
+ MOZ_RELEASE_ASSERT(gFunctionWasApplied == true);
+
+ return true;
+}
+
+static int
+TimesTwo(const BasicValue& aValue)
+{
+ return aValue.GetTag() * 2;
+}
+
+static int
+TimesTwoAndResetOriginal(BasicValue& aValue)
+{
+ int tag = aValue.GetTag();
+ aValue.SetTag(1);
+ return tag * 2;
+}
+
+struct MultiplyTagFunctor
+{
+ MultiplyTagFunctor() : mBy(2) { }
+
+ int operator()(BasicValue& aValue)
+ {
+ return aValue.GetTag() * mBy.GetTag();
+ }
+
+ BasicValue mBy;
+};
+
+static bool
+TestMap()
+{
+ // Check that map handles the 'Nothing' case.
+ Maybe<BasicValue> mayValue;
+ MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwo) == Nothing());
+ static_assert(IsSame<Maybe<int>,
+ DECLTYPE(mayValue.map(&TimesTwo))>::value,
+ "map(TimesTwo) should return a Maybe<int>");
+ MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwoAndResetOriginal) == Nothing());
+
+ // Check that map handles the 'Some' case.
+ mayValue = Some(BasicValue(2));
+ MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwo) == Some(4));
+ MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwoAndResetOriginal) == Some(4));
+ MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1);
+ mayValue = Some(BasicValue(2));
+
+ // Check that map works with a const reference.
+ mayValue->SetTag(2);
+ const Maybe<BasicValue>& mayValueCRef = mayValue;
+ MOZ_RELEASE_ASSERT(mayValueCRef.map(&TimesTwo) == Some(4));
+ static_assert(IsSame<Maybe<int>,
+ DECLTYPE(mayValueCRef.map(&TimesTwo))>::value,
+ "map(TimesTwo) should return a Maybe<int>");
+
+ // Check that map works with functors.
+ MultiplyTagFunctor tagMultiplier;
+ MOZ_RELEASE_ASSERT(tagMultiplier.mBy.GetStatus() == eWasConstructed);
+ MOZ_RELEASE_ASSERT(mayValue.map(tagMultiplier) == Some(4));
+ MOZ_RELEASE_ASSERT(tagMultiplier.mBy.GetStatus() == eWasConstructed);
+
+ // Check that map works with lambda expressions.
+ int two = 2;
+ mayValue = Some(BasicValue(2));
+ Maybe<int> mappedValue =
+ mayValue.map([&](const BasicValue& aVal) {
+ return aVal.GetTag() * two;
+ });
+ MOZ_RELEASE_ASSERT(mappedValue == Some(4));
+ mappedValue =
+ mayValue.map([=](const BasicValue& aVal) {
+ return aVal.GetTag() * two;
+ });
+ MOZ_RELEASE_ASSERT(mappedValue == Some(4));
+ mappedValue =
+ mayValueCRef.map([&](const BasicValue& aVal) {
+ return aVal.GetTag() * two;
+ });
+ MOZ_RELEASE_ASSERT(mappedValue == Some(4));
+
+ return true;
+}
+
+static bool
+TestToMaybe()
+{
+ BasicValue value(1);
+ BasicValue* nullPointer = nullptr;
+
+ // Check that a non-null pointer translates into a Some value.
+ Maybe<BasicValue> mayValue = ToMaybe(&value);
+ static_assert(IsSame<Maybe<BasicValue>, DECLTYPE(ToMaybe(&value))>::value,
+ "ToMaybe should return a Maybe<BasicValue>");
+ MOZ_RELEASE_ASSERT(mayValue.isSome());
+ MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1);
+ MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasCopyConstructed);
+ MOZ_RELEASE_ASSERT(value.GetStatus() != eWasMovedFrom);
+
+ // Check that a null pointer translates into a Nothing value.
+ mayValue = ToMaybe(nullPointer);
+ static_assert(IsSame<Maybe<BasicValue>, DECLTYPE(ToMaybe(nullPointer))>::value,
+ "ToMaybe should return a Maybe<BasicValue>");
+ MOZ_RELEASE_ASSERT(mayValue.isNothing());
+
+ return true;
+}
+
+static bool
+TestComparisonOperators()
+{
+ Maybe<BasicValue> nothingValue = Nothing();
+ Maybe<BasicValue> anotherNothingValue = Nothing();
+ Maybe<BasicValue> oneValue = Some(BasicValue(1));
+ Maybe<BasicValue> anotherOneValue = Some(BasicValue(1));
+ Maybe<BasicValue> twoValue = Some(BasicValue(2));
+
+ // Check equality.
+ MOZ_RELEASE_ASSERT(nothingValue == anotherNothingValue);
+ MOZ_RELEASE_ASSERT(oneValue == anotherOneValue);
+
+ // Check inequality.
+ MOZ_RELEASE_ASSERT(nothingValue != oneValue);
+ MOZ_RELEASE_ASSERT(oneValue != nothingValue);
+ MOZ_RELEASE_ASSERT(oneValue != twoValue);
+
+ // Check '<'.
+ MOZ_RELEASE_ASSERT(nothingValue < oneValue);
+ MOZ_RELEASE_ASSERT(oneValue < twoValue);
+
+ // Check '<='.
+ MOZ_RELEASE_ASSERT(nothingValue <= anotherNothingValue);
+ MOZ_RELEASE_ASSERT(nothingValue <= oneValue);
+ MOZ_RELEASE_ASSERT(oneValue <= oneValue);
+ MOZ_RELEASE_ASSERT(oneValue <= twoValue);
+
+ // Check '>'.
+ MOZ_RELEASE_ASSERT(oneValue > nothingValue);
+ MOZ_RELEASE_ASSERT(twoValue > oneValue);
+
+ // Check '>='.
+ MOZ_RELEASE_ASSERT(nothingValue >= anotherNothingValue);
+ MOZ_RELEASE_ASSERT(oneValue >= nothingValue);
+ MOZ_RELEASE_ASSERT(oneValue >= oneValue);
+ MOZ_RELEASE_ASSERT(twoValue >= oneValue);
+
+ return true;
+}
+
+// Check that Maybe<> can wrap a superclass that happens to also be a concrete
+// class (i.e. that the compiler doesn't warn when we invoke the superclass's
+// destructor explicitly in |reset()|.
+class MySuperClass {
+ virtual void VirtualMethod() { /* do nothing */ }
+};
+
+class MyDerivedClass : public MySuperClass {
+ void VirtualMethod() override { /* do nothing */ }
+};
+
+static bool
+TestVirtualFunction() {
+ Maybe<MySuperClass> super;
+ super.emplace();
+ super.reset();
+
+ Maybe<MyDerivedClass> derived;
+ derived.emplace();
+ derived.reset();
+
+ // If this compiles successfully, we've passed.
+ return true;
+}
+
+static Maybe<int*>
+ReturnSomeNullptr()
+{
+ return Some(nullptr);
+}
+
+struct D
+{
+ explicit D(Maybe<int*>) {}
+};
+
+static bool
+TestSomeNullptrConversion()
+{
+ Maybe<int*> m1 = Some(nullptr);
+ MOZ_RELEASE_ASSERT(m1.isSome());
+ MOZ_RELEASE_ASSERT(m1);
+ MOZ_RELEASE_ASSERT(!*m1);
+
+ auto m2 = ReturnSomeNullptr();
+ MOZ_RELEASE_ASSERT(m2.isSome());
+ MOZ_RELEASE_ASSERT(m2);
+ MOZ_RELEASE_ASSERT(!*m2);
+
+ Maybe<decltype(nullptr)> m3 = Some(nullptr);
+ MOZ_RELEASE_ASSERT(m3.isSome());
+ MOZ_RELEASE_ASSERT(m3);
+ MOZ_RELEASE_ASSERT(*m3 == nullptr);
+
+ D d(Some(nullptr));
+
+ return true;
+}
+
+struct Base {};
+struct Derived : Base {};
+
+static Maybe<Base*>
+ReturnDerivedPointer()
+{
+ Derived* d = nullptr;
+ return Some(d);
+}
+
+struct ExplicitConstructorBasePointer
+{
+ explicit ExplicitConstructorBasePointer(Maybe<Base*>) {}
+};
+
+static bool
+TestSomePointerConversion()
+{
+ Base base;
+ Derived derived;
+
+ Maybe<Base*> m1 = Some(&derived);
+ MOZ_RELEASE_ASSERT(m1.isSome());
+ MOZ_RELEASE_ASSERT(m1);
+ MOZ_RELEASE_ASSERT(*m1 == &derived);
+
+ auto m2 = ReturnDerivedPointer();
+ MOZ_RELEASE_ASSERT(m2.isSome());
+ MOZ_RELEASE_ASSERT(m2);
+ MOZ_RELEASE_ASSERT(*m2 == nullptr);
+
+ Maybe<Base*> m3 = Some(&base);
+ MOZ_RELEASE_ASSERT(m3.isSome());
+ MOZ_RELEASE_ASSERT(m3);
+ MOZ_RELEASE_ASSERT(*m3 == &base);
+
+ auto s1 = Some(&derived);
+ Maybe<Base*> c1(s1);
+ MOZ_RELEASE_ASSERT(c1.isSome());
+ MOZ_RELEASE_ASSERT(c1);
+ MOZ_RELEASE_ASSERT(*c1 == &derived);
+
+ ExplicitConstructorBasePointer ecbp(Some(&derived));
+
+ return true;
+}
+
+int
+main()
+{
+ RUN_TEST(TestBasicFeatures);
+ RUN_TEST(TestCopyAndMove);
+ RUN_TEST(TestFunctionalAccessors);
+ RUN_TEST(TestApply);
+ RUN_TEST(TestMap);
+ RUN_TEST(TestToMaybe);
+ RUN_TEST(TestComparisonOperators);
+ RUN_TEST(TestVirtualFunction);
+ RUN_TEST(TestSomeNullptrConversion);
+ RUN_TEST(TestSomePointerConversion);
+
+ return 0;
+}
diff --git a/mfbt/tests/TestNotNull.cpp b/mfbt/tests/TestNotNull.cpp
new file mode 100644
index 000000000..4642dd93e
--- /dev/null
+++ b/mfbt/tests/TestNotNull.cpp
@@ -0,0 +1,314 @@
+/* -*- 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/NotNull.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+
+using mozilla::WrapNotNull;
+using mozilla::MakeUnique;
+using mozilla::NotNull;
+using mozilla::UniquePtr;
+
+#define CHECK MOZ_RELEASE_ASSERT
+
+class Blah
+{
+public:
+ Blah() : mX(0) {}
+ void blah() {};
+ int mX;
+};
+
+// A simple smart pointer that implicity converts to and from T*.
+template <typename T>
+class MyPtr
+{
+ T* mRawPtr;
+
+public:
+ MyPtr() : mRawPtr(nullptr) {}
+ MOZ_IMPLICIT MyPtr(T* aRawPtr) : mRawPtr(aRawPtr) {}
+
+ T* get() const { return mRawPtr; }
+ operator T*() const { return get(); }
+
+ T* operator->() const { return get(); }
+};
+
+// A simple class that works with RefPtr. It keeps track of the maximum
+// refcount value for testing purposes.
+class MyRefType
+{
+ int mExpectedMaxRefCnt;
+ int mMaxRefCnt;
+ int mRefCnt;
+public:
+ explicit MyRefType(int aExpectedMaxRefCnt)
+ : mExpectedMaxRefCnt(aExpectedMaxRefCnt)
+ , mMaxRefCnt(0)
+ , mRefCnt(0)
+ {}
+
+ ~MyRefType() {
+ CHECK(mMaxRefCnt == mExpectedMaxRefCnt);
+ }
+
+ uint32_t AddRef() {
+ mRefCnt++;
+ if (mRefCnt > mMaxRefCnt) {
+ mMaxRefCnt = mRefCnt;
+ }
+ return mRefCnt;
+ }
+
+ uint32_t Release() {
+ CHECK(mRefCnt > 0);
+ mRefCnt--;
+ if (mRefCnt == 0) {
+ delete this;
+ return 0;
+ }
+ return mRefCnt;
+ }
+};
+
+void f_i(int* aPtr) {}
+void f_my(MyPtr<int> aPtr) {}
+
+void f_nni(NotNull<int*> aPtr) {}
+void f_nnmy(NotNull<MyPtr<int>> aPtr) {}
+
+void
+TestNotNullWithMyPtr()
+{
+ int i4 = 4;
+ int i5 = 5;
+
+ MyPtr<int> my4 = &i4;
+ MyPtr<int> my5 = &i5;
+
+ NotNull<int*> nni4 = WrapNotNull(&i4);
+ NotNull<int*> nni5 = WrapNotNull(&i5);
+ NotNull<MyPtr<int>> nnmy4 = WrapNotNull(my4);
+
+ //WrapNotNull(nullptr); // no wrapping from nullptr
+ //WrapNotNull(0); // no wrapping from zero
+
+ // NotNull<int*> construction combinations
+ //NotNull<int*> nni4a; // no default
+ //NotNull<int*> nni4a(nullptr); // no nullptr
+ //NotNull<int*> nni4a(0); // no zero
+ //NotNull<int*> nni4a(&i4); // no int*
+ //NotNull<int*> nni4a(my4); // no MyPtr<int>
+ NotNull<int*> nni4b(WrapNotNull(&i4)); // WrapNotNull(int*)
+ NotNull<int*> nni4c(WrapNotNull(my4)); // WrapNotNull(MyPtr<int>)
+ NotNull<int*> nni4d(nni4); // NotNull<int*>
+ NotNull<int*> nni4e(nnmy4); // NotNull<MyPtr<int>>
+ CHECK(*nni4b == 4);
+ CHECK(*nni4c == 4);
+ CHECK(*nni4d == 4);
+ CHECK(*nni4e == 4);
+
+ // NotNull<MyPtr<int>> construction combinations
+ //NotNull<MyPtr<int>> nnmy4a; // no default
+ //NotNull<MyPtr<int>> nnmy4a(nullptr); // no nullptr
+ //NotNull<MyPtr<int>> nnmy4a(0); // no zero
+ //NotNull<MyPtr<int>> nnmy4a(&i4); // no int*
+ //NotNull<MyPtr<int>> nnmy4a(my4); // no MyPtr<int>
+ NotNull<MyPtr<int>> nnmy4b(WrapNotNull(&i4)); // WrapNotNull(int*)
+ NotNull<MyPtr<int>> nnmy4c(WrapNotNull(my4)); // WrapNotNull(MyPtr<int>)
+ NotNull<MyPtr<int>> nnmy4d(nni4); // NotNull<int*>
+ NotNull<MyPtr<int>> nnmy4e(nnmy4); // NotNull<MyPtr<int>>
+ CHECK(*nnmy4b == 4);
+ CHECK(*nnmy4c == 4);
+ CHECK(*nnmy4d == 4);
+ CHECK(*nnmy4e == 4);
+
+ // NotNull<int*> assignment combinations
+ //nni4b = nullptr; // no nullptr
+ //nni4b = 0; // no zero
+ //nni4a = &i4; // no int*
+ //nni4a = my4; // no MyPtr<int>
+ nni4b = WrapNotNull(&i4); // WrapNotNull(int*)
+ nni4c = WrapNotNull(my4); // WrapNotNull(MyPtr<int>)
+ nni4d = nni4; // NotNull<int*>
+ nni4e = nnmy4; // NotNull<MyPtr<int>>
+ CHECK(*nni4b == 4);
+ CHECK(*nni4c == 4);
+ CHECK(*nni4d == 4);
+ CHECK(*nni4e == 4);
+
+ // NotNull<MyPtr<int>> assignment combinations
+ //nnmy4a = nullptr; // no nullptr
+ //nnmy4a = 0; // no zero
+ //nnmy4a = &i4; // no int*
+ //nnmy4a = my4; // no MyPtr<int>
+ nnmy4b = WrapNotNull(&i4); // WrapNotNull(int*)
+ nnmy4c = WrapNotNull(my4); // WrapNotNull(MyPtr<int>)
+ nnmy4d = nni4; // NotNull<int*>
+ nnmy4e = nnmy4; // NotNull<MyPtr<int>>
+ CHECK(*nnmy4b == 4);
+ CHECK(*nnmy4c == 4);
+ CHECK(*nnmy4d == 4);
+ CHECK(*nnmy4e == 4);
+
+ NotNull<MyPtr<int>> nnmy5 = WrapNotNull(&i5);
+ CHECK(*nnmy5 == 5);
+ CHECK(nnmy5 == &i5); // NotNull<MyPtr<int>> == int*
+ CHECK(nnmy5 == my5); // NotNull<MyPtr<int>> == MyPtr<int>
+ CHECK(nnmy5 == nni5); // NotNull<MyPtr<int>> == NotNull<int*>
+ CHECK(nnmy5 == nnmy5); // NotNull<MyPtr<int>> == NotNull<MyPtr<int>>
+ CHECK(&i5 == nnmy5); // int* == NotNull<MyPtr<int>>
+ CHECK(my5 == nnmy5); // MyPtr<int> == NotNull<MyPtr<int>>
+ CHECK(nni5 == nnmy5); // NotNull<int*> == NotNull<MyPtr<int>>
+ CHECK(nnmy5 == nnmy5); // NotNull<MyPtr<int>> == NotNull<MyPtr<int>>
+ //CHECK(nni5 == nullptr); // no comparisons with nullptr
+ //CHECK(nullptr == nni5); // no comparisons with nullptr
+ //CHECK(nni5 == 0); // no comparisons with zero
+ //CHECK(0 == nni5); // no comparisons with zero
+
+ CHECK(*nnmy5 == 5);
+ CHECK(nnmy5 != &i4); // NotNull<MyPtr<int>> != int*
+ CHECK(nnmy5 != my4); // NotNull<MyPtr<int>> != MyPtr<int>
+ CHECK(nnmy5 != nni4); // NotNull<MyPtr<int>> != NotNull<int*>
+ CHECK(nnmy5 != nnmy4); // NotNull<MyPtr<int>> != NotNull<MyPtr<int>>
+ CHECK(&i4 != nnmy5); // int* != NotNull<MyPtr<int>>
+ CHECK(my4 != nnmy5); // MyPtr<int> != NotNull<MyPtr<int>>
+ CHECK(nni4 != nnmy5); // NotNull<int*> != NotNull<MyPtr<int>>
+ CHECK(nnmy4 != nnmy5); // NotNull<MyPtr<int>> != NotNull<MyPtr<int>>
+ //CHECK(nni4 != nullptr); // no comparisons with nullptr
+ //CHECK(nullptr != nni4); // no comparisons with nullptr
+ //CHECK(nni4 != 0); // no comparisons with zero
+ //CHECK(0 != nni4); // no comparisons with zero
+
+ // int* parameter
+ f_i(&i4); // identity int* --> int*
+ f_i(my4); // implicit MyPtr<int> --> int*
+ f_i(my4.get()); // explicit MyPtr<int> --> int*
+ f_i(nni4); // implicit NotNull<int*> --> int*
+ f_i(nni4.get()); // explicit NotNull<int*> --> int*
+ //f_i(nnmy4); // no implicit NotNull<MyPtr<int>> --> int*
+ f_i(nnmy4.get()); // explicit NotNull<MyPtr<int>> --> int*
+ f_i(nnmy4.get().get());// doubly-explicit NotNull<MyPtr<int>> --> int*
+
+ // MyPtr<int> parameter
+ f_my(&i4); // implicit int* --> MyPtr<int>
+ f_my(my4); // identity MyPtr<int> --> MyPtr<int>
+ f_my(my4.get()); // explicit MyPtr<int> --> MyPtr<int>
+ //f_my(nni4); // no implicit NotNull<int*> --> MyPtr<int>
+ f_my(nni4.get()); // explicit NotNull<int*> --> MyPtr<int>
+ f_my(nnmy4); // implicit NotNull<MyPtr<int>> --> MyPtr<int>
+ f_my(nnmy4.get()); // explicit NotNull<MyPtr<int>> --> MyPtr<int>
+ f_my(nnmy4.get().get());// doubly-explicit NotNull<MyPtr<int>> --> MyPtr<int>
+
+ // NotNull<int*> parameter
+ f_nni(nni4); // identity NotNull<int*> --> NotNull<int*>
+ f_nni(nnmy4); // implicit NotNull<MyPtr<int>> --> NotNull<int*>
+
+ // NotNull<MyPtr<int>> parameter
+ f_nnmy(nni4); // implicit NotNull<int*> --> NotNull<MyPtr<int>>
+ f_nnmy(nnmy4); // identity NotNull<MyPtr<int>> --> NotNull<MyPtr<int>>
+
+ //CHECK(nni4); // disallow boolean conversion / unary expression usage
+ //CHECK(nnmy4); // ditto
+
+ // '->' dereferencing.
+ Blah blah;
+ MyPtr<Blah> myblah = &blah;
+ NotNull<Blah*> nnblah = WrapNotNull(&blah);
+ NotNull<MyPtr<Blah>> nnmyblah = WrapNotNull(myblah);
+ (&blah)->blah(); // int*
+ myblah->blah(); // MyPtr<int>
+ nnblah->blah(); // NotNull<int*>
+ nnmyblah->blah(); // NotNull<MyPtr<int>>
+
+ (&blah)->mX = 1;
+ CHECK((&blah)->mX == 1);
+ myblah->mX = 2;
+ CHECK(myblah->mX == 2);
+ nnblah->mX = 3;
+ CHECK(nnblah->mX == 3);
+ nnmyblah->mX = 4;
+ CHECK(nnmyblah->mX == 4);
+
+ // '*' dereferencing (lvalues and rvalues)
+ *(&i4) = 7; // int*
+ CHECK(*(&i4) == 7);
+ *my4 = 6; // MyPtr<int>
+ CHECK(*my4 == 6);
+ *nni4 = 5; // NotNull<int*>
+ CHECK(*nni4 == 5);
+ *nnmy4 = 4; // NotNull<MyPtr<int>>
+ CHECK(*nnmy4 == 4);
+
+ // Non-null arrays.
+ static const int N = 20;
+ int a[N];
+ NotNull<int*> nna = WrapNotNull(a);
+ for (int i = 0; i < N; i++) {
+ nna[i] = i;
+ }
+ for (int i = 0; i < N; i++) {
+ nna[i] *= 2;
+ }
+ for (int i = 0; i < N; i++) {
+ CHECK(nna[i] == i * 2);
+ }
+}
+
+void f_ref(NotNull<MyRefType*> aR)
+{
+ NotNull<RefPtr<MyRefType>> r = aR;
+}
+
+void
+TestNotNullWithRefPtr()
+{
+ // This MyRefType object will have a maximum refcount of 5.
+ NotNull<RefPtr<MyRefType>> r1 = WrapNotNull(new MyRefType(5));
+
+ // At this point the refcount is 1.
+
+ NotNull<RefPtr<MyRefType>> r2 = r1;
+
+ // At this point the refcount is 2.
+
+ NotNull<MyRefType*> r3 = r2;
+ (void)r3;
+
+ // At this point the refcount is still 2.
+
+ RefPtr<MyRefType> r4 = r2;
+ mozilla::Unused << r4;
+
+ // At this point the refcount is 3.
+
+ RefPtr<MyRefType> r5 = r3.get();
+ mozilla::Unused << r5;
+
+ // At this point the refcount is 4.
+
+ // No change to the refcount occurs because of the argument passing. Within
+ // f_ref() the refcount temporarily hits 5, due to the local RefPtr.
+ f_ref(r2);
+
+ // At this point the refcount is 4.
+
+ // At function's end all RefPtrs are destroyed and the refcount drops to 0
+ // and the MyRefType is destroyed.
+}
+
+int
+main()
+{
+ TestNotNullWithMyPtr();
+ TestNotNullWithRefPtr();
+
+ return 0;
+}
+
diff --git a/mfbt/tests/TestPair.cpp b/mfbt/tests/TestPair.cpp
new file mode 100644
index 000000000..f1fa9c554
--- /dev/null
+++ b/mfbt/tests/TestPair.cpp
@@ -0,0 +1,83 @@
+/* -*- 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/Pair.h"
+#include "mozilla/TypeTraits.h"
+
+using mozilla::IsSame;
+using mozilla::MakePair;
+using mozilla::Pair;
+
+// Sizes aren't part of the guaranteed Pair interface, but we want to verify our
+// attempts at compactness through EBO are moderately functional, *somewhere*.
+#define INSTANTIATE(T1, T2, name, size) \
+ Pair<T1, T2> name##_1(T1(0), T2(0)); \
+ static_assert(sizeof(name##_1.first()) > 0, \
+ "first method should work on Pair<" #T1 ", " #T2 ">"); \
+ static_assert(sizeof(name##_1.second()) > 0, \
+ "second method should work on Pair<" #T1 ", " #T2 ">"); \
+ static_assert(sizeof(name##_1) == (size), \
+ "Pair<" #T1 ", " #T2 "> has an unexpected size"); \
+ Pair<T2, T1> name##_2(T2(0), T1(0)); \
+ static_assert(sizeof(name##_2.first()) > 0, \
+ "first method should work on Pair<" #T2 ", " #T1 ">"); \
+ static_assert(sizeof(name##_2.second()) > 0, \
+ "second method should work on Pair<" #T2 ", " #T1 ">"); \
+ static_assert(sizeof(name##_2) == (size), \
+ "Pair<" #T2 ", " #T1 "> has an unexpected size");
+
+INSTANTIATE(int, int, prim1, 2 * sizeof(int));
+INSTANTIATE(int, long, prim2, 2 * sizeof(long));
+
+struct EmptyClass { explicit EmptyClass(int) {} };
+struct NonEmpty { char mC; explicit NonEmpty(int) {} };
+
+INSTANTIATE(int, EmptyClass, both1, sizeof(int));
+INSTANTIATE(int, NonEmpty, both2, 2 * sizeof(int));
+INSTANTIATE(EmptyClass, NonEmpty, both3, 1);
+
+struct A { char dummy; explicit A(int) {} };
+struct B : A { explicit B(int aI) : A(aI) {} };
+
+INSTANTIATE(A, A, class1, 2);
+INSTANTIATE(A, B, class2, 2);
+INSTANTIATE(A, EmptyClass, class3, 1);
+
+struct OtherEmpty : EmptyClass { explicit OtherEmpty(int aI) : EmptyClass(aI) {} };
+
+// C++11 requires distinct objects of the same type, within the same "most
+// derived object", to have different addresses. Pair allocates its elements as
+// two bases, a base and a member, or two members. If the two elements have
+// non-zero size or are unrelated, no big deal. But if they're both empty and
+// related, something -- possibly both -- must be inflated. Exactly which are
+// inflated depends which PairHelper specialization is used. We could
+// potentially assert something about size for this case, but whatever we could
+// assert would be very finicky. Plus it's two empty classes -- hardly likely.
+// So don't bother trying to assert anything about this case.
+//INSTANTIATE(EmptyClass, OtherEmpty, class4, ...something finicky...);
+
+int
+main()
+{
+ A a(0);
+ B b(0);
+ const A constA(0);
+ const B constB(0);
+
+ // Check that MakePair generates Pair objects of the correct types.
+ static_assert(IsSame<decltype(MakePair(A(0), B(0))), Pair<A, B>>::value,
+ "MakePair should strip rvalue references");
+ static_assert(IsSame<decltype(MakePair(a, b)), Pair<A, B>>::value,
+ "MakePair should strip lvalue references");
+ static_assert(IsSame<decltype(MakePair(constA, constB)), Pair<A, B>>::value,
+ "MakePair should strip CV-qualifiers");
+
+ // Check that copy assignment and move assignment work.
+ a = constA;
+ a = A(0);
+
+ return 0;
+}
diff --git a/mfbt/tests/TestPoisonArea.cpp b/mfbt/tests/TestPoisonArea.cpp
new file mode 100644
index 000000000..6f1b61ed3
--- /dev/null
+++ b/mfbt/tests/TestPoisonArea.cpp
@@ -0,0 +1,549 @@
+/* -*- 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/.
+ */
+
+/* Code in this file needs to be kept in sync with code in nsPresArena.cpp.
+ *
+ * We want to use a fixed address for frame poisoning so that it is readily
+ * identifiable in crash dumps. Whether such an address is available
+ * without any special setup depends on the system configuration.
+ *
+ * All current 64-bit CPUs (with the possible exception of PowerPC64)
+ * reserve the vast majority of the virtual address space for future
+ * hardware extensions; valid addresses must be below some break point
+ * between 2**48 and 2**54, depending on exactly which chip you have. Some
+ * chips (notably amd64) also allow the use of the *highest* 2**48 -- 2**54
+ * addresses. Thus, if user space pointers are 64 bits wide, we can just
+ * use an address outside this range, and no more is required. To
+ * accommodate the chips that allow very high addresses to be valid, the
+ * value chosen is close to 2**63 (that is, in the middle of the space).
+ *
+ * In most cases, a purely 32-bit operating system must reserve some
+ * fraction of the address space for its own use. Contemporary 32-bit OSes
+ * tend to take the high gigabyte or so (0xC000_0000 on up). If we can
+ * prove that high addresses are reserved to the kernel, we can use an
+ * address in that region. Unfortunately, not all 32-bit OSes do this;
+ * OSX 10.4 might not, and it is unclear what mobile OSes are like
+ * (some 32-bit CPUs make it very easy for the kernel to exist in its own
+ * private address space).
+ *
+ * Furthermore, when a 32-bit user space process is running on a 64-bit
+ * kernel, the operating system has no need to reserve any of the space that
+ * the process can see, and generally does not do so. This is the scenario
+ * of greatest concern, since it covers all contemporary OSX iterations
+ * (10.5+) as well as Windows Vista and 7 on newer amd64 hardware. Linux on
+ * amd64 is generally run as a pure 64-bit environment, but its 32-bit
+ * compatibility mode also has this property.
+ *
+ * Thus, when user space pointers are 32 bits wide, we need to validate
+ * our chosen address, and possibly *make* it a good poison address by
+ * allocating a page around it and marking it inaccessible. The algorithm
+ * for this is:
+ *
+ * 1. Attempt to make the page surrounding the poison address a reserved,
+ * inaccessible memory region using OS primitives. On Windows, this is
+ * done with VirtualAlloc(MEM_RESERVE); on Unix, mmap(PROT_NONE).
+ *
+ * 2. If mmap/VirtualAlloc failed, there are two possible reasons: either
+ * the region is reserved to the kernel and no further action is
+ * required, or there is already usable memory in this area and we have
+ * to pick a different address. The tricky part is knowing which case
+ * we have, without attempting to access the region. On Windows, we
+ * rely on GetSystemInfo()'s reported upper and lower bounds of the
+ * application memory area. On Unix, there is nothing devoted to the
+ * purpose, but seeing if madvise() fails is close enough (it *might*
+ * disrupt someone else's use of the memory region, but not by as much
+ * as anything else available).
+ *
+ * Be aware of these gotchas:
+ *
+ * 1. We cannot use mmap() with MAP_FIXED. MAP_FIXED is defined to
+ * _replace_ any existing mapping in the region, if necessary to satisfy
+ * the request. Obviously, as we are blindly attempting to acquire a
+ * page at a constant address, we must not do this, lest we overwrite
+ * someone else's allocation.
+ *
+ * 2. For the same reason, we cannot blindly use mprotect() if mmap() fails.
+ *
+ * 3. madvise() may fail when applied to a 'magic' memory region provided as
+ * a kernel/user interface. Fortunately, the only such case I know about
+ * is the "vsyscall" area (not to be confused with the "vdso" area) for
+ * *64*-bit processes on Linux - and we don't even run this code for
+ * 64-bit processes.
+ *
+ * 4. VirtualQuery() does not produce any useful information if
+ * applied to kernel memory - in fact, it doesn't write its output
+ * at all. Thus, it is not used here.
+ */
+
+#include "mozilla/IntegerPrintfMacros.h"
+
+// MAP_ANON(YMOUS) is not in any standard. Add defines as necessary.
+#define _GNU_SOURCE 1
+#define _DARWIN_C_SOURCE 1
+
+#include <stddef.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <sys/types.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <sys/mman.h>
+#ifndef MAP_ANON
+#ifdef MAP_ANONYMOUS
+#define MAP_ANON MAP_ANONYMOUS
+#else
+#error "Don't know how to get anonymous memory"
+#endif
+#endif
+#endif
+
+#define SIZxPTR ((int)(sizeof(uintptr_t)*2))
+
+/* This program assumes that a whole number of return instructions fit into
+ * 32 bits, and that 32-bit alignment is sufficient for a branch destination.
+ * For architectures where this is not true, fiddling with RETURN_INSTR_TYPE
+ * can be enough.
+ */
+
+#if defined __i386__ || defined __x86_64__ || \
+ defined __i386 || defined __x86_64 || \
+ defined _M_IX86 || defined _M_AMD64
+#define RETURN_INSTR 0xC3C3C3C3 /* ret; ret; ret; ret */
+
+#elif defined __arm__ || defined _M_ARM
+#define RETURN_INSTR 0xE12FFF1E /* bx lr */
+
+// PPC has its own style of CPU-id #defines. There is no Windows for
+// PPC as far as I know, so no _M_ variant.
+#elif defined _ARCH_PPC || defined _ARCH_PWR || defined _ARCH_PWR2
+#define RETURN_INSTR 0x4E800020 /* blr */
+
+#elif defined __sparc || defined __sparcv9
+#define RETURN_INSTR 0x81c3e008 /* retl */
+
+#elif defined __alpha
+#define RETURN_INSTR 0x6bfa8001 /* ret */
+
+#elif defined __hppa
+#define RETURN_INSTR 0xe840c002 /* bv,n r0(rp) */
+
+#elif defined __mips
+#define RETURN_INSTR 0x03e00008 /* jr ra */
+
+#ifdef __MIPSEL
+/* On mipsel, jr ra needs to be followed by a nop.
+ 0x03e00008 as a 64 bits integer just does that */
+#define RETURN_INSTR_TYPE uint64_t
+#endif
+
+#elif defined __s390__
+#define RETURN_INSTR 0x07fe0000 /* br %r14 */
+
+#elif defined __aarch64__
+#define RETURN_INSTR 0xd65f03c0 /* ret */
+
+#elif defined __ia64
+struct ia64_instr { uint32_t mI[4]; };
+static const ia64_instr _return_instr =
+ {{ 0x00000011, 0x00000001, 0x80000200, 0x00840008 }}; /* br.ret.sptk.many b0 */
+
+#define RETURN_INSTR _return_instr
+#define RETURN_INSTR_TYPE ia64_instr
+
+#else
+#error "Need return instruction for this architecture"
+#endif
+
+#ifndef RETURN_INSTR_TYPE
+#define RETURN_INSTR_TYPE uint32_t
+#endif
+
+// Miscellaneous Windows/Unix portability gumph
+
+#ifdef _WIN32
+// Uses of this function deliberately leak the string.
+static LPSTR
+StrW32Error(DWORD aErrcode)
+{
+ LPSTR errmsg;
+ FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr, aErrcode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPSTR)&errmsg, 0, nullptr);
+
+ // FormatMessage puts an unwanted newline at the end of the string
+ size_t n = strlen(errmsg)-1;
+ while (errmsg[n] == '\r' || errmsg[n] == '\n') {
+ n--;
+ }
+ errmsg[n+1] = '\0';
+ return errmsg;
+}
+#define LastErrMsg() (StrW32Error(GetLastError()))
+
+// Because we use VirtualAlloc in MEM_RESERVE mode, the "page size" we want
+// is the allocation granularity.
+static SYSTEM_INFO sInfo_;
+
+static inline uint32_t
+PageSize()
+{
+ return sInfo_.dwAllocationGranularity;
+}
+
+static void*
+ReserveRegion(uintptr_t aRequest, bool aAccessible)
+{
+ return VirtualAlloc((void*)aRequest, PageSize(),
+ aAccessible ? MEM_RESERVE|MEM_COMMIT : MEM_RESERVE,
+ aAccessible ? PAGE_EXECUTE_READWRITE : PAGE_NOACCESS);
+}
+
+static void
+ReleaseRegion(void* aPage)
+{
+ VirtualFree(aPage, PageSize(), MEM_RELEASE);
+}
+
+static bool
+ProbeRegion(uintptr_t aPage)
+{
+ return aPage >= (uintptr_t)sInfo_.lpMaximumApplicationAddress &&
+ aPage + PageSize() >= (uintptr_t)sInfo_.lpMaximumApplicationAddress;
+}
+
+static bool
+MakeRegionExecutable(void*)
+{
+ return false;
+}
+
+#undef MAP_FAILED
+#define MAP_FAILED 0
+
+#else // Unix
+
+#define LastErrMsg() (strerror(errno))
+
+static unsigned long gUnixPageSize;
+
+static inline unsigned long
+PageSize()
+{
+ return gUnixPageSize;
+}
+
+static void*
+ReserveRegion(uintptr_t aRequest, bool aAccessible)
+{
+ return mmap(reinterpret_cast<void*>(aRequest), PageSize(),
+ aAccessible ? PROT_READ|PROT_WRITE : PROT_NONE,
+ MAP_PRIVATE|MAP_ANON, -1, 0);
+}
+
+static void
+ReleaseRegion(void* aPage)
+{
+ munmap(aPage, PageSize());
+}
+
+static bool
+ProbeRegion(uintptr_t aPage)
+{
+ return !!madvise(reinterpret_cast<void*>(aPage), PageSize(), MADV_NORMAL);
+}
+
+static int
+MakeRegionExecutable(void* aPage)
+{
+ return mprotect((caddr_t)aPage, PageSize(), PROT_READ|PROT_WRITE|PROT_EXEC);
+}
+
+#endif
+
+static uintptr_t
+ReservePoisonArea()
+{
+ if (sizeof(uintptr_t) == 8) {
+ // Use the hardware-inaccessible region.
+ // We have to avoid 64-bit constants and shifts by 32 bits, since this
+ // code is compiled in 32-bit mode, although it is never executed there.
+ uintptr_t result = (((uintptr_t(0x7FFFFFFFu) << 31) << 1 |
+ uintptr_t(0xF0DEAFFFu)) &
+ ~uintptr_t(PageSize()-1));
+ printf("INFO | poison area assumed at 0x%.*" PRIxPTR "\n", SIZxPTR, result);
+ return result;
+ }
+
+ // First see if we can allocate the preferred poison address from the OS.
+ uintptr_t candidate = (0xF0DEAFFF & ~(PageSize() - 1));
+ void* result = ReserveRegion(candidate, false);
+ if (result == reinterpret_cast<void*>(candidate)) {
+ // success - inaccessible page allocated
+ printf("INFO | poison area allocated at 0x%.*" PRIxPTR
+ " (preferred addr)\n", SIZxPTR, reinterpret_cast<uintptr_t>(result));
+ return candidate;
+ }
+
+ // That didn't work, so see if the preferred address is within a range
+ // of permanently inacessible memory.
+ if (ProbeRegion(candidate)) {
+ // success - selected page cannot be usable memory
+ if (result != MAP_FAILED) {
+ ReleaseRegion(result);
+ }
+ printf("INFO | poison area assumed at 0x%.*" PRIxPTR
+ " (preferred addr)\n", SIZxPTR, candidate);
+ return candidate;
+ }
+
+ // The preferred address is already in use. Did the OS give us a
+ // consolation prize?
+ if (result != MAP_FAILED) {
+ uintptr_t ures = reinterpret_cast<uintptr_t>(result);
+ printf("INFO | poison area allocated at 0x%.*" PRIxPTR
+ " (consolation prize)\n", SIZxPTR, ures);
+ return ures;
+ }
+
+ // It didn't, so try to allocate again, without any constraint on
+ // the address.
+ result = ReserveRegion(0, false);
+ if (result != MAP_FAILED) {
+ uintptr_t ures = reinterpret_cast<uintptr_t>(result);
+ printf("INFO | poison area allocated at 0x%.*" PRIxPTR
+ " (fallback)\n", SIZxPTR, ures);
+ return ures;
+ }
+
+ printf("ERROR | no usable poison area found\n");
+ return 0;
+}
+
+/* The "positive control" area confirms that we can allocate a page with the
+ * proper characteristics.
+ */
+static uintptr_t
+ReservePositiveControl()
+{
+
+ void* result = ReserveRegion(0, false);
+ if (result == MAP_FAILED) {
+ printf("ERROR | allocating positive control | %s\n", LastErrMsg());
+ return 0;
+ }
+ printf("INFO | positive control allocated at 0x%.*" PRIxPTR "\n",
+ SIZxPTR, (uintptr_t)result);
+ return (uintptr_t)result;
+}
+
+/* The "negative control" area confirms that our probe logic does detect a
+ * page that is readable, writable, or executable.
+ */
+static uintptr_t
+ReserveNegativeControl()
+{
+ void* result = ReserveRegion(0, true);
+ if (result == MAP_FAILED) {
+ printf("ERROR | allocating negative control | %s\n", LastErrMsg());
+ return 0;
+ }
+
+ // Fill the page with return instructions.
+ RETURN_INSTR_TYPE* p = reinterpret_cast<RETURN_INSTR_TYPE*>(result);
+ RETURN_INSTR_TYPE* limit =
+ reinterpret_cast<RETURN_INSTR_TYPE*>(
+ reinterpret_cast<char*>(result) + PageSize());
+ while (p < limit) {
+ *p++ = RETURN_INSTR;
+ }
+
+ // Now mark it executable as well as readable and writable.
+ // (mmap(PROT_EXEC) may fail when applied to anonymous memory.)
+
+ if (MakeRegionExecutable(result)) {
+ printf("ERROR | making negative control executable | %s\n", LastErrMsg());
+ return 0;
+ }
+
+ printf("INFO | negative control allocated at 0x%.*" PRIxPTR "\n",
+ SIZxPTR, (uintptr_t)result);
+ return (uintptr_t)result;
+}
+
+static void
+JumpTo(uintptr_t aOpaddr)
+{
+#ifdef __ia64
+ struct func_call
+ {
+ uintptr_t mFunc;
+ uintptr_t mGp;
+ } call = { aOpaddr, };
+ ((void (*)())&call)();
+#else
+ ((void (*)())aOpaddr)();
+#endif
+}
+
+#ifdef _WIN32
+static BOOL
+IsBadExecPtr(uintptr_t aPtr)
+{
+ BOOL ret = false;
+
+#ifdef _MSC_VER
+ __try {
+ JumpTo(aPtr);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ ret = true;
+ }
+#else
+ printf("INFO | exec test not supported on MinGW build\n");
+ // We do our best
+ ret = IsBadReadPtr((const void*)aPtr, 1);
+#endif
+ return ret;
+}
+#endif
+
+/* Test each page. */
+static bool
+TestPage(const char* aPageLabel, uintptr_t aPageAddr, int aShouldSucceed)
+{
+ const char* oplabel;
+ uintptr_t opaddr;
+
+ bool failed = false;
+ for (unsigned int test = 0; test < 3; test++) {
+ switch (test) {
+ // The execute test must be done before the write test, because the
+ // write test will clobber memory at the target address.
+ case 0: oplabel = "reading"; opaddr = aPageAddr + PageSize()/2 - 1; break;
+ case 1: oplabel = "executing"; opaddr = aPageAddr + PageSize()/2; break;
+ case 2: oplabel = "writing"; opaddr = aPageAddr + PageSize()/2 - 1; break;
+ default: abort();
+ }
+
+#ifdef _WIN32
+ BOOL badptr;
+
+ switch (test) {
+ case 0: badptr = IsBadReadPtr((const void*)opaddr, 1); break;
+ case 1: badptr = IsBadExecPtr(opaddr); break;
+ case 2: badptr = IsBadWritePtr((void*)opaddr, 1); break;
+ default: abort();
+ }
+
+ if (badptr) {
+ if (aShouldSucceed) {
+ printf("TEST-UNEXPECTED-FAIL | %s %s\n", oplabel, aPageLabel);
+ failed = true;
+ } else {
+ printf("TEST-PASS | %s %s\n", oplabel, aPageLabel);
+ }
+ } else {
+ // if control reaches this point the probe succeeded
+ if (aShouldSucceed) {
+ printf("TEST-PASS | %s %s\n", oplabel, aPageLabel);
+ } else {
+ printf("TEST-UNEXPECTED-FAIL | %s %s\n", oplabel, aPageLabel);
+ failed = true;
+ }
+ }
+#else
+ pid_t pid = fork();
+ if (pid == -1) {
+ printf("ERROR | %s %s | fork=%s\n", oplabel, aPageLabel,
+ LastErrMsg());
+ exit(2);
+ } else if (pid == 0) {
+ volatile unsigned char scratch;
+ switch (test) {
+ case 0: scratch = *(volatile unsigned char*)opaddr; break;
+ case 1: JumpTo(opaddr); break;
+ case 2: *(volatile unsigned char*)opaddr = 0; break;
+ default: abort();
+ }
+ (void)scratch;
+ _exit(0);
+ } else {
+ int status;
+ if (waitpid(pid, &status, 0) != pid) {
+ printf("ERROR | %s %s | wait=%s\n", oplabel, aPageLabel,
+ LastErrMsg());
+ exit(2);
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+ if (aShouldSucceed) {
+ printf("TEST-PASS | %s %s\n", oplabel, aPageLabel);
+ } else {
+ printf("TEST-UNEXPECTED-FAIL | %s %s | unexpected successful exit\n",
+ oplabel, aPageLabel);
+ failed = true;
+ }
+ } else if (WIFEXITED(status)) {
+ printf("ERROR | %s %s | unexpected exit code %d\n",
+ oplabel, aPageLabel, WEXITSTATUS(status));
+ exit(2);
+ } else if (WIFSIGNALED(status)) {
+ if (aShouldSucceed) {
+ printf("TEST-UNEXPECTED-FAIL | %s %s | unexpected signal %d\n",
+ oplabel, aPageLabel, WTERMSIG(status));
+ failed = true;
+ } else {
+ printf("TEST-PASS | %s %s | signal %d (as expected)\n",
+ oplabel, aPageLabel, WTERMSIG(status));
+ }
+ } else {
+ printf("ERROR | %s %s | unexpected exit status %d\n",
+ oplabel, aPageLabel, status);
+ exit(2);
+ }
+ }
+#endif
+ }
+ return failed;
+}
+
+int
+main()
+{
+#ifdef _WIN32
+ GetSystemInfo(&sInfo_);
+#else
+ gUnixPageSize = sysconf(_SC_PAGESIZE);
+#endif
+
+ uintptr_t ncontrol = ReserveNegativeControl();
+ uintptr_t pcontrol = ReservePositiveControl();
+ uintptr_t poison = ReservePoisonArea();
+
+ if (!ncontrol || !pcontrol || !poison) {
+ return 2;
+ }
+
+ bool failed = false;
+ failed |= TestPage("negative control", ncontrol, 1);
+ failed |= TestPage("positive control", pcontrol, 0);
+ failed |= TestPage("poison area", poison, 0);
+
+ return failed ? 1 : 0;
+}
diff --git a/mfbt/tests/TestRange.cpp b/mfbt/tests/TestRange.cpp
new file mode 100644
index 000000000..419c1cc26
--- /dev/null
+++ b/mfbt/tests/TestRange.cpp
@@ -0,0 +1,23 @@
+/* -*- 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/Range.h"
+#include "mozilla/TypeTraits.h"
+
+using mozilla::IsConvertible;
+using mozilla::Range;
+
+static_assert(IsConvertible<Range<int>, Range<const int>>::value,
+ "Range should convert into const");
+static_assert(!IsConvertible<Range<const int>, Range<int>>::value,
+ "Range should not drop const in conversion");
+
+// We need a proper program so we have someplace to hang the static_asserts.
+int
+main()
+{
+ return 0;
+}
diff --git a/mfbt/tests/TestRefPtr.cpp b/mfbt/tests/TestRefPtr.cpp
new file mode 100644
index 000000000..4d9e712bd
--- /dev/null
+++ b/mfbt/tests/TestRefPtr.cpp
@@ -0,0 +1,139 @@
+/* -*- 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/RefPtr.h"
+#include "mozilla/RefCounted.h"
+
+using mozilla::RefCounted;
+
+class Foo : public RefCounted<Foo>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(Foo)
+
+ Foo() : mDead(false) {}
+
+ static int sNumDestroyed;
+
+ ~Foo()
+ {
+ MOZ_ASSERT(!mDead);
+ mDead = true;
+ sNumDestroyed++;
+ }
+
+private:
+ bool mDead;
+};
+int Foo::sNumDestroyed;
+
+struct Bar : public Foo {};
+
+already_AddRefed<Foo>
+NewFoo()
+{
+ RefPtr<Foo> f(new Foo());
+ return f.forget();
+}
+
+already_AddRefed<Foo>
+NewBar()
+{
+ RefPtr<Bar> bar = new Bar();
+ return bar.forget();
+}
+
+void
+GetNewFoo(Foo** aFoo)
+{
+ *aFoo = new Bar();
+ // Kids, don't try this at home
+ (*aFoo)->AddRef();
+}
+
+void
+GetNewFoo(RefPtr<Foo>* aFoo)
+{
+ *aFoo = new Bar();
+}
+
+already_AddRefed<Foo>
+GetNullFoo()
+{
+ return 0;
+}
+
+int
+main()
+{
+ MOZ_RELEASE_ASSERT(0 == Foo::sNumDestroyed);
+ {
+ RefPtr<Foo> f = new Foo();
+ MOZ_RELEASE_ASSERT(f->refCount() == 1);
+ }
+ MOZ_RELEASE_ASSERT(1 == Foo::sNumDestroyed);
+
+ {
+ RefPtr<Foo> f1 = NewFoo();
+ RefPtr<Foo> f2(NewFoo());
+ MOZ_RELEASE_ASSERT(1 == Foo::sNumDestroyed);
+ }
+ MOZ_RELEASE_ASSERT(3 == Foo::sNumDestroyed);
+
+ {
+ RefPtr<Foo> b = NewBar();
+ MOZ_RELEASE_ASSERT(3 == Foo::sNumDestroyed);
+ }
+ MOZ_RELEASE_ASSERT(4 == Foo::sNumDestroyed);
+
+ {
+ RefPtr<Foo> f1;
+ {
+ f1 = new Foo();
+ RefPtr<Foo> f2(f1);
+ RefPtr<Foo> f3 = f2;
+ MOZ_RELEASE_ASSERT(4 == Foo::sNumDestroyed);
+ }
+ MOZ_RELEASE_ASSERT(4 == Foo::sNumDestroyed);
+ }
+ MOZ_RELEASE_ASSERT(5 == Foo::sNumDestroyed);
+
+ {
+ {
+ RefPtr<Foo> f = new Foo();
+ RefPtr<Foo> g = f.forget();
+ }
+ MOZ_RELEASE_ASSERT(6 == Foo::sNumDestroyed);
+ }
+
+ {
+ RefPtr<Foo> f = new Foo();
+ GetNewFoo(getter_AddRefs(f));
+ MOZ_RELEASE_ASSERT(7 == Foo::sNumDestroyed);
+ }
+ MOZ_RELEASE_ASSERT(8 == Foo::sNumDestroyed);
+
+ {
+ RefPtr<Foo> f = new Foo();
+ GetNewFoo(&f);
+ MOZ_RELEASE_ASSERT(9 == Foo::sNumDestroyed);
+ }
+ MOZ_RELEASE_ASSERT(10 == Foo::sNumDestroyed);
+
+ {
+ RefPtr<Foo> f1 = new Bar();
+ }
+ MOZ_RELEASE_ASSERT(11 == Foo::sNumDestroyed);
+
+ {
+ RefPtr<Foo> f = GetNullFoo();
+ MOZ_RELEASE_ASSERT(11 == Foo::sNumDestroyed);
+ }
+ MOZ_RELEASE_ASSERT(11 == Foo::sNumDestroyed);
+
+ return 0;
+}
+
diff --git a/mfbt/tests/TestRollingMean.cpp b/mfbt/tests/TestRollingMean.cpp
new file mode 100644
index 000000000..bec77786f
--- /dev/null
+++ b/mfbt/tests/TestRollingMean.cpp
@@ -0,0 +1,129 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/RollingMean.h"
+
+using mozilla::RollingMean;
+
+class MyClass
+{
+public:
+ uint32_t mValue;
+
+ explicit MyClass(uint32_t aValue = 0) : mValue(aValue) { }
+
+ bool operator==(const MyClass& aOther) const
+ {
+ return mValue == aOther.mValue;
+ }
+
+ MyClass operator+(const MyClass& aOther) const
+ {
+ return MyClass(mValue + aOther.mValue);
+ }
+
+ MyClass operator-(const MyClass& aOther) const
+ {
+ return MyClass(mValue - aOther.mValue);
+ }
+
+ MyClass operator/(uint32_t aDiv) const
+ {
+ return MyClass(mValue / aDiv);
+ }
+};
+
+class RollingMeanSuite
+{
+public:
+ RollingMeanSuite() { }
+
+ void runTests() {
+ testZero();
+ testClear();
+ testRolling();
+ testClass();
+ testMove();
+ }
+
+private:
+ void testZero()
+ {
+ RollingMean<uint32_t, uint64_t> mean(3);
+ MOZ_RELEASE_ASSERT(mean.empty());
+ }
+
+ void testClear()
+ {
+ RollingMean<uint32_t, uint64_t> mean(3);
+
+ mean.insert(4);
+ MOZ_RELEASE_ASSERT(mean.mean() == 4);
+
+ mean.clear();
+ MOZ_RELEASE_ASSERT(mean.empty());
+
+ mean.insert(3);
+ MOZ_RELEASE_ASSERT(mean.mean() == 3);
+ }
+
+ void testRolling()
+ {
+ RollingMean<uint32_t, uint64_t> mean(3);
+
+ mean.insert(10);
+ MOZ_RELEASE_ASSERT(mean.mean() == 10);
+
+ mean.insert(20);
+ MOZ_RELEASE_ASSERT(mean.mean() == 15);
+
+ mean.insert(35);
+ MOZ_RELEASE_ASSERT(mean.mean() == 21);
+
+ mean.insert(5);
+ MOZ_RELEASE_ASSERT(mean.mean() == 20);
+
+ mean.insert(10);
+ MOZ_RELEASE_ASSERT(mean.mean() == 16);
+ }
+
+ void testClass()
+ {
+ RollingMean<MyClass, MyClass> mean(3);
+
+ mean.insert(MyClass(4));
+ MOZ_RELEASE_ASSERT(mean.mean() == MyClass(4));
+
+ mean.clear();
+ MOZ_RELEASE_ASSERT(mean.empty());
+ }
+
+ void testMove()
+ {
+ RollingMean<uint32_t, uint64_t> mean(3);
+ mean = RollingMean<uint32_t, uint64_t>(4);
+ MOZ_RELEASE_ASSERT(mean.maxValues() == 4);
+
+ mean.insert(10);
+ MOZ_RELEASE_ASSERT(mean.mean() == 10);
+
+ mean = RollingMean<uint32_t, uint64_t>(3);
+ mean.insert(30);
+ mean.insert(40);
+ mean.insert(50);
+ mean.insert(60);
+ MOZ_RELEASE_ASSERT(mean.mean() == 50);
+ }
+};
+
+int
+main()
+{
+ RollingMeanSuite suite;
+ suite.runTests();
+ return 0;
+}
diff --git a/mfbt/tests/TestSHA1.cpp b/mfbt/tests/TestSHA1.cpp
new file mode 100644
index 000000000..fd64df1ca
--- /dev/null
+++ b/mfbt/tests/TestSHA1.cpp
@@ -0,0 +1,208 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/SHA1.h"
+
+using mozilla::SHA1Sum;
+
+static unsigned int gTestV[1024] = {
+ 0x048edc1a, 0x4345588c, 0x0ef03cbf, 0x1d6438f5, 0x094e0a1e, 0x68535f60,
+ 0x14e8c927, 0x60190043, 0x5d640ab7, 0x73dc7c62, 0x364223f9, 0x47320292,
+ 0x3924cae0, 0x5f6b26d3, 0x5efa04ef, 0x7aab361e, 0x2773b1aa, 0x1631b07d,
+ 0x385b5dd1, 0x26c809b0, 0x28ad3a9f, 0x0315292a, 0x1a544e67, 0x1e79dcb9,
+ 0x787683e8, 0x3a591c75, 0x1dd338c7, 0x01c539e5, 0x1c15b23e, 0x0697c25c,
+ 0x4df5fd45, 0x672aa324, 0x39f74e6e, 0x269cdd5f, 0x087b6fce, 0x293509db,
+ 0x0aef54a9, 0x210c4cc5, 0x29d6dc4a, 0x16320825, 0x3ab7b181, 0x56d6fd25,
+ 0x6837fda2, 0x3e7994c2, 0x37f77529, 0x48c85472, 0x424fd84d, 0x00aba7fa,
+ 0x6d8475de, 0x354634a7, 0x0c73bb49, 0x0a335de6, 0x0a9ea542, 0x5ffb31f1,
+ 0x00a6a3f2, 0x76b14a03, 0x1e436a37, 0x173b766a, 0x33cf3ca0, 0x34eb0f1a,
+ 0x4ca073ee, 0x27591fe6, 0x5eaf3356, 0x10c24493, 0x1bad88b6, 0x676f2309,
+ 0x7f5e2d91, 0x74bd4c83, 0x66549b43, 0x52ffdf24, 0x2dfa0a83, 0x7c3e1cbf,
+ 0x1edf87fc, 0x1f6fa930, 0x7c29bc74, 0x374bcd2f, 0x5b43de94, 0x0d09a3a6,
+ 0x7437ecb0, 0x635117f8, 0x2aa78f65, 0x2c788958, 0x098cb9f3, 0x13ed5b3f,
+ 0x41b7c7ba, 0x696b2d88, 0x42e20d63, 0x69585b1d, 0x4a9b027c, 0x0c761cba,
+ 0x563bdbc4, 0x3bde2f5b, 0x0bab9730, 0x7740104c, 0x11641702, 0x26f03c32,
+ 0x011a87c6, 0x2c5e4e6c, 0x46c34200, 0x6a167e84, 0x34205728, 0x0e8a6152,
+ 0x0014604b, 0x6793bacd, 0x442bca9c, 0x6f2018ce, 0x4313e07e, 0x77f2c69c,
+ 0x62621441, 0x47bf6358, 0x59c45e04, 0x16ba3426, 0x6ac0c19d, 0x20218c6b,
+ 0x510b4ddc, 0x585f6c9d, 0x1ed02b0c, 0x366bf0a9, 0x131c7f59, 0x0ebcd320,
+ 0x00ca858a, 0x5efbcb77, 0x2a7a1859, 0x64bb5afd, 0x76258886, 0x6505c895,
+ 0x602cfa32, 0x17040942, 0x783df744, 0x3838e0ae, 0x6a021e39, 0x4c8c9c5a,
+ 0x4a5e96b6, 0x10f4477d, 0x247fda4f, 0x4c390400, 0x0cbe048c, 0x7b547d26,
+ 0x1e2e6897, 0x4ba7e01b, 0x5cfea1bb, 0x39a2d199, 0x45aee64a, 0x12615500,
+ 0x0151615f, 0x1a9f5d33, 0x4542ed44, 0x101357eb, 0x35a16b1f, 0x3420b3e1,
+ 0x6442bac7, 0x1c0f2a8c, 0x68d642f1, 0x45744fc4, 0x048e60cb, 0x5f217f44,
+ 0x6cc7d151, 0x27f41984, 0x2d01eb09, 0x2bb15aea, 0x6dda49f8, 0x590dd6bc,
+ 0x280cc20b, 0x7e2592b5, 0x043642f0, 0x292b5d29, 0x2e0a9b69, 0x41162471,
+ 0x1e55db6b, 0x648b96fe, 0x05f8f9d1, 0x4a9d4cbb, 0x38517039, 0x2b0f8917,
+ 0x4d1e67bb, 0x713e0974, 0x64fdf214, 0x11223963, 0x2bd09d24, 0x19924092,
+ 0x4b4a70f0, 0x1ece6b03, 0x1780c9c1, 0x09b4c3ac, 0x58ac7e73, 0x5c9a4747,
+ 0x321f943b, 0x41167667, 0x3a19cf8c, 0x53f4144d, 0x03a498de, 0x6fb4b742,
+ 0x54d793cb, 0x7ee164e2, 0x501af74c, 0x43201e7f, 0x0ad581be, 0x497f046a,
+ 0x3b1d2a9f, 0x53b88eb0, 0x2c3a26c5, 0x5ae970ba, 0x7d7ee4ff, 0x471366c5,
+ 0x46119703, 0x3bfc2e58, 0x456d6c4f, 0x4b6bb181, 0x45d7c872, 0x0d023221,
+ 0x021176d1, 0x4195ad44, 0x4621ec90, 0x3ae68279, 0x57952f71, 0x1796080c,
+ 0x228077bb, 0x5e2b7fee, 0x3d71dd88, 0x4a651849, 0x7f1c8081, 0x04c333fc,
+ 0x1f99bff6, 0x11b7754c, 0x740be324, 0x069bf2e2, 0x0802f3e0, 0x371cf30e,
+ 0x1d44dda5, 0x6033b9e5, 0x5639a9b0, 0x6526bfff, 0x14d7d9b7, 0x4182b6a7,
+ 0x01a5fa76, 0x7aa5e581, 0x762465e6, 0x386b3a2e, 0x495a3ab0, 0x04421b2e,
+ 0x46e04591, 0x472af458, 0x6a007dd3, 0x2e8be484, 0x18660abe, 0x7969af82,
+ 0x5a242a83, 0x581b5f72, 0x5f0eff6d, 0x38aea98c, 0x2acb5853, 0x6d650b35,
+ 0x10b750d7, 0x18fdcd14, 0x09b4816c, 0x3ceef016, 0x6957153c, 0x27cf39fb,
+ 0x60e3495d, 0x381e1da6, 0x4b5be02d, 0x14b6f309, 0x6380c589, 0x1a31f436,
+ 0x4b5e50c1, 0x493ac048, 0x314baad1, 0x71e24ab7, 0x718af49c, 0x022f4658,
+ 0x1a419d5b, 0x1854610d, 0x2ec4e99a, 0x7096ce50, 0x5467ba00, 0x404aab4c,
+ 0x1a5ab015, 0x217580f7, 0x2d50071e, 0x71a9f437, 0x27f758b5, 0x11cd8b3f,
+ 0x63b089c9, 0x53c860c1, 0x2fa6b7d7, 0x61e54771, 0x5c0ba6b9, 0x3138f796,
+ 0x5c7359cd, 0x4c2c5654, 0x549d581c, 0x3129ebf7, 0x4958a248, 0x1a460541,
+ 0x68e64964, 0x597c0609, 0x57afcbab, 0x2f1c6479, 0x57a0ad5c, 0x5936938f,
+ 0x536a5cbe, 0x29aacf0b, 0x43eca70d, 0x6e7a3e4e, 0x563c1e3b, 0x32f23909,
+ 0x12faa42d, 0x28b0bbde, 0x797e2842, 0x1b827bdf, 0x0df96a6e, 0x542ef7f4,
+ 0x6226d368, 0x01cb4258, 0x77bcba08, 0x7e6dc041, 0x0571eda3, 0x0fdf5065,
+ 0x5c9b9f7a, 0x2b496dd6, 0x02d3b40b, 0x3a5752db, 0x4843a293, 0x6fdc9c3f,
+ 0x42963996, 0x39c9e4eb, 0x01db58ad, 0x7e79381c, 0x5bb207bb, 0x2df5de51,
+ 0x1549ec82, 0x64f01e70, 0x536eb0d0, 0x10fa6e03, 0x5b7f9a20, 0x2d8b625d,
+ 0x397410c7, 0x7778284e, 0x1ab75170, 0x254f304e, 0x395ba877, 0x0c2e2815,
+ 0x5c723dec, 0x63b91327, 0x7c5954b5, 0x67dd69a3, 0x21d220c7, 0x5a287fcd,
+ 0x0d0b9c59, 0x22444c9f, 0x6305cb43, 0x12f717cc, 0x77c11945, 0x0e79bda8,
+ 0x6e014391, 0x441d0179, 0x5e17dd2f, 0x53e57a5c, 0x692f4b9a, 0x76c1e94b,
+ 0x5a872d81, 0x044f7e7e, 0x0970844f, 0x25e34e73, 0x57865d3c, 0x640771d2,
+ 0x12d410ed, 0x1424e079, 0x3e1c7fd7, 0x0e89295a, 0x48dcf262, 0x55a29550,
+ 0x0fd4d360, 0x7494d449, 0x41e6f260, 0x2230d4e7, 0x5ad1cd49, 0x7f8dd428,
+ 0x7722b48a, 0x7a14848d, 0x2a83335a, 0x548c0d9b, 0x24f5d43b, 0x33a417cb,
+ 0x3061e078, 0x1a1bc935, 0x5aedb5df, 0x6755f3e4, 0x795e4cdb, 0x64dfcd1c,
+ 0x6d5164fc, 0x34a3df0e, 0x2cc92142, 0x2569127d, 0x130f3d86, 0x43617cc2,
+ 0x25eaf1fa, 0x044ae792, 0x4b47ee17, 0x6879ea87, 0x7eb455fa, 0x54481e19,
+ 0x13bba2f0, 0x6da3fe79, 0x19c306ff, 0x42591e38, 0x2b0e205d, 0x60bd48bc,
+ 0x550aa0ce, 0x2296a6ef, 0x551eb052, 0x76df1b8e, 0x242a2d22, 0x0ada0b06,
+ 0x58b661ec, 0x490bec94, 0x20bd7c59, 0x760de8c3, 0x7a048ee8, 0x44ba6dcd,
+ 0x3816abd9, 0x47e8527e, 0x2194a188, 0x6967a480, 0x7f7e2083, 0x0ec455f3,
+ 0x78198eab, 0x3d710773, 0x05969198, 0x76ffcffe, 0x54be4797, 0x11105781,
+ 0x3a851719, 0x516284b8, 0x4295de1c, 0x3905be43, 0x6d4e7d6a, 0x0877796d,
+ 0x0b9e986a, 0x5e2b853f, 0x7e6c79cd, 0x4a44a54c, 0x1e28b9a2, 0x5b1e408e,
+ 0x6a1c8eac, 0x62a87929, 0x4f075dac, 0x5c030e8c, 0x3df73ce9, 0x321c3c69,
+ 0x2325cc45, 0x4eaf0759, 0x486a31fb, 0x12d04b94, 0x714e15d5, 0x420d1910,
+ 0x092dc45b, 0x0119beac, 0x68b2bfdb, 0x74863a17, 0x3c7ab8e5, 0x035bc2df,
+ 0x4e7a7965, 0x017f58d6, 0x6414074e, 0x3a1e64ae, 0x2d6725d8, 0x0f22f82a,
+ 0x0a0affa0, 0x4159f31e, 0x4002cb9d, 0x234e393f, 0x6028169f, 0x3b804078,
+ 0x0c16e2e1, 0x0e198020, 0x24b13c40, 0x1ceb2143, 0x38dd4246, 0x6f483590,
+ 0x69b20a6e, 0x105580b1, 0x5d60f184, 0x065d18eb, 0x09a28739, 0x70345728,
+ 0x595a5934, 0x14a78a43, 0x449f05c7, 0x6556fcfc, 0x260bc0b2, 0x3afb600e,
+ 0x1f47bb91, 0x145c14b6, 0x541832fe, 0x54f10f23, 0x3013650e, 0x6c0d32ba,
+ 0x4f202c8d, 0x66bcc661, 0x6131dc7f, 0x04828b25, 0x1737565d, 0x520e967f,
+ 0x16cf0438, 0x6f2bc19e, 0x553c3dda, 0x356906b0, 0x333916d5, 0x2887c195,
+ 0x11e7440b, 0x6354f182, 0x06b2f977, 0x6d2c9a5c, 0x2d02bfb7, 0x74fafcf6,
+ 0x2b955161, 0x74035c38, 0x6e9bc991, 0x09a3a5b9, 0x460f416a, 0x11afabfc,
+ 0x66e32d10, 0x4a56ac6e, 0x6448afa8, 0x680b0044, 0x05d0e296, 0x49569eac,
+ 0x0adb563b, 0x4a9da168, 0x4f857004, 0x0f234600, 0x6db386ec, 0x280b94bf,
+ 0x7cd258a5, 0x6165fd88, 0x3bf2aac9, 0x2cb47c44, 0x2381c2a4, 0x4fe42552,
+ 0x21d4c81e, 0x24baa9af, 0x365231cb, 0x11b7fc81, 0x419748fb, 0x38ff637e,
+ 0x065f3365, 0x21f1aba8, 0x2df41ace, 0x5cec1d95, 0x22c078a8, 0x7bb894fc,
+ 0x2d66fc53, 0x7ed82ccc, 0x4485c9d7, 0x1af210fc, 0x5d2faa09, 0x3b33412e,
+ 0x79d12ea8, 0x7bb8103b, 0x5cea1a7b, 0x2779db45, 0x1250ed5b, 0x0c4d8964,
+ 0x6c18e9f5, 0x501ddc60, 0x3de43ae4, 0x6c0e8577, 0x0adfb426, 0x7ec718f5,
+ 0x1991f387, 0x101ccb9c, 0x632360b4, 0x7d52ce4d, 0x0b58c91c, 0x1fa59d53,
+ 0x0b0b48b0, 0x297315d0, 0x7f3132ff, 0x323b85d1, 0x2f852141, 0x23e84bdc,
+ 0x3732cb25, 0x1274eb57, 0x21a882c3, 0x095288a9, 0x2120e253, 0x617799ce,
+ 0x5e4926b3, 0x52575363, 0x696722e0, 0x509c9117, 0x3b60f14f, 0x423310fa,
+ 0x4e694e80, 0x000a647e, 0x453e283a, 0x3f1d21ef, 0x527c91f0, 0x7ac2e88a,
+ 0x1ba3b840, 0x1c3f253a, 0x04c40280, 0x437dc361, 0x7247859c, 0x61e5b34c,
+ 0x20746a53, 0x58cfc2df, 0x79edf48e, 0x5b48e723, 0x7b08baac, 0x1d1035ea,
+ 0x023fc918, 0x2de0427c, 0x71540904, 0x4030e8f5, 0x2b0961f6, 0x4ec98ef0,
+ 0x781076ee, 0x0dac959b, 0x16f66214, 0x273411e5, 0x02334297, 0x3b568cd1,
+ 0x7cf4e8c0, 0x0f4c2c91, 0x2d8dd28e, 0x4a7b3fb0, 0x237969ae, 0x363d6cb6,
+ 0x75fee60a, 0x5825f4df, 0x29f79f9d, 0x22de4f33, 0x2309590e, 0x1977c2bd,
+ 0x67f7bebe, 0x452b8330, 0x5dc70832, 0x5cddbea4, 0x59091e0b, 0x4d287830,
+ 0x2bbc2ce6, 0x420ee023, 0x02d6e086, 0x228a7a14, 0x48207207, 0x1d5ccc5a,
+ 0x37d32cdc, 0x50dc6508, 0x0b795304, 0x5b9fd543, 0x2a3f2925, 0x72e71606,
+ 0x0dc8ba42, 0x3279a910, 0x6bd2c2e2, 0x775065d8, 0x547c59a6, 0x4b5374cf,
+ 0x0c45cd18, 0x532096d6, 0x351c9bd1, 0x107fdce0, 0x3ae69075, 0x5dddd5de,
+ 0x3bb0ba8b, 0x0b1a0019, 0x6c226525, 0x109e9002, 0x312191be, 0x16fa3de8,
+ 0x4a5197aa, 0x0931b2d2, 0x79ee6e1b, 0x657a142b, 0x6ab74d38, 0x77440cff,
+ 0x11e37956, 0x5c335799, 0x269d3be3, 0x18923cfd, 0x4dd71b00, 0x77c58014,
+ 0x07145324, 0x1678546a, 0x5dfd4f6a, 0x207f4e13, 0x6b0a98c0, 0x015bc2cf,
+ 0x1636d8fe, 0x7bc5f038, 0x183a0661, 0x573ec5f3, 0x54cf2255, 0x2fcc905c,
+ 0x71bb70b9, 0x2b122a89, 0x59f86e5b, 0x5528273d, 0x464cf857, 0x27efdeec,
+ 0x1d0bcfcc, 0x64d7837f, 0x1e7a659a, 0x02aa611c, 0x53969ad5, 0x0e83f59f,
+ 0x50a6d11b, 0x79513c59, 0x0e5c3c98, 0x2ed7bbcf, 0x117de9d9, 0x375ec696,
+ 0x19c830aa, 0x66950511, 0x2b6dbbaa, 0x5ca18c9b, 0x0a487514, 0x6f44a887,
+ 0x6921bc6e, 0x3ef8130b, 0x26f6cde3, 0x686d7605, 0x6583553a, 0x29bcf7cc,
+ 0x55d42201, 0x1c93497c, 0x64c53231, 0x32088f6e, 0x381c5770, 0x617574d8,
+ 0x09757952, 0x1a616eb0, 0x1140e8aa, 0x0ff66ffb, 0x32039001, 0x5a455e7c,
+ 0x0027b906, 0x21cf154c, 0x67d3527f, 0x56fd7602, 0x150f8b25, 0x2ae8e4c8,
+ 0x0bf10aec, 0x3d26a40f, 0x5c4c8ffc, 0x3c291322, 0x737fd02c, 0x4b506209,
+ 0x484ddaa4, 0x00b44669, 0x5974bdd1, 0x7d39d617, 0x12995404, 0x48f00bbe,
+ 0x44f7c59a, 0x23cb9292, 0x6476f20b, 0x034fbd59, 0x2893161c, 0x1dbae8c0,
+ 0x50348c2e, 0x797f0957, 0x685ddeaf, 0x36fb8a2e, 0x0fceb6f4, 0x10347ab4,
+ 0x72720bfc, 0x292a4304, 0x0cbf8a27, 0x3cea6db7, 0x4b0c6b15, 0x57e8e716,
+ 0x4e9c54cc, 0x4fc7f7ca, 0x49a6d3e2, 0x10fc2df3, 0x73db387e, 0x72cb89c3,
+ 0x71dba437, 0x4b14048c, 0x6e1af265, 0x1084b213, 0x3842107d, 0x6ecdc171,
+ 0x647919b2, 0x41a80841, 0x7b387c76, 0x46bc094b, 0x331b312a, 0x2f140cc4,
+ 0x355d0a11, 0x19390200, 0x69b05263, 0x582963fa, 0x44897e31, 0x66a473f0,
+ 0x0374f08d, 0x35879e45, 0x5e1dd7ef, 0x34d6a311, 0x6e4e18eb, 0x7b44734b,
+ 0x0e421333, 0x3da026d8, 0x5becbf4b, 0x56db4a1f, 0x1f2089bc, 0x28c733f2,
+ 0x04b0975d, 0x6156f224, 0x12d1f40f, 0x7f4d30f4, 0x2c0b9861, 0x769a083b,
+ 0x739544fb, 0x1dbd1067, 0x0e8cd717, 0x4c246fb2, 0x115eff39, 0x19e22f2a,
+ 0x4563ba61, 0x5d33a617, 0x54af83cf, 0x030bde73, 0x54b4736d, 0x0f01dfec,
+ 0x08869c01, 0x4e9e4d7b, 0x4739855a, 0x62d964a3, 0x26948fde, 0x30adf212,
+ 0x1f57b400, 0x3766c914, 0x1e7f9d1c, 0x33258b59, 0x522ab2c2, 0x3dc99798,
+ 0x15f53fe2, 0x05636669, 0x354b59c3, 0x1c37ebd4, 0x0bb7ebf9, 0x0e4e87f9,
+ 0x680d3124, 0x2770d549, 0x0c5e112e, 0x74aaa7ed, 0x06c0b550, 0x342b5922,
+ 0x4532ab5b, 0x4257dbee, 0x087f32a9, 0x45ada3e3, 0x7a854272, 0x061625f2,
+ 0x47c85a91, 0x25ad375d, 0x2809bd9d, 0x168b9348, 0x4381b0a3, 0x6f2dc6ca,
+ 0x122e54f6, 0x6c3228a6, 0x653c1652, 0x60b60584, 0x1d304b77, 0x4cc74c58,
+ 0x087e3dd5, 0x79bd540e, 0x79ab7a70, 0x26fcd1c9, 0x342abaaf, 0x644716b0,
+ 0x01f076cb, 0x73628937, 0x20b01ff8, 0x5832b80b, 0x2f77fc92, 0x4468d962,
+ 0x2bac2679, 0x7f850778, 0x47d2997c, 0x02690cb7, 0x7de54951, 0x54d80b14,
+ 0x5e0c6854, 0x313cc749, 0x622b86ba, 0x38dbf6d3, 0x045d3e52, 0x574f87fd,
+ 0x09f1b078, 0x31784f71, 0x4f01dd2f, 0x1874c9f9, 0x5837c7af, 0x2372f768,
+ 0x531bd1e8, 0x61816c0b, 0x4592995f, 0x156463c0, 0x250c5afe, 0x40c83178,
+ 0x4396f6b7, 0x29bdbec0, 0x43ea8ca5, 0x5c474696, 0x2c869192, 0x2ff2f51a,
+ 0x7c963fe5, 0x294319c1, 0x019fbe26, 0x72fa8e68, 0x245ca463, 0x4ca88208,
+ 0x72ac845a, 0x25307181, 0x2cdf88f7, 0x0adbfebd, 0x2eea465b, 0x52e4eee0,
+ 0x084daacd, 0x717ce67e, 0x594087c2, 0x2b8ee5c7, 0x4558f811, 0x76b65ba4,
+ 0x5de05e09, 0x3db76e27, 0x3c75110d, 0x04ca67e7, 0x51cd6d09, 0x7b4e9c3e,
+ 0x7cdda4d2, 0x674fb021, 0x7d372d2d, 0x13f7978b, 0x5fb106b1, 0x034377d1,
+ 0x2e5336f3, 0x099bb17d, 0x04e6755e, 0x34f73c1e, 0x004e0a0d, 0x7f2c32e2,
+ 0x1fc8f910, 0x67d0859d, 0x76462b25, 0x59fa9a17, 0x028e53ef, 0x3d6d5fdd,
+ 0x79a4671e, 0x5cbec506, 0x2c23ee6d, 0x628a2c1e, 0x4dae87bd, 0x07a189ea,
+ 0x3a414a96, 0x5915f622, 0x6bea011e, 0x412674cf, 0x07ecc314, 0x6a7dbce8,
+ 0x7e176f10, 0x68e60d47, 0x079ea970, 0x79f3b55c, 0x65a46098, 0x56155533,
+ 0x7e5d0272, 0x795bfad5, 0x094da770, 0x05ba427c, 0x152e430e, 0x187d8470,
+ 0x08e607bc, 0x45ce5ef9, 0x654231ae, 0x38d8cb48, 0x605632f8, 0x25cf8ee9,
+ 0x11497170, 0x171a3b00, 0x0f103d49, 0x24826483, 0x2848e187, 0x7498919b,
+ 0x1bb788cb, 0x791ad5c7, 0x5129330e, 0x016c4436, 0x430f05bf, 0x1f06b5cd,
+ 0x62df1378, 0x0423b9b4, 0x0341acaf, 0x3189543c, 0x7b96b2ea, 0x6c4865c3,
+ 0x4cc7adc3, 0x78a2bff6, 0x642db7c7, 0x70d02300, 0x7cd43ac0, 0x4f5fe414,
+ 0x333b52c2, 0x500d3c74, 0x65782c01, 0x3f72a2c5, 0x278f59d8, 0x493bf7f8,
+ 0x16bf51a0, 0x6cc70ced, 0x6ed15979, 0x1a77abae, 0x08cadbb7, 0x2f2e0bc0,
+ 0x236f5e8d, 0x1a4b4495, 0x360bd008, 0x32227d40
+};
+
+int
+main()
+{
+ SHA1Sum sum;
+ SHA1Sum::Hash hash;
+ sum.update(reinterpret_cast<const uint8_t*>(gTestV), sizeof(gTestV));
+ sum.finish(hash);
+
+ static const uint8_t expected[20] = {
+ 0xc8, 0xf2, 0x09, 0x59, 0x4e, 0x64, 0x40, 0xaa, 0x7b, 0xf7, 0xb8, 0xe0,
+ 0xfa, 0x44, 0xb2, 0x31, 0x95, 0xad, 0x94, 0x81
+ };
+
+ static_assert(sizeof(expected) == sizeof(SHA1Sum::Hash),
+ "expected-data size should be the same as the actual hash "
+ "size");
+
+ for (size_t i = 0; i < SHA1Sum::kHashSize; i++) {
+ MOZ_RELEASE_ASSERT(hash[i] == expected[i]);
+ }
+
+ return 0;
+}
diff --git a/mfbt/tests/TestSaturate.cpp b/mfbt/tests/TestSaturate.cpp
new file mode 100644
index 000000000..06573ba4a
--- /dev/null
+++ b/mfbt/tests/TestSaturate.cpp
@@ -0,0 +1,215 @@
+/* -*- 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/Saturate.h>
+
+#include <mozilla/Assertions.h>
+
+#include <limits>
+
+using mozilla::detail::Saturate;
+
+#define A(a) MOZ_RELEASE_ASSERT(a, "Test \'" #a "\' failed.")
+
+static const unsigned long sNumOps = 32;
+
+template<typename T>
+static T
+StartValue()
+{
+ // Specialize |StartValue| for the given type.
+ A(false);
+}
+
+template<>
+int8_t
+StartValue<int8_t>()
+{
+ return 0;
+}
+
+template<>
+int16_t
+StartValue<int16_t>()
+{
+ return 0;
+}
+
+template<>
+int32_t
+StartValue<int32_t>()
+{
+ return 0;
+}
+
+template<>
+uint8_t
+StartValue<uint8_t>()
+{
+ // Picking a value near middle of uint8_t's range.
+ return static_cast<uint8_t>(std::numeric_limits<int8_t>::max());
+}
+
+template<>
+uint16_t
+StartValue<uint16_t>()
+{
+ // Picking a value near middle of uint16_t's range.
+ return static_cast<uint8_t>(std::numeric_limits<int16_t>::max());
+}
+
+template<>
+uint32_t
+StartValue<uint32_t>()
+{
+ // Picking a value near middle of uint32_t's range.
+ return static_cast<uint8_t>(std::numeric_limits<int32_t>::max());
+}
+
+// Add
+//
+
+template<typename T>
+static void
+TestPrefixIncr()
+{
+ T value = StartValue<T>();
+ Saturate<T> satValue(value);
+
+ for (T i = 0; i < static_cast<T>(sNumOps); ++i) {
+ A(++value == ++satValue);
+ }
+}
+
+template<typename T>
+static void
+TestPostfixIncr()
+{
+ T value = StartValue<T>();
+ Saturate<T> satValue(value);
+
+ for (T i = 0; i < static_cast<T>(sNumOps); ++i) {
+ A(value++ == satValue++);
+ }
+}
+
+template<typename T>
+static void
+TestAdd()
+{
+ T value = StartValue<T>();
+ Saturate<T> satValue(value);
+
+ for (T i = 0; i < static_cast<T>(sNumOps); ++i) {
+ A((value + i) == (satValue + i));
+ }
+}
+
+// Subtract
+//
+
+template<typename T>
+static void
+TestPrefixDecr()
+{
+ T value = StartValue<T>();
+ Saturate<T> satValue(value);
+
+ for (T i = 0; i < static_cast<T>(sNumOps); ++i) {
+ A(--value == --satValue);
+ }
+}
+
+template<typename T>
+static void
+TestPostfixDecr()
+{
+ T value = StartValue<T>();
+ Saturate<T> satValue(value);
+
+ for (T i = 0; i < static_cast<T>(sNumOps); ++i) {
+ A(value-- == satValue--);
+ }
+}
+
+template<typename T>
+static void
+TestSub()
+{
+ T value = StartValue<T>();
+ Saturate<T> satValue(value);
+
+ for (T i = 0; i < static_cast<T>(sNumOps); ++i) {
+ A((value - i) == (satValue - i));
+ }
+}
+
+// Corner cases near bounds
+//
+
+template<typename T>
+static void
+TestUpperBound()
+{
+ Saturate<T> satValue(std::numeric_limits<T>::max());
+
+ A(--satValue == (std::numeric_limits<T>::max() - 1));
+ A(++satValue == (std::numeric_limits<T>::max()));
+ A(++satValue == (std::numeric_limits<T>::max())); // don't overflow here
+ A(++satValue == (std::numeric_limits<T>::max())); // don't overflow here
+ A(--satValue == (std::numeric_limits<T>::max() - 1)); // back at (max - 1)
+ A(--satValue == (std::numeric_limits<T>::max() - 2));
+}
+
+template<typename T>
+static void
+TestLowerBound()
+{
+ Saturate<T> satValue(std::numeric_limits<T>::min());
+
+ A(++satValue == (std::numeric_limits<T>::min() + 1));
+ A(--satValue == (std::numeric_limits<T>::min()));
+ A(--satValue == (std::numeric_limits<T>::min())); // don't overflow here
+ A(--satValue == (std::numeric_limits<T>::min())); // don't overflow here
+ A(++satValue == (std::numeric_limits<T>::min() + 1)); // back at (max + 1)
+ A(++satValue == (std::numeric_limits<T>::min() + 2));
+}
+
+// Framework
+//
+
+template<typename T>
+static void
+TestAll()
+{
+ // Assert that we don't accidently hit type's range limits in tests.
+ const T value = StartValue<T>();
+ A(std::numeric_limits<T>::min() + static_cast<T>(sNumOps) <= value);
+ A(std::numeric_limits<T>::max() - static_cast<T>(sNumOps) >= value);
+
+ TestPrefixIncr<T>();
+ TestPostfixIncr<T>();
+ TestAdd<T>();
+
+ TestPrefixDecr<T>();
+ TestPostfixDecr<T>();
+ TestSub<T>();
+
+ TestUpperBound<T>();
+ TestLowerBound<T>();
+}
+
+int
+main()
+{
+ TestAll<int8_t>();
+ TestAll<int16_t>();
+ TestAll<int32_t>();
+ TestAll<uint8_t>();
+ TestAll<uint16_t>();
+ TestAll<uint32_t>();
+ return 0;
+}
diff --git a/mfbt/tests/TestScopeExit.cpp b/mfbt/tests/TestScopeExit.cpp
new file mode 100644
index 000000000..fadd3a10a
--- /dev/null
+++ b/mfbt/tests/TestScopeExit.cpp
@@ -0,0 +1,54 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/ScopeExit.h"
+
+using mozilla::MakeScopeExit;
+
+#define CHECK(c) \
+ do { \
+ bool cond = !!(c); \
+ MOZ_RELEASE_ASSERT(cond, "Failed assertion: " #c); \
+ if (!cond) { \
+ return false; \
+ } \
+ } while (false)
+
+static bool
+Test()
+{
+ int a = 1;
+ int b = 1;
+
+ {
+ a++;
+ auto guardA = MakeScopeExit([&] {
+ a--;
+ });
+
+ b++;
+ auto guardB = MakeScopeExit([&] {
+ b--;
+ });
+
+ guardB.release();
+ }
+
+ CHECK(a == 1);
+ CHECK(b == 2);
+
+ return true;
+}
+
+int
+main()
+{
+ if (!Test()) {
+ return 1;
+ }
+ return 0;
+}
diff --git a/mfbt/tests/TestSegmentedVector.cpp b/mfbt/tests/TestSegmentedVector.cpp
new file mode 100644
index 000000000..2bf2540aa
--- /dev/null
+++ b/mfbt/tests/TestSegmentedVector.cpp
@@ -0,0 +1,279 @@
+/* -*- 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_MUST_USE. 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);
+
+ // Fill the vector up to verify PopLastN works.
+ for (i = 0; i < 1000; ++i) {
+ v.InfallibleAppend(mozilla::Move(i));
+ }
+ MOZ_RELEASE_ASSERT(!v.IsEmpty());
+ MOZ_RELEASE_ASSERT(v.Length() == 1000);
+
+ // Verify we pop the right amount of elements.
+ v.PopLastN(300);
+ MOZ_RELEASE_ASSERT(v.Length() == 700);
+
+ // Verify the contents are what we expect.
+ n = 0;
+ for (auto iter = v.Iter(); !iter.Done(); iter.Next()) {
+ MOZ_RELEASE_ASSERT(iter.Get() == n);
+ n++;
+ }
+ MOZ_RELEASE_ASSERT(n == 700);
+}
+
+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()
+{
+ size_t defaultCtorCalls = 0;
+ size_t explicitCtorCalls = 0;
+ size_t copyCtorCalls = 0;
+ size_t moveCtorCalls = 0;
+ size_t dtorCalls = 0;
+
+ {
+ static const size_t segmentSize = 64;
+
+ // A SegmentedVector with a non-POD element type.
+ NonPOD x(1); // explicit constructor called
+ explicitCtorCalls++;
+ SegmentedVector<NonPOD, segmentSize, InfallibleAllocPolicy> v;
+ // default constructor called 0 times
+ MOZ_RELEASE_ASSERT(v.IsEmpty());
+ gDummy = v.Append(x); // copy constructor called
+ copyCtorCalls++;
+ NonPOD y(1); // explicit constructor called
+ explicitCtorCalls++;
+ gDummy = v.Append(mozilla::Move(y)); // move constructor called
+ moveCtorCalls++;
+ NonPOD z(1); // explicit constructor called
+ explicitCtorCalls++;
+ v.InfallibleAppend(mozilla::Move(z)); // move constructor called
+ moveCtorCalls++;
+ v.PopLast(); // destructor called 1 time
+ dtorCalls++;
+ MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls);
+ v.Clear(); // destructor called 2 times
+ dtorCalls += 2;
+
+ // Test that PopLastN() correctly calls the destructors of all the
+ // elements in the segments it destroys.
+ //
+ // We depend on the size of NonPOD when determining how many things
+ // to push onto the vector. It would be nicer to get this information
+ // from SegmentedVector itself...
+ static_assert(sizeof(NonPOD) == 1, "Fix length calculations!");
+
+ size_t nonFullLastSegmentSize = segmentSize - 1;
+ for (size_t i = 0; i < nonFullLastSegmentSize; ++i) {
+ gDummy = v.Append(x); // copy constructor called
+ copyCtorCalls++;
+ }
+ MOZ_RELEASE_ASSERT(gNumCopyCtors == copyCtorCalls);
+
+ // Pop some of the elements.
+ {
+ size_t partialPopAmount = 5;
+ MOZ_RELEASE_ASSERT(nonFullLastSegmentSize > partialPopAmount);
+ v.PopLastN(partialPopAmount); // destructor called partialPopAmount times
+ dtorCalls += partialPopAmount;
+ MOZ_RELEASE_ASSERT(v.Length() == nonFullLastSegmentSize - partialPopAmount);
+ MOZ_RELEASE_ASSERT(!v.IsEmpty());
+ MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls);
+ }
+
+ // Pop a full segment.
+ {
+ size_t length = v.Length();
+ v.PopLastN(length);
+ dtorCalls += length;
+ // These two tests *are* semantically different given the underlying
+ // implementation; Length sums up the sizes of the internal segments,
+ // while IsEmpty looks at the sequence of internal segments.
+ MOZ_RELEASE_ASSERT(v.Length() == 0);
+ MOZ_RELEASE_ASSERT(v.IsEmpty());
+ MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls);
+ }
+
+ size_t multipleSegmentsSize = (segmentSize * 3) / 2;
+ for (size_t i = 0; i < multipleSegmentsSize; ++i) {
+ gDummy = v.Append(x); // copy constructor called
+ copyCtorCalls++;
+ }
+ MOZ_RELEASE_ASSERT(gNumCopyCtors == copyCtorCalls);
+
+ // Pop across segment boundaries.
+ {
+ v.PopLastN(segmentSize);
+ dtorCalls += segmentSize;
+ MOZ_RELEASE_ASSERT(v.Length() == (multipleSegmentsSize - segmentSize));
+ MOZ_RELEASE_ASSERT(!v.IsEmpty());
+ MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls);
+ }
+
+ // Clear everything here to make calculations easier.
+ {
+ size_t length = v.Length();
+ v.Clear();
+ dtorCalls += length;
+ MOZ_RELEASE_ASSERT(v.IsEmpty());
+ MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls);
+ }
+
+ MOZ_RELEASE_ASSERT(gNumDefaultCtors == defaultCtorCalls);
+ MOZ_RELEASE_ASSERT(gNumExplicitCtors == explicitCtorCalls);
+ MOZ_RELEASE_ASSERT(gNumCopyCtors == copyCtorCalls);
+ MOZ_RELEASE_ASSERT(gNumMoveCtors == moveCtorCalls);
+ MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls);
+ } // destructor called for x, y, z
+ dtorCalls += 3;
+ MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls);
+}
+
+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;
+}
diff --git a/mfbt/tests/TestSplayTree.cpp b/mfbt/tests/TestSplayTree.cpp
new file mode 100644
index 000000000..030336fa7
--- /dev/null
+++ b/mfbt/tests/TestSplayTree.cpp
@@ -0,0 +1,212 @@
+/* -*- 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/. */
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/SplayTree.h"
+#include "mozilla/Unused.h"
+
+using mozilla::SplayTree;
+using mozilla::SplayTreeNode;
+
+// The following array contains the values 0..999 in random order, as computed
+// with the following Python program:
+//
+// from random import shuffle
+// x = range(1000)
+// shuffle(x)
+// print(x);
+//
+static int gValues[] = {
+ 778, 999, 248, 795, 607, 177, 725, 33, 215, 565, 436, 821, 941, 802, 322, 54,
+ 151, 416, 531, 65, 818, 99, 340, 401, 274, 767, 278, 617, 425, 629, 833, 878,
+ 440, 984, 724, 519, 100, 369, 490, 131, 422, 169, 932, 476, 823, 521, 390,
+ 781, 747, 218, 376, 461, 717, 532, 471, 298, 720, 608, 334, 788, 161, 500,
+ 280, 963, 430, 484, 779, 572, 96, 333, 650, 158, 199, 137, 991, 399, 882,
+ 689, 358, 548, 196, 718, 211, 388, 133, 188, 321, 892, 25, 694, 735, 886,
+ 872, 785, 195, 275, 696, 975, 393, 619, 894, 18, 281, 191, 792, 846, 861,
+ 351, 542, 806, 570, 702, 931, 585, 444, 284, 217, 132, 251, 253, 302, 808,
+ 224, 37, 63, 863, 409, 49, 780, 790, 31, 638, 890, 186, 114, 152, 949, 491,
+ 207, 392, 170, 460, 794, 482, 877, 407, 263, 909, 249, 710, 614, 51, 431,
+ 915, 62, 332, 74, 495, 901, 23, 365, 752, 89, 660, 745, 741, 547, 669, 449,
+ 465, 605, 107, 774, 205, 852, 266, 247, 690, 835, 765, 410, 140, 122, 400,
+ 510, 664, 105, 935, 230, 134, 106, 959, 375, 884, 361, 527, 715, 840, 272,
+ 232, 102, 415, 903, 117, 313, 153, 463, 464, 876, 406, 967, 713, 381, 836,
+ 555, 190, 859, 172, 483, 61, 633, 294, 993, 72, 337, 11, 896, 523, 101, 916,
+ 244, 566, 706, 533, 439, 201, 222, 695, 739, 553, 571, 289, 918, 209, 189,
+ 357, 814, 670, 866, 910, 579, 246, 636, 750, 891, 494, 758, 341, 626, 426,
+ 772, 254, 682, 588, 104, 347, 184, 977, 126, 498, 165, 955, 241, 516, 235,
+ 497, 121, 123, 791, 844, 259, 995, 283, 602, 417, 221, 308, 855, 429, 86,
+ 345, 928, 44, 679, 796, 363, 402, 445, 492, 450, 964, 749, 925, 847, 637,
+ 982, 648, 635, 481, 564, 867, 940, 291, 159, 290, 929, 59, 712, 986, 611,
+ 954, 820, 103, 622, 316, 142, 204, 225, 678, 314, 84, 578, 315, 141, 990,
+ 880, 504, 969, 412, 746, 47, 517, 124, 848, 466, 438, 674, 979, 782, 651,
+ 181, 26, 435, 832, 386, 951, 229, 642, 655, 91, 162, 921, 647, 113, 686, 56,
+ 805, 763, 245, 581, 287, 998, 525, 641, 135, 634, 237, 728, 112, 828, 228,
+ 899, 1, 723, 16, 613, 144, 659, 97, 185, 312, 292, 733, 624, 276, 387, 926,
+ 339, 768, 960, 610, 807, 656, 851, 219, 582, 709, 927, 514, 680, 870, 597,
+ 536, 77, 164, 512, 149, 900, 85, 335, 997, 8, 705, 777, 653, 815, 311, 701,
+ 507, 202, 530, 827, 541, 958, 82, 874, 55, 487, 383, 885, 684, 180, 829, 760,
+ 109, 194, 540, 816, 906, 657, 469, 446, 857, 907, 38, 600, 618, 797, 950,
+ 822, 277, 842, 116, 513, 255, 424, 643, 163, 372, 129, 67, 118, 754, 529,
+ 917, 687, 473, 174, 538, 939, 663, 775, 474, 242, 883, 20, 837, 293, 584,
+ 943, 32, 176, 904, 14, 448, 893, 888, 744, 171, 714, 454, 691, 261, 934, 606,
+ 789, 825, 671, 397, 338, 317, 612, 737, 130, 41, 923, 574, 136, 980, 850, 12,
+ 729, 197, 403, 57, 783, 360, 146, 75, 432, 447, 192, 799, 740, 267, 214, 250,
+ 367, 853, 968, 120, 736, 391, 881, 784, 665, 68, 398, 350, 839, 268, 697,
+ 567, 428, 738, 48, 182, 70, 220, 865, 418, 374, 148, 945, 353, 539, 589, 307,
+ 427, 506, 265, 558, 128, 46, 336, 299, 349, 309, 377, 304, 420, 30, 34, 875,
+ 948, 212, 394, 442, 719, 273, 269, 157, 502, 675, 751, 838, 897, 862, 831,
+ 676, 590, 811, 966, 854, 477, 15, 598, 573, 108, 98, 81, 408, 421, 296, 73,
+ 644, 456, 362, 666, 550, 331, 368, 193, 470, 203, 769, 342, 36, 604, 60, 970,
+ 748, 813, 522, 515, 90, 672, 243, 793, 947, 595, 632, 912, 475, 258, 80, 873,
+ 623, 524, 546, 262, 727, 216, 505, 330, 373, 58, 297, 609, 908, 150, 206,
+ 703, 755, 260, 511, 213, 198, 766, 898, 992, 488, 405, 974, 770, 936, 743,
+ 554, 0, 499, 976, 94, 160, 919, 434, 324, 156, 757, 830, 677, 183, 630, 871,
+ 640, 938, 518, 344, 366, 742, 552, 306, 535, 200, 652, 496, 233, 419, 787,
+ 318, 981, 371, 166, 143, 384, 88, 508, 698, 812, 559, 658, 549, 208, 599,
+ 621, 961, 668, 563, 93, 154, 587, 560, 389, 3, 210, 326, 4, 924, 300, 2, 804,
+ 914, 801, 753, 654, 27, 236, 19, 708, 451, 985, 596, 478, 922, 240, 127, 994,
+ 983, 385, 472, 40, 528, 288, 111, 543, 568, 155, 625, 759, 937, 956, 545,
+ 953, 962, 382, 479, 809, 557, 501, 354, 414, 343, 378, 843, 379, 178, 556,
+ 800, 803, 592, 627, 942, 576, 920, 704, 707, 726, 223, 119, 404, 24, 879,
+ 722, 868, 5, 238, 817, 520, 631, 946, 462, 457, 295, 480, 957, 441, 145, 286,
+ 303, 688, 17, 628, 493, 364, 226, 110, 615, 69, 320, 534, 593, 721, 411, 285,
+ 869, 952, 849, 139, 356, 346, 28, 887, 810, 92, 798, 544, 458, 996, 692, 396,
+ 667, 328, 173, 22, 773, 50, 645, 987, 42, 685, 734, 700, 683, 601, 580, 639,
+ 913, 323, 858, 179, 761, 6, 841, 905, 234, 730, 29, 21, 575, 586, 902, 443,
+ 826, 646, 257, 125, 649, 53, 453, 252, 13, 87, 971, 227, 485, 168, 380, 711,
+ 79, 732, 325, 52, 468, 76, 551, 39, 395, 327, 973, 459, 45, 583, 989, 147,
+ 455, 776, 944, 569, 889, 256, 35, 175, 834, 756, 933, 860, 526, 845, 864,
+ 764, 771, 282, 9, 693, 352, 731, 7, 577, 264, 319, 138, 467, 819, 930, 231,
+ 115, 988, 978, 762, 486, 301, 616, 10, 78, 603, 452, 965, 279, 972, 413, 895,
+ 591, 662, 594, 348, 423, 489, 43, 699, 433, 509, 355, 270, 66, 83, 95, 561,
+ 661, 562, 329, 620, 370, 64, 187, 503, 716, 856, 310, 786, 167, 71, 239, 359,
+ 537, 437, 305, 673, 824, 911, 681, 271
+};
+
+struct SplayInt : SplayTreeNode<SplayInt>
+{
+ explicit SplayInt(int aValue) : mValue(aValue) {}
+
+ static int compare(const SplayInt& aOne, const SplayInt& aTwo)
+ {
+ if (aOne.mValue < aTwo.mValue) {
+ return -1;
+ }
+ if (aOne.mValue > aTwo.mValue) {
+ return 1;
+ }
+ return 0;
+ }
+
+ int mValue;
+};
+
+struct SplayNoCopy : SplayTreeNode<SplayNoCopy>
+{
+ SplayNoCopy(const SplayNoCopy&) = delete;
+ SplayNoCopy(SplayNoCopy&&) = delete;
+
+ static int compare(const SplayNoCopy&, const SplayNoCopy&) { return 0; }
+};
+
+static SplayTree<SplayNoCopy, SplayNoCopy> testNoCopy;
+
+int
+main()
+{
+ mozilla::Unused << testNoCopy;
+
+ SplayTree<SplayInt, SplayInt> tree;
+
+ MOZ_RELEASE_ASSERT(tree.empty());
+
+ MOZ_RELEASE_ASSERT(!tree.find(SplayInt(0)));
+
+ static const int N = mozilla::ArrayLength(gValues);
+
+ // Insert the values, and check each one is findable just after insertion.
+ for (int i = 0; i < N; i++) {
+ tree.insert(new SplayInt(gValues[i]));
+ SplayInt* inserted = tree.find(SplayInt(gValues[i]));
+ MOZ_RELEASE_ASSERT(inserted);
+ MOZ_RELEASE_ASSERT(tree.findOrInsert(SplayInt(gValues[i])) == inserted);
+ tree.checkCoherency();
+ }
+
+ // Check they're all findable after all insertions.
+ for (int i = 0; i < N; i++) {
+ MOZ_RELEASE_ASSERT(tree.find(SplayInt(gValues[i])));
+ MOZ_RELEASE_ASSERT(tree.findOrInsert(SplayInt(gValues[i])));
+ tree.checkCoherency();
+ }
+
+ // Check that non-inserted values cannot be found.
+ MOZ_RELEASE_ASSERT(!tree.find(SplayInt(-1)));
+ MOZ_RELEASE_ASSERT(!tree.find(SplayInt(N)));
+ MOZ_RELEASE_ASSERT(!tree.find(SplayInt(0x7fffffff)));
+
+ // Remove the values, and check each one is not findable just after removal.
+ for (int i = 0; i < N; i++) {
+ SplayInt* removed = tree.remove(SplayInt(gValues[i]));
+ MOZ_RELEASE_ASSERT(removed->mValue == gValues[i]);
+ MOZ_RELEASE_ASSERT(!tree.find(*removed));
+ delete removed;
+ tree.checkCoherency();
+ }
+
+ MOZ_RELEASE_ASSERT(tree.empty());
+
+ // Insert the values, and check each one is findable just after insertion.
+ for (int i = 0; i < N; i++) {
+ SplayInt* inserted = tree.findOrInsert(SplayInt(gValues[i]));
+ MOZ_RELEASE_ASSERT(tree.find(SplayInt(gValues[i])) == inserted);
+ MOZ_RELEASE_ASSERT(tree.findOrInsert(SplayInt(gValues[i])) == inserted);
+ tree.checkCoherency();
+ }
+
+ // Check they're all findable after all insertions.
+ for (int i = 0; i < N; i++) {
+ MOZ_RELEASE_ASSERT(tree.find(SplayInt(gValues[i])));
+ MOZ_RELEASE_ASSERT(tree.findOrInsert(SplayInt(gValues[i])));
+ tree.checkCoherency();
+ }
+
+ // Check that non-inserted values cannot be found.
+ MOZ_RELEASE_ASSERT(!tree.find(SplayInt(-1)));
+ MOZ_RELEASE_ASSERT(!tree.find(SplayInt(N)));
+ MOZ_RELEASE_ASSERT(!tree.find(SplayInt(0x7fffffff)));
+
+ // Remove the values, and check each one is not findable just after removal.
+ for (int i = 0; i < N; i++) {
+ SplayInt* removed = tree.remove(SplayInt(gValues[i]));
+ MOZ_RELEASE_ASSERT(removed->mValue == gValues[i]);
+ MOZ_RELEASE_ASSERT(!tree.find(*removed));
+ delete removed;
+ tree.checkCoherency();
+ }
+
+ MOZ_RELEASE_ASSERT(tree.empty());
+
+ // Reinsert the values, in reverse order to last time.
+ for (int i = 0; i < N; i++) {
+ tree.insert(new SplayInt(gValues[N - i - 1]));
+ tree.checkCoherency();
+ }
+
+ // Remove the minimum value repeatedly.
+ for (int i = 0; i < N; i++) {
+ SplayInt* removed = tree.removeMin();
+ MOZ_RELEASE_ASSERT(removed->mValue == i);
+ delete removed;
+ tree.checkCoherency();
+ }
+
+ MOZ_RELEASE_ASSERT(tree.empty());
+
+ return 0;
+}
diff --git a/mfbt/tests/TestTemplateLib.cpp b/mfbt/tests/TestTemplateLib.cpp
new file mode 100644
index 000000000..7ad0831ed
--- /dev/null
+++ b/mfbt/tests/TestTemplateLib.cpp
@@ -0,0 +1,35 @@
+/* -*- 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/TemplateLib.h"
+
+using mozilla::tl::And;
+
+static_assert(And<>::value == true,
+ "And<>::value should be true");
+static_assert(And<true>::value == true,
+ "And<true>::value should be true");
+static_assert(And<false>::value == false,
+ "And<false>::value should be false");
+static_assert(And<false, true>::value == false,
+ "And<false, true>::value should be false");
+static_assert(And<false, false>::value == false,
+ "And<false, false>::value should be false");
+static_assert(And<true, false>::value == false,
+ "And<true, false>::value should be false");
+static_assert(And<true, true>::value == true,
+ "And<true, true>::value should be true");
+static_assert(And<true, true, true>::value == true,
+ "And<true, true, true>::value should be true");
+static_assert(And<true, false, true>::value == false,
+ "And<true, false, true>::value should be false");
+
+int
+main()
+{
+ // Nothing to do here.
+ return 0;
+}
diff --git a/mfbt/tests/TestTuple.cpp b/mfbt/tests/TestTuple.cpp
new file mode 100644
index 000000000..a85552fef
--- /dev/null
+++ b/mfbt/tests/TestTuple.cpp
@@ -0,0 +1,296 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/Move.h"
+#include "mozilla/Pair.h"
+#include "mozilla/Tuple.h"
+#include "mozilla/TypeTraits.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+
+#include <stddef.h>
+#include <utility>
+
+using mozilla::Get;
+using mozilla::IsSame;
+using mozilla::MakeTuple;
+using mozilla::MakeUnique;
+using mozilla::Move;
+using mozilla::Pair;
+using mozilla::Tie;
+using mozilla::Tuple;
+using mozilla::UniquePtr;
+using mozilla::Unused;
+using std::pair;
+
+#define CHECK(c) \
+ do { \
+ bool cond = !!(c); \
+ MOZ_RELEASE_ASSERT(cond, "Failed assertion: " #c); \
+ } while (false)
+
+// The second argument is the expected type. It's variadic to allow the
+// type to contain commas.
+#define CHECK_TYPE(expression, ...) \
+ static_assert(IsSame<decltype(expression), __VA_ARGS__>::value, \
+ "Type mismatch!")
+
+struct ConvertibleToInt
+{
+ operator int() const { return 42; }
+};
+
+static void
+TestConstruction()
+{
+ // Default construction
+ Tuple<> a;
+ Unused << a;
+ Tuple<int> b;
+ Unused << b;
+
+ // Construction from elements
+ int x = 1, y = 1;
+ Tuple<int, int> c{x, y};
+ Tuple<int&, const int&> d{x, y};
+ x = 42;
+ y = 42;
+ CHECK(Get<0>(c) == 1);
+ CHECK(Get<1>(c) == 1);
+ CHECK(Get<0>(d) == 42);
+ CHECK(Get<1>(d) == 42);
+
+ // Construction from objects convertible to the element types
+ Tuple<int, int> e{1.0, ConvertibleToInt{}};
+
+ // Copy construction
+ Tuple<int> x1;
+ Tuple<int> x2{x1};
+
+ Tuple<int, int> f(c);
+ CHECK(Get<0>(f) == 1);
+ CHECK(Get<0>(f) == 1);
+
+ // Move construction
+ Tuple<UniquePtr<int>> g{MakeUnique<int>(42)};
+ Tuple<UniquePtr<int>> h{Move(g)};
+ CHECK(Get<0>(g) == nullptr);
+ CHECK(*Get<0>(h) == 42);
+}
+
+static void
+TestConstructionFromMozPair()
+{
+ // Construction from elements
+ int x = 1, y = 1;
+ Pair<int, int> a{x, y};
+ Pair<int&, const int&> b{x, y};
+ Tuple<int, int> c(a);
+ Tuple<int&, const int&> d(b);
+ x = 42;
+ y = 42;
+ CHECK(Get<0>(c) == 1);
+ CHECK(Get<1>(c) == 1);
+ CHECK(Get<0>(d) == 42);
+ CHECK(Get<1>(d) == 42);
+}
+
+static void
+TestConstructionFromStdPair()
+{
+ // Construction from elements
+ int x = 1, y = 1;
+ pair<int, int> a{x, y};
+ pair<int&, const int&> b{x, y};
+ Tuple<int, int> c(a);
+ Tuple<int&, const int&> d(b);
+ x = 42;
+ y = 42;
+ CHECK(Get<0>(c) == 1);
+ CHECK(Get<1>(c) == 1);
+ CHECK(Get<0>(d) == 42);
+ CHECK(Get<1>(d) == 42);
+}
+
+static void
+TestAssignment()
+{
+ // Copy assignment
+ Tuple<int> a{0};
+ Tuple<int> b{42};
+ a = b;
+ CHECK(Get<0>(a) == 42);
+
+ // Assignment to reference member
+ int i = 0;
+ int j = 42;
+ Tuple<int&> c{i};
+ Tuple<int&> d{j};
+ c = d;
+ CHECK(i == 42);
+
+ // Move assignment
+ Tuple<UniquePtr<int>> e{MakeUnique<int>(0)};
+ Tuple<UniquePtr<int>> f{MakeUnique<int>(42)};
+ e = Move(f);
+ CHECK(*Get<0>(e) == 42);
+ CHECK(Get<0>(f) == nullptr);
+}
+
+static void
+TestAssignmentFromMozPair()
+{
+ // Copy assignment
+ Tuple<int, int> a{0, 0};
+ Pair<int, int> b{42, 42};
+ a = b;
+ CHECK(Get<0>(a) == 42);
+ CHECK(Get<1>(a) == 42);
+
+ // Assignment to reference member
+ int i = 0;
+ int j = 0;
+ int k = 42;
+ Tuple<int&, int&> c{i, j};
+ Pair<int&, int&> d{k, k};
+ c = d;
+ CHECK(i == 42);
+ CHECK(j == 42);
+
+ // Move assignment
+ Tuple<UniquePtr<int>, UniquePtr<int>> e{MakeUnique<int>(0),
+ MakeUnique<int>(0)};
+ Pair<UniquePtr<int>, UniquePtr<int>> f{MakeUnique<int>(42),
+ MakeUnique<int>(42)};
+ e = Move(f);
+ CHECK(*Get<0>(e) == 42);
+ CHECK(*Get<1>(e) == 42);
+ CHECK(f.first() == nullptr);
+ CHECK(f.second() == nullptr);
+}
+
+static void
+TestAssignmentFromStdPair()
+{
+ // Copy assignment
+ Tuple<int, int> a{0, 0};
+ pair<int, int> b{42, 42};
+ a = b;
+ CHECK(Get<0>(a) == 42);
+ CHECK(Get<1>(a) == 42);
+
+ // Assignment to reference member
+ int i = 0;
+ int j = 0;
+ int k = 42;
+ Tuple<int&, int&> c{i, j};
+ pair<int&, int&> d{k, k};
+ c = d;
+ CHECK(i == 42);
+ CHECK(j == 42);
+
+ // Move assignment.
+ Tuple<UniquePtr<int>, UniquePtr<int>> e{MakeUnique<int>(0), MakeUnique<int>(0)};
+ // XXX: On some platforms std::pair doesn't support move constructor.
+ pair<UniquePtr<int>, UniquePtr<int>> f;
+ f.first = MakeUnique<int>(42);
+ f.second = MakeUnique<int>(42);
+
+ e = Move(f);
+ CHECK(*Get<0>(e) == 42);
+ CHECK(*Get<1>(e) == 42);
+ CHECK(f.first == nullptr);
+ CHECK(f.second == nullptr);
+}
+
+static void
+TestGet()
+{
+ int x = 1;
+ int y = 2;
+ int z = 3;
+ Tuple<int, int&, const int&> tuple(x, y, z);
+
+ // Using Get<>() to read elements
+ CHECK(Get<0>(tuple) == 1);
+ CHECK(Get<1>(tuple) == 2);
+ CHECK(Get<2>(tuple) == 3);
+
+ // Using Get<>() to write to elements
+ Get<0>(tuple) = 41;
+ CHECK(Get<0>(tuple) == 41);
+
+ // Writing through reference elements
+ Get<1>(tuple) = 42;
+ CHECK(Get<1>(tuple) == 42);
+ CHECK(y == 42);
+}
+
+static void
+TestMakeTuple()
+{
+ auto tuple = MakeTuple(42, 0.5f, 'c');
+ CHECK_TYPE(tuple, Tuple<int, float, char>);
+ CHECK(Get<0>(tuple) == 42);
+ CHECK(Get<1>(tuple) == 0.5f);
+ CHECK(Get<2>(tuple) == 'c');
+
+ // Make sure we don't infer the type to be Tuple<int&>.
+ int x = 1;
+ auto tuple2 = MakeTuple(x);
+ CHECK_TYPE(tuple2, Tuple<int>);
+ x = 2;
+ CHECK(Get<0>(tuple2) == 1);
+}
+
+static bool
+TestTie()
+{
+ int i;
+ float f;
+ char c;
+ Tuple<int, float, char> rhs1(42, 0.5f, 'c');
+ Tie(i, f, c) = rhs1;
+ CHECK(i == Get<0>(rhs1));
+ CHECK(f == Get<1>(rhs1));
+ CHECK(c == Get<2>(rhs1));
+ // Test conversions
+ Tuple<ConvertibleToInt, double, unsigned char> rhs2(ConvertibleToInt(),
+ 0.7f, 'd');
+ Tie(i, f, c) = rhs2;
+ CHECK(i == Get<0>(rhs2));
+ CHECK(f == Get<1>(rhs2));
+ CHECK(c == Get<2>(rhs2));
+
+ // Test Pair
+ Pair<int, float> rhs3(-1, 1.2f);
+ Tie(i, f) = rhs3;
+ CHECK(i == rhs3.first());
+ CHECK(f == rhs3.second());
+
+ pair<int, float> rhs4(42, 1.5f);
+ Tie(i, f) = rhs4;
+ CHECK(i == rhs4.first);
+ CHECK(f == rhs4.second);
+
+ return true;
+}
+
+int
+main()
+{
+ TestConstruction();
+ TestConstructionFromMozPair();
+ TestConstructionFromStdPair();
+ TestAssignment();
+ TestAssignmentFromMozPair();
+ TestAssignmentFromStdPair();
+ TestGet();
+ TestMakeTuple();
+ TestTie();
+ return 0;
+}
diff --git a/mfbt/tests/TestTypeTraits.cpp b/mfbt/tests/TestTypeTraits.cpp
new file mode 100644
index 000000000..f0a565142
--- /dev/null
+++ b/mfbt/tests/TestTypeTraits.cpp
@@ -0,0 +1,660 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/TypeTraits.h"
+
+#define TEST_CV_QUALIFIERS(test, type, ...) \
+ test(type, __VA_ARGS__) \
+ test(const type, __VA_ARGS__) \
+ test(volatile type, __VA_ARGS__) \
+ test(const volatile type, __VA_ARGS__)
+
+using mozilla::AddLvalueReference;
+using mozilla::AddPointer;
+using mozilla::AddRvalueReference;
+using mozilla::Decay;
+using mozilla::DeclVal;
+using mozilla::IsFunction;
+using mozilla::IsArray;
+using mozilla::IsBaseOf;
+using mozilla::IsClass;
+using mozilla::IsConvertible;
+using mozilla::IsEmpty;
+using mozilla::IsLvalueReference;
+using mozilla::IsPointer;
+using mozilla::IsReference;
+using mozilla::IsRvalueReference;
+using mozilla::IsSame;
+using mozilla::IsSigned;
+using mozilla::IsUnsigned;
+using mozilla::IsDestructible;
+using mozilla::MakeSigned;
+using mozilla::MakeUnsigned;
+using mozilla::RemoveExtent;
+using mozilla::RemovePointer;
+
+static_assert(!IsFunction<int>::value,
+ "int is not a function type");
+static_assert(IsFunction<void(int)>::value,
+ "void(int) is a function type");
+static_assert(!IsFunction<void(*)(int)>::value,
+ "void(*)(int) is not a function type");
+
+static_assert(!IsArray<bool>::value,
+ "bool not an array");
+static_assert(IsArray<bool[]>::value,
+ "bool[] is an array");
+static_assert(IsArray<bool[5]>::value,
+ "bool[5] is an array");
+
+static_assert(!IsPointer<bool>::value,
+ "bool not a pointer");
+static_assert(IsPointer<bool*>::value,
+ "bool* is a pointer");
+static_assert(IsPointer<bool* const>::value,
+ "bool* const is a pointer");
+static_assert(IsPointer<bool* volatile>::value,
+ "bool* volatile is a pointer");
+static_assert(IsPointer<bool* const volatile>::value,
+ "bool* const volatile is a pointer");
+static_assert(IsPointer<bool**>::value,
+ "bool** is a pointer");
+static_assert(IsPointer<void (*)(void)>::value,
+ "void (*)(void) is a pointer");
+struct IsPointerTest { bool m; void f(); };
+static_assert(!IsPointer<IsPointerTest>::value,
+ "IsPointerTest not a pointer");
+static_assert(IsPointer<IsPointerTest*>::value,
+ "IsPointerTest* is a pointer");
+static_assert(!IsPointer<bool(IsPointerTest::*)>::value,
+ "bool(IsPointerTest::*) not a pointer");
+static_assert(!IsPointer<void(IsPointerTest::*)(void)>::value,
+ "void(IsPointerTest::*)(void) not a pointer");
+
+static_assert(!IsLvalueReference<bool>::value,
+ "bool not an lvalue reference");
+static_assert(!IsLvalueReference<bool*>::value,
+ "bool* not an lvalue reference");
+static_assert(IsLvalueReference<bool&>::value,
+ "bool& is an lvalue reference");
+static_assert(!IsLvalueReference<bool&&>::value,
+ "bool&& not an lvalue reference");
+
+static_assert(!IsLvalueReference<void>::value,
+ "void not an lvalue reference");
+static_assert(!IsLvalueReference<void*>::value,
+ "void* not an lvalue reference");
+
+static_assert(!IsLvalueReference<int>::value,
+ "int not an lvalue reference");
+static_assert(!IsLvalueReference<int*>::value,
+ "int* not an lvalue reference");
+static_assert(IsLvalueReference<int&>::value,
+ "int& is an lvalue reference");
+static_assert(!IsLvalueReference<int&&>::value,
+ "int&& not an lvalue reference");
+
+static_assert(!IsRvalueReference<bool>::value,
+ "bool not an rvalue reference");
+static_assert(!IsRvalueReference<bool*>::value,
+ "bool* not an rvalue reference");
+static_assert(!IsRvalueReference<bool&>::value,
+ "bool& not an rvalue reference");
+static_assert(IsRvalueReference<bool&&>::value,
+ "bool&& is an rvalue reference");
+
+static_assert(!IsRvalueReference<void>::value,
+ "void not an rvalue reference");
+static_assert(!IsRvalueReference<void*>::value,
+ "void* not an rvalue reference");
+
+static_assert(!IsRvalueReference<int>::value,
+ "int not an rvalue reference");
+static_assert(!IsRvalueReference<int*>::value,
+ "int* not an rvalue reference");
+static_assert(!IsRvalueReference<int&>::value,
+ "int& not an rvalue reference");
+static_assert(IsRvalueReference<int&&>::value,
+ "int&& is an rvalue reference");
+
+static_assert(!IsReference<bool>::value,
+ "bool not a reference");
+static_assert(!IsReference<bool*>::value,
+ "bool* not a reference");
+static_assert(IsReference<bool&>::value,
+ "bool& is a reference");
+static_assert(IsReference<bool&&>::value,
+ "bool&& is a reference");
+
+static_assert(!IsReference<void>::value,
+ "void not a reference");
+static_assert(!IsReference<void*>::value,
+ "void* not a reference");
+
+static_assert(!IsReference<int>::value,
+ "int not a reference");
+static_assert(!IsReference<int*>::value,
+ "int* not a reference");
+static_assert(IsReference<int&>::value,
+ "int& is a reference");
+static_assert(IsReference<int&&>::value,
+ "int&& is a reference");
+
+namespace CPlusPlus11IsMemberPointer {
+
+using mozilla::IsMemberPointer;
+
+struct S {};
+union U {};
+
+#define ASSERT_IS_MEMBER_POINTER(type, msg) \
+ static_assert(IsMemberPointer<type>::value, #type msg);
+#define TEST_IS_MEMBER_POINTER(type) \
+ TEST_CV_QUALIFIERS(ASSERT_IS_MEMBER_POINTER, type, \
+ " is a member pointer type")
+
+TEST_IS_MEMBER_POINTER(int S::*)
+TEST_IS_MEMBER_POINTER(int U::*)
+
+#undef TEST_IS_MEMBER_POINTER
+#undef ASSERT_IS_MEMBER_POINTER
+
+#define ASSERT_IS_NOT_MEMBER_POINTER(type, msg) \
+ static_assert(!IsMemberPointer<type>::value, #type msg);
+#define TEST_IS_NOT_MEMBER_POINTER(type) \
+ TEST_CV_QUALIFIERS(ASSERT_IS_NOT_MEMBER_POINTER, type, \
+ " is not a member pointer type")
+
+TEST_IS_NOT_MEMBER_POINTER(int*)
+
+#undef TEST_IS_NOT_MEMBER_POINTER
+#undef ASSERT_IS_NOT_MEMBER_POINTER
+
+} // CPlusPlus11IsMemberPointer
+
+namespace CPlusPlus11IsScalar {
+
+using mozilla::IsScalar;
+
+enum E {};
+enum class EC {};
+class C {};
+struct S {};
+union U {};
+
+#define ASSERT_IS_SCALAR(type, msg) \
+ static_assert(IsScalar<type>::value, #type msg);
+#define TEST_IS_SCALAR(type) \
+ TEST_CV_QUALIFIERS(ASSERT_IS_SCALAR, type, " is a scalar type")
+
+TEST_IS_SCALAR(int)
+TEST_IS_SCALAR(float)
+TEST_IS_SCALAR(E)
+TEST_IS_SCALAR(EC)
+TEST_IS_SCALAR(S*)
+TEST_IS_SCALAR(int S::*)
+
+#undef TEST_IS_SCALAR
+#undef ASSERT_IS_SCALAR
+
+#define ASSERT_IS_NOT_SCALAR(type, msg) \
+ static_assert(!IsScalar<type>::value, #type msg);
+#define TEST_IS_NOT_SCALAR(type) \
+ TEST_CV_QUALIFIERS(ASSERT_IS_NOT_SCALAR, type, " is not a scalar type")
+
+TEST_IS_NOT_SCALAR(C)
+TEST_IS_NOT_SCALAR(S)
+TEST_IS_NOT_SCALAR(U)
+
+#undef TEST_IS_NOT_SCALAR
+#undef ASSERT_IS_NOT_SCALAR
+
+} // CPlusPlus11IsScalar
+
+struct S1 {};
+union U1 { int mX; };
+
+static_assert(!IsClass<int>::value,
+ "int isn't a class");
+static_assert(IsClass<const S1>::value,
+ "S is a class");
+static_assert(!IsClass<U1>::value,
+ "U isn't a class");
+
+static_assert(!mozilla::IsEmpty<int>::value,
+ "not a class => not empty");
+static_assert(!mozilla::IsEmpty<bool[5]>::value,
+ "not a class => not empty");
+
+static_assert(!mozilla::IsEmpty<U1>::value,
+ "not a class => not empty");
+
+struct E1 {};
+struct E2 { int : 0; };
+struct E3 : E1 {};
+struct E4 : E2 {};
+
+static_assert(IsEmpty<const volatile S1>::value,
+ "S should be empty");
+
+static_assert(mozilla::IsEmpty<E1>::value &&
+ mozilla::IsEmpty<E2>::value &&
+ mozilla::IsEmpty<E3>::value &&
+ mozilla::IsEmpty<E4>::value,
+ "all empty");
+
+union U2 { E1 e1; };
+static_assert(!mozilla::IsEmpty<U2>::value,
+ "not a class => not empty");
+
+struct NE1 { int mX; };
+struct NE2 : virtual E1 {};
+struct NE3 : E2 { virtual ~NE3() {} };
+struct NE4 { virtual void f() {} };
+
+static_assert(!mozilla::IsEmpty<NE1>::value &&
+ !mozilla::IsEmpty<NE2>::value &&
+ !mozilla::IsEmpty<NE3>::value &&
+ !mozilla::IsEmpty<NE4>::value,
+ "all empty");
+
+static_assert(!IsSigned<bool>::value,
+ "bool shouldn't be signed");
+static_assert(IsUnsigned<bool>::value,
+ "bool should be unsigned");
+
+static_assert(!IsSigned<const bool>::value,
+ "const bool shouldn't be signed");
+static_assert(IsUnsigned<const bool>::value,
+ "const bool should be unsigned");
+
+static_assert(!IsSigned<volatile bool>::value,
+ "volatile bool shouldn't be signed");
+static_assert(IsUnsigned<volatile bool>::value,
+ "volatile bool should be unsigned");
+
+static_assert(!IsSigned<unsigned char>::value,
+ "unsigned char shouldn't be signed");
+static_assert(IsUnsigned<unsigned char>::value,
+ "unsigned char should be unsigned");
+static_assert(IsSigned<signed char>::value,
+ "signed char should be signed");
+static_assert(!IsUnsigned<signed char>::value,
+ "signed char shouldn't be unsigned");
+
+static_assert(!IsSigned<unsigned short>::value,
+ "unsigned short shouldn't be signed");
+static_assert(IsUnsigned<unsigned short>::value,
+ "unsigned short should be unsigned");
+static_assert(IsSigned<short>::value,
+ "short should be signed");
+static_assert(!IsUnsigned<short>::value,
+ "short shouldn't be unsigned");
+
+static_assert(!IsSigned<unsigned int>::value,
+ "unsigned int shouldn't be signed");
+static_assert(IsUnsigned<unsigned int>::value,
+ "unsigned int should be unsigned");
+static_assert(IsSigned<int>::value,
+ "int should be signed");
+static_assert(!IsUnsigned<int>::value,
+ "int shouldn't be unsigned");
+
+static_assert(!IsSigned<unsigned long>::value,
+ "unsigned long shouldn't be signed");
+static_assert(IsUnsigned<unsigned long>::value,
+ "unsigned long should be unsigned");
+static_assert(IsSigned<long>::value,
+ "long should be signed");
+static_assert(!IsUnsigned<long>::value,
+ "long shouldn't be unsigned");
+
+static_assert(IsSigned<float>::value,
+ "float should be signed");
+static_assert(!IsUnsigned<float>::value,
+ "float shouldn't be unsigned");
+
+static_assert(IsSigned<const float>::value,
+ "const float should be signed");
+static_assert(!IsUnsigned<const float>::value,
+ "const float shouldn't be unsigned");
+
+static_assert(IsSigned<double>::value,
+ "double should be signed");
+static_assert(!IsUnsigned<double>::value,
+ "double shouldn't be unsigned");
+
+static_assert(IsSigned<volatile double>::value,
+ "volatile double should be signed");
+static_assert(!IsUnsigned<volatile double>::value,
+ "volatile double shouldn't be unsigned");
+
+static_assert(IsSigned<long double>::value,
+ "long double should be signed");
+static_assert(!IsUnsigned<long double>::value,
+ "long double shouldn't be unsigned");
+
+static_assert(IsSigned<const volatile long double>::value,
+ "const volatile long double should be signed");
+static_assert(!IsUnsigned<const volatile long double>::value,
+ "const volatile long double shouldn't be unsigned");
+
+class NotIntConstructible
+{
+ NotIntConstructible(int) = delete;
+};
+
+static_assert(!IsSigned<NotIntConstructible>::value,
+ "non-arithmetic types are not signed");
+static_assert(!IsUnsigned<NotIntConstructible>::value,
+ "non-arithmetic types are not unsigned");
+
+class PublicDestructible
+{
+public:
+ ~PublicDestructible();
+};
+class PrivateDestructible
+{
+private:
+ ~PrivateDestructible();
+};
+class TrivialDestructible
+{
+};
+
+static_assert(IsDestructible<PublicDestructible>::value,
+ "public destructible class is destructible");
+static_assert(!IsDestructible<PrivateDestructible>::value,
+ "private destructible class is not destructible");
+static_assert(IsDestructible<TrivialDestructible>::value,
+ "trivial destructible class is destructible");
+
+namespace CPlusPlus11IsBaseOf {
+
+// Adapted from C++11 § 20.9.6.
+struct B {};
+struct B1 : B {};
+struct B2 : B {};
+struct D : private B1, private B2 {};
+
+static void
+StandardIsBaseOfTests()
+{
+ static_assert((IsBaseOf<B, D>::value) == true,
+ "IsBaseOf fails on diamond");
+ static_assert((IsBaseOf<const B, D>::value) == true,
+ "IsBaseOf fails on diamond plus constness change");
+ static_assert((IsBaseOf<B, const D>::value) == true,
+ "IsBaseOf fails on diamond plus constness change");
+ static_assert((IsBaseOf<B, const B>::value) == true,
+ "IsBaseOf fails on constness change");
+ static_assert((IsBaseOf<D, B>::value) == false,
+ "IsBaseOf got the direction of inheritance wrong");
+ static_assert((IsBaseOf<B&, D&>::value) == false,
+ "IsBaseOf should return false on references");
+ static_assert((IsBaseOf<B[3], D[3]>::value) == false,
+ "IsBaseOf should return false on arrays");
+ // We fail at the following test. To fix it, we need to specialize IsBaseOf
+ // for all built-in types.
+ // static_assert((IsBaseOf<int, int>::value) == false);
+}
+
+} /* namespace CPlusPlus11IsBaseOf */
+
+class A { };
+class B : public A { };
+class C : private A { };
+class D { };
+class E : public A { };
+class F : public B, public E { };
+
+static void
+TestIsBaseOf()
+{
+ static_assert((IsBaseOf<A, B>::value),
+ "A is a base of B");
+ static_assert((!IsBaseOf<B, A>::value),
+ "B is not a base of A");
+ static_assert((IsBaseOf<A, C>::value),
+ "A is a base of C");
+ static_assert((!IsBaseOf<C, A>::value),
+ "C is not a base of A");
+ static_assert((IsBaseOf<A, F>::value),
+ "A is a base of F");
+ static_assert((!IsBaseOf<F, A>::value),
+ "F is not a base of A");
+ static_assert((!IsBaseOf<A, D>::value),
+ "A is not a base of D");
+ static_assert((!IsBaseOf<D, A>::value),
+ "D is not a base of A");
+ static_assert((IsBaseOf<B, B>::value),
+ "B is the same as B (and therefore, a base of B)");
+}
+
+class ExplicitCopyConstructor {
+ explicit ExplicitCopyConstructor(const ExplicitCopyConstructor&) = default;
+};
+
+static void
+TestIsConvertible()
+{
+ // Pointer type convertibility
+ static_assert((IsConvertible<A*, A*>::value),
+ "A* should convert to A*");
+ static_assert((IsConvertible<B*, A*>::value),
+ "B* should convert to A*");
+ static_assert((!IsConvertible<A*, B*>::value),
+ "A* shouldn't convert to B*");
+ static_assert((!IsConvertible<A*, C*>::value),
+ "A* shouldn't convert to C*");
+ static_assert((!IsConvertible<A*, D*>::value),
+ "A* shouldn't convert to unrelated D*");
+ static_assert((!IsConvertible<D*, A*>::value),
+ "D* shouldn't convert to unrelated A*");
+
+ // Instance type convertibility
+ static_assert((IsConvertible<A, A>::value),
+ "A is A");
+ static_assert((IsConvertible<B, A>::value),
+ "B converts to A");
+ static_assert((!IsConvertible<D, A>::value),
+ "D and A are unrelated");
+ static_assert((!IsConvertible<A, D>::value),
+ "A and D are unrelated");
+
+ static_assert(IsConvertible<void, void>::value, "void is void");
+ static_assert(!IsConvertible<A, void>::value, "A shouldn't convert to void");
+ static_assert(!IsConvertible<void, B>::value, "void shouldn't convert to B");
+
+ static_assert(!IsConvertible<const ExplicitCopyConstructor&,
+ ExplicitCopyConstructor>::value,
+ "IsConvertible should test for implicit convertibility");
+
+ // These cases seem to require C++11 support to properly implement them, so
+ // for now just disable them.
+ //static_assert((!IsConvertible<C*, A*>::value),
+ // "C* shouldn't convert to A* (private inheritance)");
+ //static_assert((!IsConvertible<C, A>::value),
+ // "C doesn't convert to A (private inheritance)");
+}
+
+static_assert(IsSame<AddLvalueReference<int>::Type, int&>::value,
+ "not adding & to int correctly");
+static_assert(IsSame<AddLvalueReference<volatile int&>::Type, volatile int&>::value,
+ "not adding & to volatile int& correctly");
+static_assert(IsSame<AddLvalueReference<void*>::Type, void*&>::value,
+ "not adding & to void* correctly");
+static_assert(IsSame<AddLvalueReference<void>::Type, void>::value,
+ "void shouldn't be transformed by AddLvalueReference");
+static_assert(IsSame<AddLvalueReference<struct S1&&>::Type, struct S1&>::value,
+ "not reference-collapsing struct S1&& & to struct S1& correctly");
+
+static_assert(IsSame<AddRvalueReference<int>::Type, int&&>::value,
+ "not adding && to int correctly");
+static_assert(IsSame<AddRvalueReference<volatile int&>::Type, volatile int&>::value,
+ "not adding && to volatile int& correctly");
+static_assert(IsSame<AddRvalueReference<const int&&>::Type, const int&&>::value,
+ "not adding && to volatile int& correctly");
+static_assert(IsSame<AddRvalueReference<void*>::Type, void*&&>::value,
+ "not adding && to void* correctly");
+static_assert(IsSame<AddRvalueReference<void>::Type, void>::value,
+ "void shouldn't be transformed by AddRvalueReference");
+static_assert(IsSame<AddRvalueReference<struct S1&>::Type, struct S1&>::value,
+ "not reference-collapsing struct S1& && to struct S1& correctly");
+
+struct TestWithDefaultConstructor
+{
+ int foo() const { return 0; }
+};
+struct TestWithNoDefaultConstructor
+{
+ explicit TestWithNoDefaultConstructor(int) {}
+ int foo() const { return 1; }
+};
+
+static_assert(IsSame<decltype(TestWithDefaultConstructor().foo()), int>::value,
+ "decltype should work using a struct with a default constructor");
+static_assert(IsSame<decltype(DeclVal<TestWithDefaultConstructor>().foo()), int>::value,
+ "decltype should work using a DeclVal'd struct with a default constructor");
+static_assert(IsSame<decltype(DeclVal<TestWithNoDefaultConstructor>().foo()), int>::value,
+ "decltype should work using a DeclVal'd struct without a default constructor");
+
+static_assert(IsSame<MakeSigned<const unsigned char>::Type, const signed char>::value,
+ "const unsigned char won't signify correctly");
+static_assert(IsSame<MakeSigned<volatile unsigned short>::Type, volatile signed short>::value,
+ "volatile unsigned short won't signify correctly");
+static_assert(IsSame<MakeSigned<const volatile unsigned int>::Type, const volatile signed int>::value,
+ "const volatile unsigned int won't signify correctly");
+static_assert(IsSame<MakeSigned<unsigned long>::Type, signed long>::value,
+ "unsigned long won't signify correctly");
+static_assert(IsSame<MakeSigned<const signed char>::Type, const signed char>::value,
+ "const signed char won't signify correctly");
+
+static_assert(IsSame<MakeSigned<volatile signed short>::Type, volatile signed short>::value,
+ "volatile signed short won't signify correctly");
+static_assert(IsSame<MakeSigned<const volatile signed int>::Type, const volatile signed int>::value,
+ "const volatile signed int won't signify correctly");
+static_assert(IsSame<MakeSigned<signed long>::Type, signed long>::value,
+ "signed long won't signify correctly");
+
+static_assert(IsSame<MakeSigned<char>::Type, signed char>::value,
+ "char won't signify correctly");
+static_assert(IsSame<MakeSigned<volatile char>::Type, volatile signed char>::value,
+ "volatile char won't signify correctly");
+static_assert(IsSame<MakeSigned<const char>::Type, const signed char>::value,
+ "const char won't signify correctly");
+
+static_assert(IsSame<MakeUnsigned<const signed char>::Type, const unsigned char>::value,
+ "const signed char won't unsignify correctly");
+static_assert(IsSame<MakeUnsigned<volatile signed short>::Type, volatile unsigned short>::value,
+ "volatile signed short won't unsignify correctly");
+static_assert(IsSame<MakeUnsigned<const volatile signed int>::Type, const volatile unsigned int>::value,
+ "const volatile signed int won't unsignify correctly");
+static_assert(IsSame<MakeUnsigned<signed long>::Type, unsigned long>::value,
+ "signed long won't unsignify correctly");
+
+static_assert(IsSame<MakeUnsigned<const unsigned char>::Type, const unsigned char>::value,
+ "const unsigned char won't unsignify correctly");
+
+static_assert(IsSame<MakeUnsigned<volatile unsigned short>::Type, volatile unsigned short>::value,
+ "volatile unsigned short won't unsignify correctly");
+static_assert(IsSame<MakeUnsigned<const volatile unsigned int>::Type, const volatile unsigned int>::value,
+ "const volatile unsigned int won't unsignify correctly");
+static_assert(IsSame<MakeUnsigned<unsigned long>::Type, unsigned long>::value,
+ "signed long won't unsignify correctly");
+
+static_assert(IsSame<MakeUnsigned<char>::Type, unsigned char>::value,
+ "char won't unsignify correctly");
+static_assert(IsSame<MakeUnsigned<volatile char>::Type, volatile unsigned char>::value,
+ "volatile char won't unsignify correctly");
+static_assert(IsSame<MakeUnsigned<const char>::Type, const unsigned char>::value,
+ "const char won't unsignify correctly");
+
+static_assert(IsSame<RemoveExtent<int>::Type, int>::value,
+ "removing extent from non-array must return the non-array");
+static_assert(IsSame<RemoveExtent<const int[]>::Type, const int>::value,
+ "removing extent from unknown-bound array must return element type");
+static_assert(IsSame<RemoveExtent<volatile int[5]>::Type, volatile int>::value,
+ "removing extent from known-bound array must return element type");
+static_assert(IsSame<RemoveExtent<long[][17]>::Type, long[17]>::value,
+ "removing extent from multidimensional array must return element type");
+
+struct TestRemovePointer { bool m; void f(); };
+static_assert(IsSame<RemovePointer<int>::Type, int>::value,
+ "removing pointer from int must return int");
+static_assert(IsSame<RemovePointer<int*>::Type, int>::value,
+ "removing pointer from int* must return int");
+static_assert(IsSame<RemovePointer<int* const>::Type, int>::value,
+ "removing pointer from int* const must return int");
+static_assert(IsSame<RemovePointer<int* volatile>::Type, int>::value,
+ "removing pointer from int* volatile must return int");
+static_assert(IsSame<RemovePointer<const long*>::Type, const long>::value,
+ "removing pointer from const long* must return const long");
+static_assert(IsSame<RemovePointer<void* const>::Type, void>::value,
+ "removing pointer from void* const must return void");
+static_assert(IsSame<RemovePointer<void (TestRemovePointer::*)()>::Type,
+ void (TestRemovePointer::*)()>::value,
+ "removing pointer from void (S::*)() must return void (S::*)()");
+static_assert(IsSame<RemovePointer<void (*)()>::Type, void()>::value,
+ "removing pointer from void (*)() must return void()");
+static_assert(IsSame<RemovePointer<bool TestRemovePointer::*>::Type,
+ bool TestRemovePointer::*>::value,
+ "removing pointer from bool S::* must return bool S::*");
+
+static_assert(IsSame<AddPointer<int>::Type, int*>::value,
+ "adding pointer to int must return int*");
+static_assert(IsSame<AddPointer<int*>::Type, int**>::value,
+ "adding pointer to int* must return int**");
+static_assert(IsSame<AddPointer<int&>::Type, int*>::value,
+ "adding pointer to int& must return int*");
+static_assert(IsSame<AddPointer<int* const>::Type, int* const*>::value,
+ "adding pointer to int* const must return int* const*");
+static_assert(IsSame<AddPointer<int* volatile>::Type, int* volatile*>::value,
+ "adding pointer to int* volatile must return int* volatile*");
+
+static_assert(IsSame<Decay<int>::Type, int>::value,
+ "decaying int must return int");
+static_assert(IsSame<Decay<int*>::Type, int*>::value,
+ "decaying int* must return int*");
+static_assert(IsSame<Decay<int* const>::Type, int*>::value,
+ "decaying int* const must return int*");
+static_assert(IsSame<Decay<int* volatile>::Type, int*>::value,
+ "decaying int* volatile must return int*");
+static_assert(IsSame<Decay<int&>::Type, int>::value,
+ "decaying int& must return int");
+static_assert(IsSame<Decay<const int&>::Type, int>::value,
+ "decaying const int& must return int");
+static_assert(IsSame<Decay<int&&>::Type, int>::value,
+ "decaying int&& must return int");
+static_assert(IsSame<Decay<int[1]>::Type, int*>::value,
+ "decaying int[1] must return int*");
+static_assert(IsSame<Decay<void(int)>::Type, void(*)(int)>::value,
+ "decaying void(int) must return void(*)(int)");
+
+/*
+ * Android's broken [u]intptr_t inttype macros are broken because its PRI*PTR
+ * macros are defined as "ld", but sizeof(long) is 8 and sizeof(intptr_t)
+ * is 4 on 32-bit Android. We redefine Android's PRI*PTR macros in
+ * IntegerPrintfMacros.h and assert here that our new definitions match the
+ * actual type sizes seen at compile time.
+ */
+#if defined(ANDROID) && !defined(__LP64__)
+static_assert(mozilla::IsSame<int, intptr_t>::value,
+ "emulated PRI[di]PTR definitions will be wrong");
+static_assert(mozilla::IsSame<unsigned int, uintptr_t>::value,
+ "emulated PRI[ouxX]PTR definitions will be wrong");
+#endif
+
+int
+main()
+{
+ CPlusPlus11IsBaseOf::StandardIsBaseOfTests();
+ TestIsBaseOf();
+ TestIsConvertible();
+ return 0;
+}
diff --git a/mfbt/tests/TestTypedEnum.cpp b/mfbt/tests/TestTypedEnum.cpp
new file mode 100644
index 000000000..43c36f2b8
--- /dev/null
+++ b/mfbt/tests/TestTypedEnum.cpp
@@ -0,0 +1,556 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/TypedEnumBits.h"
+
+#include <stdint.h>
+
+// A rough feature check for is_literal_type. Not very carefully checked.
+// Feel free to amend as needed.
+// We leave ANDROID out because it's using stlport which doesn't have std::is_literal_type.
+#if __cplusplus >= 201103L && !defined(ANDROID)
+# if defined(__clang__)
+ /*
+ * Per Clang documentation, "Note that marketing version numbers should not
+ * be used to check for language features, as different vendors use different
+ * numbering schemes. Instead, use the feature checking macros."
+ */
+# ifndef __has_extension
+# define __has_extension __has_feature /* compatibility, for older versions of clang */
+# endif
+# if __has_extension(is_literal) && __has_include(<type_traits>)
+# define MOZ_HAVE_IS_LITERAL
+# endif
+# elif defined(__GNUC__) || defined(_MSC_VER)
+# define MOZ_HAVE_IS_LITERAL
+# endif
+#endif
+
+#if defined(MOZ_HAVE_IS_LITERAL) && defined(MOZ_HAVE_CXX11_CONSTEXPR)
+#include <type_traits>
+template<typename T>
+void
+RequireLiteralType()
+{
+ static_assert(std::is_literal_type<T>::value, "Expected a literal type");
+}
+#else // not MOZ_HAVE_IS_LITERAL
+template<typename T>
+void
+RequireLiteralType()
+{
+}
+#endif
+
+template<typename T>
+void
+RequireLiteralType(const T&)
+{
+ RequireLiteralType<T>();
+}
+
+enum class AutoEnum {
+ A,
+ B = -3,
+ C
+};
+
+enum class CharEnum : char {
+ A,
+ B = 3,
+ C
+};
+
+enum class AutoEnumBitField {
+ A = 0x10,
+ B = 0x20,
+ C
+};
+
+enum class CharEnumBitField : char {
+ A = 0x10,
+ B,
+ C = 0x40
+};
+
+struct Nested
+{
+ enum class AutoEnum {
+ A,
+ B,
+ C = -1
+ };
+
+ enum class CharEnum : char {
+ A = 4,
+ B,
+ C = 1
+ };
+
+ enum class AutoEnumBitField {
+ A,
+ B = 0x20,
+ C
+ };
+
+ enum class CharEnumBitField : char {
+ A = 1,
+ B = 1,
+ C = 1
+ };
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(AutoEnumBitField)
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CharEnumBitField)
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Nested::AutoEnumBitField)
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Nested::CharEnumBitField)
+
+#define MAKE_STANDARD_BITFIELD_FOR_TYPE(IntType) \
+ enum class BitFieldFor_##IntType : IntType { \
+ A = 1, \
+ B = 2, \
+ C = 4, \
+ }; \
+ MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(BitFieldFor_##IntType)
+
+MAKE_STANDARD_BITFIELD_FOR_TYPE(int8_t)
+MAKE_STANDARD_BITFIELD_FOR_TYPE(uint8_t)
+MAKE_STANDARD_BITFIELD_FOR_TYPE(int16_t)
+MAKE_STANDARD_BITFIELD_FOR_TYPE(uint16_t)
+MAKE_STANDARD_BITFIELD_FOR_TYPE(int32_t)
+MAKE_STANDARD_BITFIELD_FOR_TYPE(uint32_t)
+MAKE_STANDARD_BITFIELD_FOR_TYPE(int64_t)
+MAKE_STANDARD_BITFIELD_FOR_TYPE(uint64_t)
+MAKE_STANDARD_BITFIELD_FOR_TYPE(char)
+typedef signed char signed_char;
+MAKE_STANDARD_BITFIELD_FOR_TYPE(signed_char)
+typedef unsigned char unsigned_char;
+MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_char)
+MAKE_STANDARD_BITFIELD_FOR_TYPE(short)
+typedef unsigned short unsigned_short;
+MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_short)
+MAKE_STANDARD_BITFIELD_FOR_TYPE(int)
+typedef unsigned int unsigned_int;
+MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_int)
+MAKE_STANDARD_BITFIELD_FOR_TYPE(long)
+typedef unsigned long unsigned_long;
+MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_long)
+typedef long long long_long;
+MAKE_STANDARD_BITFIELD_FOR_TYPE(long_long)
+typedef unsigned long long unsigned_long_long;
+MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_long_long)
+
+#undef MAKE_STANDARD_BITFIELD_FOR_TYPE
+
+template<typename T>
+void
+TestNonConvertibilityForOneType()
+{
+ using mozilla::IsConvertible;
+
+ static_assert(!IsConvertible<T, bool>::value, "should not be convertible");
+ static_assert(!IsConvertible<T, int>::value, "should not be convertible");
+ static_assert(!IsConvertible<T, uint64_t>::value, "should not be convertible");
+
+ static_assert(!IsConvertible<bool, T>::value, "should not be convertible");
+ static_assert(!IsConvertible<int, T>::value, "should not be convertible");
+ static_assert(!IsConvertible<uint64_t, T>::value, "should not be convertible");
+}
+
+template<typename TypedEnum>
+void
+TestTypedEnumBasics()
+{
+ const TypedEnum a = TypedEnum::A;
+ int unused = int(a);
+ (void) unused;
+ RequireLiteralType(TypedEnum::A);
+ RequireLiteralType(a);
+ TestNonConvertibilityForOneType<TypedEnum>();
+}
+
+// Op wraps a bitwise binary operator, passed as a char template parameter,
+// and applies it to its arguments (aT1, aT2). For example,
+//
+// Op<'|'>(aT1, aT2)
+//
+// is the same as
+//
+// aT1 | aT2.
+//
+template<char o, typename T1, typename T2>
+auto Op(const T1& aT1, const T2& aT2)
+ -> decltype(aT1 | aT2) // See the static_assert's below --- the return type
+ // depends solely on the operands type, not on the
+ // choice of operation.
+{
+ using mozilla::IsSame;
+ static_assert(IsSame<decltype(aT1 | aT2), decltype(aT1 & aT2)>::value,
+ "binary ops should have the same result type");
+ static_assert(IsSame<decltype(aT1 | aT2), decltype(aT1 ^ aT2)>::value,
+ "binary ops should have the same result type");
+
+ static_assert(o == '|' ||
+ o == '&' ||
+ o == '^', "unexpected operator character");
+
+ return o == '|' ? aT1 | aT2
+ : o == '&' ? aT1 & aT2
+ : aT1 ^ aT2;
+}
+
+// OpAssign wraps a bitwise binary operator, passed as a char template
+// parameter, and applies the corresponding compound-assignment operator to its
+// arguments (aT1, aT2). For example,
+//
+// OpAssign<'|'>(aT1, aT2)
+//
+// is the same as
+//
+// aT1 |= aT2.
+//
+template<char o, typename T1, typename T2>
+T1& OpAssign(T1& aT1, const T2& aT2)
+{
+ static_assert(o == '|' ||
+ o == '&' ||
+ o == '^', "unexpected operator character");
+
+ switch (o) {
+ case '|': return aT1 |= aT2;
+ case '&': return aT1 &= aT2;
+ case '^': return aT1 ^= aT2;
+ default: MOZ_CRASH();
+ }
+}
+
+// Tests a single binary bitwise operator, using a single set of three operands.
+// The operations tested are:
+//
+// result = aT1 Op aT2;
+// result Op= aT3;
+//
+// Where Op is the operator specified by the char template parameter 'o' and
+// can be any of '|', '&', '^'.
+//
+// Note that the operands aT1, aT2, aT3 are intentionally passed with free
+// types (separate template parameters for each) because their type may
+// actually be different from TypedEnum:
+//
+// 1) Their type could be CastableTypedEnumResult<TypedEnum> if they are
+// the result of a bitwise operation themselves;
+// 2) In the non-c++11 legacy path, the type of enum values is also
+// different from TypedEnum.
+//
+template<typename TypedEnum, char o, typename T1, typename T2, typename T3>
+void TestBinOp(const T1& aT1, const T2& aT2, const T3& aT3)
+{
+ typedef typename mozilla::detail::UnsignedIntegerTypeForEnum<TypedEnum>::Type
+ UnsignedIntegerType;
+
+ // Part 1:
+ // Test the bitwise binary operator i.e.
+ // result = aT1 Op aT2;
+ auto result = Op<o>(aT1, aT2);
+
+ typedef decltype(result) ResultType;
+
+ RequireLiteralType<ResultType>();
+ TestNonConvertibilityForOneType<ResultType>();
+
+ UnsignedIntegerType unsignedIntegerResult =
+ Op<o>(UnsignedIntegerType(aT1), UnsignedIntegerType(aT2));
+
+ MOZ_RELEASE_ASSERT(unsignedIntegerResult == UnsignedIntegerType(result));
+ MOZ_RELEASE_ASSERT(TypedEnum(unsignedIntegerResult) == TypedEnum(result));
+ MOZ_RELEASE_ASSERT((!unsignedIntegerResult) == (!result));
+ MOZ_RELEASE_ASSERT((!!unsignedIntegerResult) == (!!result));
+ MOZ_RELEASE_ASSERT(bool(unsignedIntegerResult) == bool(result));
+
+ // Part 2:
+ // Test the compound-assignment operator, i.e.
+ // result Op= aT3;
+ TypedEnum newResult = result;
+ OpAssign<o>(newResult, aT3);
+ UnsignedIntegerType unsignedIntegerNewResult = unsignedIntegerResult;
+ OpAssign<o>(unsignedIntegerNewResult, UnsignedIntegerType(aT3));
+ MOZ_RELEASE_ASSERT(TypedEnum(unsignedIntegerNewResult) == newResult);
+
+ // Part 3:
+ // Test additional boolean operators that we unfortunately had to add to
+ // CastableTypedEnumResult at some point to please some compiler,
+ // even though bool convertibility should have been enough.
+ MOZ_RELEASE_ASSERT(result == TypedEnum(result));
+ MOZ_RELEASE_ASSERT(!(result != TypedEnum(result)));
+ MOZ_RELEASE_ASSERT((result && true) == bool(result));
+ MOZ_RELEASE_ASSERT((result && false) == false);
+ MOZ_RELEASE_ASSERT((true && result) == bool(result));
+ MOZ_RELEASE_ASSERT((false && result && false) == false);
+ MOZ_RELEASE_ASSERT((result || false) == bool(result));
+ MOZ_RELEASE_ASSERT((result || true) == true);
+ MOZ_RELEASE_ASSERT((false || result) == bool(result));
+ MOZ_RELEASE_ASSERT((true || result) == true);
+}
+
+// Similar to TestBinOp but testing the unary ~ operator.
+template<typename TypedEnum, typename T>
+void TestTilde(const T& aT)
+{
+ typedef typename mozilla::detail::UnsignedIntegerTypeForEnum<TypedEnum>::Type
+ UnsignedIntegerType;
+
+ auto result = ~aT;
+
+ typedef decltype(result) ResultType;
+
+ RequireLiteralType<ResultType>();
+ TestNonConvertibilityForOneType<ResultType>();
+
+ UnsignedIntegerType unsignedIntegerResult = ~(UnsignedIntegerType(aT));
+
+ MOZ_RELEASE_ASSERT(unsignedIntegerResult == UnsignedIntegerType(result));
+ MOZ_RELEASE_ASSERT(TypedEnum(unsignedIntegerResult) == TypedEnum(result));
+ MOZ_RELEASE_ASSERT((!unsignedIntegerResult) == (!result));
+ MOZ_RELEASE_ASSERT((!!unsignedIntegerResult) == (!!result));
+ MOZ_RELEASE_ASSERT(bool(unsignedIntegerResult) == bool(result));
+}
+
+// Helper dispatching a given triple of operands to all operator-specific
+// testing functions.
+template<typename TypedEnum, typename T1, typename T2, typename T3>
+void TestAllOpsForGivenOperands(const T1& aT1, const T2& aT2, const T3& aT3)
+{
+ TestBinOp<TypedEnum, '|'>(aT1, aT2, aT3);
+ TestBinOp<TypedEnum, '&'>(aT1, aT2, aT3);
+ TestBinOp<TypedEnum, '^'>(aT1, aT2, aT3);
+ TestTilde<TypedEnum>(aT1);
+}
+
+// Helper building various triples of operands using a given operator,
+// and testing all operators with them.
+template<typename TypedEnum, char o>
+void TestAllOpsForOperandsBuiltUsingGivenOp()
+{
+ // The type of enum values like TypedEnum::A may be different from
+ // TypedEnum. That is the case in the legacy non-C++11 path. We want to
+ // ensure good test coverage even when these two types are distinct.
+ // To that effect, we have both 'auto' typed variables, preserving the
+ // original type of enum values, and 'plain' typed variables, that
+ // are plain TypedEnum's.
+
+ const TypedEnum a_plain = TypedEnum::A;
+ const TypedEnum b_plain = TypedEnum::B;
+ const TypedEnum c_plain = TypedEnum::C;
+
+ auto a_auto = TypedEnum::A;
+ auto b_auto = TypedEnum::B;
+ auto c_auto = TypedEnum::C;
+
+ auto ab_plain = Op<o>(a_plain, b_plain);
+ auto bc_plain = Op<o>(b_plain, c_plain);
+ auto ab_auto = Op<o>(a_auto, b_auto);
+ auto bc_auto = Op<o>(b_auto, c_auto);
+
+ // On each row below, we pass a triple of operands. Keep in mind that this
+ // is going to be received as (aT1, aT2, aT3) and the actual tests performed
+ // will be of the form
+ //
+ // result = aT1 Op aT2;
+ // result Op= aT3;
+ //
+ // For this reason, we carefully ensure that the values of (aT1, aT2)
+ // systematically cover all types of such pairs; to limit complexity,
+ // we are not so careful with aT3, and we just try to pass aT3's
+ // that may lead to nontrivial bitwise operations.
+ TestAllOpsForGivenOperands<TypedEnum>(a_plain, b_plain, c_plain);
+ TestAllOpsForGivenOperands<TypedEnum>(a_plain, bc_plain, b_auto);
+ TestAllOpsForGivenOperands<TypedEnum>(ab_plain, c_plain, a_plain);
+ TestAllOpsForGivenOperands<TypedEnum>(ab_plain, bc_plain, a_auto);
+
+ TestAllOpsForGivenOperands<TypedEnum>(a_plain, b_auto, c_plain);
+ TestAllOpsForGivenOperands<TypedEnum>(a_plain, bc_auto, b_auto);
+ TestAllOpsForGivenOperands<TypedEnum>(ab_plain, c_auto, a_plain);
+ TestAllOpsForGivenOperands<TypedEnum>(ab_plain, bc_auto, a_auto);
+
+ TestAllOpsForGivenOperands<TypedEnum>(a_auto, b_plain, c_plain);
+ TestAllOpsForGivenOperands<TypedEnum>(a_auto, bc_plain, b_auto);
+ TestAllOpsForGivenOperands<TypedEnum>(ab_auto, c_plain, a_plain);
+ TestAllOpsForGivenOperands<TypedEnum>(ab_auto, bc_plain, a_auto);
+
+ TestAllOpsForGivenOperands<TypedEnum>(a_auto, b_auto, c_plain);
+ TestAllOpsForGivenOperands<TypedEnum>(a_auto, bc_auto, b_auto);
+ TestAllOpsForGivenOperands<TypedEnum>(ab_auto, c_auto, a_plain);
+ TestAllOpsForGivenOperands<TypedEnum>(ab_auto, bc_auto, a_auto);
+}
+
+// Tests all bitwise operations on a given TypedEnum bitfield.
+template<typename TypedEnum>
+void
+TestTypedEnumBitField()
+{
+ TestTypedEnumBasics<TypedEnum>();
+
+ TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '|'>();
+ TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '&'>();
+ TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '^'>();
+}
+
+// Checks that enum bitwise expressions have the same non-convertibility
+// properties as c++11 enum classes do, i.e. not implicitly convertible to
+// anything (though *explicitly* convertible).
+void TestNoConversionsBetweenUnrelatedTypes()
+{
+ using mozilla::IsConvertible;
+
+ // Two typed enum classes having the same underlying integer type, to ensure
+ // that we would catch bugs accidentally allowing conversions in that case.
+ typedef CharEnumBitField T1;
+ typedef Nested::CharEnumBitField T2;
+
+ static_assert(!IsConvertible<T1, T2>::value,
+ "should not be convertible");
+ static_assert(!IsConvertible<T1, decltype(T2::A)>::value,
+ "should not be convertible");
+ static_assert(!IsConvertible<T1, decltype(T2::A | T2::B)>::value,
+ "should not be convertible");
+
+ static_assert(!IsConvertible<decltype(T1::A), T2>::value,
+ "should not be convertible");
+ static_assert(!IsConvertible<decltype(T1::A), decltype(T2::A)>::value,
+ "should not be convertible");
+ static_assert(!IsConvertible<decltype(T1::A), decltype(T2::A | T2::B)>::value,
+ "should not be convertible");
+
+ static_assert(!IsConvertible<decltype(T1::A | T1::B), T2>::value,
+ "should not be convertible");
+ static_assert(!IsConvertible<decltype(T1::A | T1::B), decltype(T2::A)>::value,
+ "should not be convertible");
+ static_assert(!IsConvertible<decltype(T1::A | T1::B), decltype(T2::A | T2::B)>::value,
+ "should not be convertible");
+}
+
+enum class Int8EnumWithHighBits : int8_t {
+ A = 0x20,
+ B = 0x40
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int8EnumWithHighBits)
+
+enum class Uint8EnumWithHighBits : uint8_t {
+ A = 0x40,
+ B = 0x80
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint8EnumWithHighBits)
+
+enum class Int16EnumWithHighBits : int16_t {
+ A = 0x2000,
+ B = 0x4000
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int16EnumWithHighBits)
+
+enum class Uint16EnumWithHighBits : uint16_t {
+ A = 0x4000,
+ B = 0x8000
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint16EnumWithHighBits)
+
+enum class Int32EnumWithHighBits : int32_t {
+ A = 0x20000000,
+ B = 0x40000000
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int32EnumWithHighBits)
+
+enum class Uint32EnumWithHighBits : uint32_t {
+ A = 0x40000000u,
+ B = 0x80000000u
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint32EnumWithHighBits)
+
+enum class Int64EnumWithHighBits : int64_t {
+ A = 0x2000000000000000ll,
+ B = 0x4000000000000000ll
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int64EnumWithHighBits)
+
+enum class Uint64EnumWithHighBits : uint64_t {
+ A = 0x4000000000000000ull,
+ B = 0x8000000000000000ull
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint64EnumWithHighBits)
+
+// Checks that we don't accidentally truncate high bits by coercing to the wrong
+// integer type internally when implementing bitwise ops.
+template<typename EnumType, typename IntType>
+void TestIsNotTruncated()
+{
+ EnumType a = EnumType::A;
+ EnumType b = EnumType::B;
+ MOZ_RELEASE_ASSERT(IntType(a));
+ MOZ_RELEASE_ASSERT(IntType(b));
+ MOZ_RELEASE_ASSERT(a | EnumType::B);
+ MOZ_RELEASE_ASSERT(a | b);
+ MOZ_RELEASE_ASSERT(EnumType::A | EnumType::B);
+ EnumType c = EnumType::A | EnumType::B;
+ MOZ_RELEASE_ASSERT(IntType(c));
+ MOZ_RELEASE_ASSERT(c & c);
+ MOZ_RELEASE_ASSERT(c | c);
+ MOZ_RELEASE_ASSERT(c == (EnumType::A | EnumType::B));
+ MOZ_RELEASE_ASSERT(a != (EnumType::A | EnumType::B));
+ MOZ_RELEASE_ASSERT(b != (EnumType::A | EnumType::B));
+ MOZ_RELEASE_ASSERT(c & EnumType::A);
+ MOZ_RELEASE_ASSERT(c & EnumType::B);
+ EnumType d = EnumType::A;
+ d |= EnumType::B;
+ MOZ_RELEASE_ASSERT(d == c);
+}
+
+int
+main()
+{
+ TestTypedEnumBasics<AutoEnum>();
+ TestTypedEnumBasics<CharEnum>();
+ TestTypedEnumBasics<Nested::AutoEnum>();
+ TestTypedEnumBasics<Nested::CharEnum>();
+
+ TestTypedEnumBitField<AutoEnumBitField>();
+ TestTypedEnumBitField<CharEnumBitField>();
+ TestTypedEnumBitField<Nested::AutoEnumBitField>();
+ TestTypedEnumBitField<Nested::CharEnumBitField>();
+
+ TestTypedEnumBitField<BitFieldFor_uint8_t>();
+ TestTypedEnumBitField<BitFieldFor_int8_t>();
+ TestTypedEnumBitField<BitFieldFor_uint16_t>();
+ TestTypedEnumBitField<BitFieldFor_int16_t>();
+ TestTypedEnumBitField<BitFieldFor_uint32_t>();
+ TestTypedEnumBitField<BitFieldFor_int32_t>();
+ TestTypedEnumBitField<BitFieldFor_uint64_t>();
+ TestTypedEnumBitField<BitFieldFor_int64_t>();
+ TestTypedEnumBitField<BitFieldFor_char>();
+ TestTypedEnumBitField<BitFieldFor_signed_char>();
+ TestTypedEnumBitField<BitFieldFor_unsigned_char>();
+ TestTypedEnumBitField<BitFieldFor_short>();
+ TestTypedEnumBitField<BitFieldFor_unsigned_short>();
+ TestTypedEnumBitField<BitFieldFor_int>();
+ TestTypedEnumBitField<BitFieldFor_unsigned_int>();
+ TestTypedEnumBitField<BitFieldFor_long>();
+ TestTypedEnumBitField<BitFieldFor_unsigned_long>();
+ TestTypedEnumBitField<BitFieldFor_long_long>();
+ TestTypedEnumBitField<BitFieldFor_unsigned_long_long>();
+
+ TestNoConversionsBetweenUnrelatedTypes();
+
+ TestIsNotTruncated<Int8EnumWithHighBits, int8_t>();
+ TestIsNotTruncated<Int16EnumWithHighBits, int16_t>();
+ TestIsNotTruncated<Int32EnumWithHighBits, int32_t>();
+ TestIsNotTruncated<Int64EnumWithHighBits, int64_t>();
+ TestIsNotTruncated<Uint8EnumWithHighBits, uint8_t>();
+ TestIsNotTruncated<Uint16EnumWithHighBits, uint16_t>();
+ TestIsNotTruncated<Uint32EnumWithHighBits, uint32_t>();
+ TestIsNotTruncated<Uint64EnumWithHighBits, uint64_t>();
+
+ return 0;
+}
diff --git a/mfbt/tests/TestUniquePtr.cpp b/mfbt/tests/TestUniquePtr.cpp
new file mode 100644
index 000000000..c866fcc3a
--- /dev/null
+++ b/mfbt/tests/TestUniquePtr.cpp
@@ -0,0 +1,592 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/Compiler.h"
+#include "mozilla/Move.h"
+#include "mozilla/TypeTraits.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
+
+#include <stddef.h>
+
+using mozilla::DefaultDelete;
+using mozilla::IsSame;
+using mozilla::MakeUnique;
+using mozilla::Swap;
+using mozilla::UniquePtr;
+using mozilla::Vector;
+
+#define CHECK(c) \
+ do { \
+ bool cond = !!(c); \
+ MOZ_ASSERT(cond, "Failed assertion: " #c); \
+ if (!cond) { \
+ return false; \
+ } \
+ } while (false)
+
+typedef UniquePtr<int> NewInt;
+static_assert(sizeof(NewInt) == sizeof(int*), "stored most efficiently");
+
+static size_t gADestructorCalls = 0;
+
+struct A
+{
+public:
+ A() : mX(0) {}
+ virtual ~A() { gADestructorCalls++; }
+
+ int mX;
+};
+
+static size_t gBDestructorCalls = 0;
+
+struct B : public A
+{
+public:
+ B() : mY(1) {}
+ ~B() { gBDestructorCalls++; }
+
+ int mY;
+};
+
+typedef UniquePtr<A> UniqueA;
+typedef UniquePtr<B, UniqueA::DeleterType> UniqueB; // permit interconversion
+
+static_assert(sizeof(UniqueA) == sizeof(A*), "stored most efficiently");
+static_assert(sizeof(UniqueB) == sizeof(B*), "stored most efficiently");
+
+struct DeleterSubclass : UniqueA::DeleterType {};
+
+typedef UniquePtr<B, DeleterSubclass> UniqueC;
+static_assert(sizeof(UniqueC) == sizeof(B*), "stored most efficiently");
+
+static UniqueA
+ReturnUniqueA()
+{
+ return UniqueA(new B);
+}
+
+static UniqueA
+ReturnLocalA()
+{
+ UniqueA a(new A);
+ return Move(a);
+}
+
+static void
+TestDeleterType()
+{
+ // Make sure UniquePtr will use its deleter's pointer type if it defines one.
+ typedef int* Ptr;
+ struct Deleter {
+ typedef Ptr pointer;
+ Deleter() {}
+ void operator()(int*p) {
+ delete p;
+ }
+ };
+ UniquePtr<Ptr, Deleter> u(new int, Deleter());
+}
+
+static bool
+TestDefaultFreeGuts()
+{
+ static_assert(IsSame<NewInt::DeleterType, DefaultDelete<int> >::value,
+ "weird deleter?");
+
+ NewInt n1(new int);
+ CHECK(n1);
+ CHECK(n1.get() != nullptr);
+
+ n1 = nullptr;
+ CHECK(!n1);
+ CHECK(n1.get() == nullptr);
+
+ int* p1 = new int;
+ n1.reset(p1);
+ CHECK(n1);
+ NewInt n2(Move(n1));
+ CHECK(!n1);
+ CHECK(n1.get() == nullptr);
+ CHECK(n2.get() == p1);
+
+ Swap(n1, n2);
+ CHECK(n1.get() == p1);
+ CHECK(n2.get() == nullptr);
+
+ n1.swap(n2);
+ CHECK(n1.get() == nullptr);
+ CHECK(n2.get() == p1);
+ delete n2.release();
+
+ CHECK(n1.get() == nullptr);
+ CHECK(n2 == nullptr);
+ CHECK(nullptr == n2);
+
+ int* p2 = new int;
+ int* p3 = new int;
+ n1.reset(p2);
+ n2.reset(p3);
+ CHECK(n1.get() == p2);
+ CHECK(n2.get() == p3);
+
+ n1.swap(n2);
+ CHECK(n2 != nullptr);
+ CHECK(nullptr != n2);
+ CHECK(n2.get() == p2);
+ CHECK(n1.get() == p3);
+
+ UniqueA a1;
+ CHECK(a1 == nullptr);
+ a1.reset(new A);
+ CHECK(gADestructorCalls == 0);
+ CHECK(a1->mX == 0);
+
+ B* bp1 = new B;
+ bp1->mX = 5;
+ CHECK(gBDestructorCalls == 0);
+ a1.reset(bp1);
+ CHECK(gADestructorCalls == 1);
+ CHECK(a1->mX == 5);
+ a1.reset(nullptr);
+ CHECK(gADestructorCalls == 2);
+ CHECK(gBDestructorCalls == 1);
+
+ B* bp2 = new B;
+ UniqueB b1(bp2);
+ UniqueA a2(nullptr);
+ a2 = Move(b1);
+ CHECK(gADestructorCalls == 2);
+ CHECK(gBDestructorCalls == 1);
+
+ UniqueA a3(Move(a2));
+ a3 = nullptr;
+ CHECK(gADestructorCalls == 3);
+ CHECK(gBDestructorCalls == 2);
+
+ B* bp3 = new B;
+ bp3->mX = 42;
+ UniqueB b2(bp3);
+ UniqueA a4(Move(b2));
+ CHECK(b2.get() == nullptr);
+ CHECK((*a4).mX == 42);
+ CHECK(gADestructorCalls == 3);
+ CHECK(gBDestructorCalls == 2);
+
+ UniqueA a5(new A);
+ UniqueB b3(new B);
+ a5 = Move(b3);
+ CHECK(gADestructorCalls == 4);
+ CHECK(gBDestructorCalls == 2);
+
+ ReturnUniqueA();
+ CHECK(gADestructorCalls == 5);
+ CHECK(gBDestructorCalls == 3);
+
+ ReturnLocalA();
+ CHECK(gADestructorCalls == 6);
+ CHECK(gBDestructorCalls == 3);
+
+ UniqueA a6(ReturnLocalA());
+ a6 = nullptr;
+ CHECK(gADestructorCalls == 7);
+ CHECK(gBDestructorCalls == 3);
+
+ UniqueC c1(new B);
+ UniqueA a7(new B);
+ a7 = Move(c1);
+ CHECK(gADestructorCalls == 8);
+ CHECK(gBDestructorCalls == 4);
+
+ c1.reset(new B);
+
+ UniqueA a8(Move(c1));
+ CHECK(gADestructorCalls == 8);
+ CHECK(gBDestructorCalls == 4);
+
+ // These smart pointers still own B resources.
+ CHECK(a4);
+ CHECK(a5);
+ CHECK(a7);
+ CHECK(a8);
+ return true;
+}
+
+static bool
+TestDefaultFree()
+{
+ CHECK(TestDefaultFreeGuts());
+ CHECK(gADestructorCalls == 12);
+ CHECK(gBDestructorCalls == 8);
+ return true;
+}
+
+static size_t FreeClassCounter = 0;
+
+struct FreeClass
+{
+public:
+ FreeClass() {}
+
+ void operator()(int* aPtr)
+ {
+ FreeClassCounter++;
+ delete aPtr;
+ }
+};
+
+typedef UniquePtr<int, FreeClass> NewIntCustom;
+static_assert(sizeof(NewIntCustom) == sizeof(int*),
+ "stored most efficiently");
+
+static bool
+TestFreeClass()
+{
+ CHECK(FreeClassCounter == 0);
+ {
+ NewIntCustom n1(new int);
+ CHECK(FreeClassCounter == 0);
+ }
+ CHECK(FreeClassCounter == 1);
+
+ NewIntCustom n2;
+ {
+ NewIntCustom n3(new int);
+ CHECK(FreeClassCounter == 1);
+ n2 = Move(n3);
+ }
+ CHECK(FreeClassCounter == 1);
+ n2 = nullptr;
+ CHECK(FreeClassCounter == 2);
+
+ n2.reset(nullptr);
+ CHECK(FreeClassCounter == 2);
+ n2.reset(new int);
+ n2.reset();
+ CHECK(FreeClassCounter == 3);
+
+ NewIntCustom n4(new int, FreeClass());
+ CHECK(FreeClassCounter == 3);
+ n4.reset(new int);
+ CHECK(FreeClassCounter == 4);
+ n4.reset();
+ CHECK(FreeClassCounter == 5);
+
+ FreeClass f;
+ NewIntCustom n5(new int, f);
+ CHECK(FreeClassCounter == 5);
+ int* p = n5.release();
+ CHECK(FreeClassCounter == 5);
+ delete p;
+
+ return true;
+}
+
+typedef UniquePtr<int, DefaultDelete<int>&> IntDeleterRef;
+typedef UniquePtr<A, DefaultDelete<A>&> ADeleterRef;
+typedef UniquePtr<B, DefaultDelete<A>&> BDeleterRef;
+
+static_assert(sizeof(IntDeleterRef) > sizeof(int*),
+ "has to be heavier than an int* to store the reference");
+static_assert(sizeof(ADeleterRef) > sizeof(A*),
+ "has to be heavier than an A* to store the reference");
+static_assert(sizeof(BDeleterRef) > sizeof(int*),
+ "has to be heavier than a B* to store the reference");
+
+static bool
+TestReferenceDeleterGuts()
+{
+ DefaultDelete<int> delInt;
+ IntDeleterRef id1(new int, delInt);
+
+ IntDeleterRef id2(Move(id1));
+ CHECK(id1 == nullptr);
+ CHECK(nullptr != id2);
+ CHECK(&id1.get_deleter() == &id2.get_deleter());
+
+ IntDeleterRef id3(Move(id2));
+
+ DefaultDelete<A> delA;
+ ADeleterRef a1(new A, delA);
+ a1.reset(nullptr);
+ a1.reset(new B);
+ a1 = nullptr;
+
+ BDeleterRef b1(new B, delA);
+ a1 = Move(b1);
+
+ BDeleterRef b2(new B, delA);
+
+ ADeleterRef a2(Move(b2));
+
+ return true;
+}
+
+static bool
+TestReferenceDeleter()
+{
+ gADestructorCalls = 0;
+ gBDestructorCalls = 0;
+
+ CHECK(TestReferenceDeleterGuts());
+
+ CHECK(gADestructorCalls == 4);
+ CHECK(gBDestructorCalls == 3);
+
+ gADestructorCalls = 0;
+ gBDestructorCalls = 0;
+ return true;
+}
+
+typedef void (&FreeSignature)(void*);
+
+static size_t DeleteIntFunctionCallCount = 0;
+
+static void
+DeleteIntFunction(void* aPtr)
+{
+ DeleteIntFunctionCallCount++;
+ delete static_cast<int*>(aPtr);
+}
+
+static void
+SetMallocedInt(UniquePtr<int, FreeSignature>& aPtr, int aI)
+{
+ int* newPtr = static_cast<int*>(malloc(sizeof(int)));
+ *newPtr = aI;
+ aPtr.reset(newPtr);
+}
+
+static UniquePtr<int, FreeSignature>
+MallocedInt(int aI)
+{
+ UniquePtr<int, FreeSignature>
+ ptr(static_cast<int*>(malloc(sizeof(int))), free);
+ *ptr = aI;
+ return Move(ptr);
+}
+static bool
+TestFunctionReferenceDeleter()
+{
+ // Look for allocator mismatches and leaks to verify these bits
+ UniquePtr<int, FreeSignature> i1(MallocedInt(17));
+ CHECK(*i1 == 17);
+
+ SetMallocedInt(i1, 42);
+ CHECK(*i1 == 42);
+
+ // These bits use a custom deleter so we can instrument deletion.
+ {
+ UniquePtr<int, FreeSignature> i2 =
+ UniquePtr<int, FreeSignature>(new int(42), DeleteIntFunction);
+ CHECK(DeleteIntFunctionCallCount == 0);
+
+ i2.reset(new int(76));
+ CHECK(DeleteIntFunctionCallCount == 1);
+ }
+
+ CHECK(DeleteIntFunctionCallCount == 2);
+
+ return true;
+}
+
+template<typename T>
+struct AppendNullptrTwice
+{
+ AppendNullptrTwice() {}
+
+ bool operator()(Vector<T>& vec)
+ {
+ CHECK(vec.append(nullptr));
+ CHECK(vec.append(nullptr));
+ return true;
+ }
+};
+
+static size_t AAfter;
+static size_t BAfter;
+
+static bool
+TestVectorGuts()
+{
+ Vector<UniqueA> vec;
+ CHECK(vec.append(new B));
+ CHECK(vec.append(new A));
+ CHECK(AppendNullptrTwice<UniqueA>()(vec));
+ CHECK(vec.append(new B));
+
+ size_t initialLength = vec.length();
+
+ UniqueA* begin = vec.begin();
+ bool appendA = true;
+ do {
+ CHECK(appendA ? vec.append(new A) : vec.append(new B));
+ appendA = !appendA;
+ } while (begin == vec.begin());
+
+ size_t numAppended = vec.length() - initialLength;
+
+ BAfter = numAppended / 2;
+ AAfter = numAppended - numAppended / 2;
+
+ CHECK(gADestructorCalls == 0);
+ CHECK(gBDestructorCalls == 0);
+ return true;
+}
+
+static bool
+TestVector()
+{
+ gADestructorCalls = 0;
+ gBDestructorCalls = 0;
+
+ CHECK(TestVectorGuts());
+
+ CHECK(gADestructorCalls == 3 + AAfter + BAfter);
+ CHECK(gBDestructorCalls == 2 + BAfter);
+ return true;
+}
+
+typedef UniquePtr<int[]> IntArray;
+static_assert(sizeof(IntArray) == sizeof(int*),
+ "stored most efficiently");
+
+static bool
+TestArray()
+{
+ static_assert(IsSame<IntArray::DeleterType, DefaultDelete<int[]> >::value,
+ "weird deleter?");
+
+ IntArray n1(new int[5]);
+ CHECK(n1);
+ CHECK(n1.get() != nullptr);
+
+ n1 = nullptr;
+ CHECK(!n1);
+ CHECK(n1.get() == nullptr);
+
+ int* p1 = new int[42];
+ n1.reset(p1);
+ CHECK(n1);
+ IntArray n2(Move(n1));
+ CHECK(!n1);
+ CHECK(n1.get() == nullptr);
+ CHECK(n2.get() == p1);
+
+ Swap(n1, n2);
+ CHECK(n1.get() == p1);
+ CHECK(n2.get() == nullptr);
+
+ n1.swap(n2);
+ CHECK(n1.get() == nullptr);
+ CHECK(n2.get() == p1);
+ delete[] n2.release();
+
+ CHECK(n1.get() == nullptr);
+ CHECK(n2.get() == nullptr);
+
+ int* p2 = new int[7];
+ int* p3 = new int[42];
+ n1.reset(p2);
+ n2.reset(p3);
+ CHECK(n1.get() == p2);
+ CHECK(n2.get() == p3);
+
+ n1.swap(n2);
+ CHECK(n2.get() == p2);
+ CHECK(n1.get() == p3);
+
+ n1 = Move(n2);
+ CHECK(n1.get() == p2);
+ n1 = Move(n2);
+ CHECK(n1.get() == nullptr);
+
+ UniquePtr<A[]> a1(new A[17]);
+ static_assert(sizeof(a1) == sizeof(A*),
+ "stored most efficiently");
+
+ UniquePtr<A[]> a2(new A[5], DefaultDelete<A[]>());
+ a2.reset(nullptr);
+ a2.reset(new A[17]);
+ a2 = nullptr;
+
+ UniquePtr<A[]> a3(nullptr);
+ a3.reset(new A[7]);
+
+ return true;
+}
+
+struct Q
+{
+ Q() {}
+ Q(const Q&) {}
+
+ Q(Q&, char) {}
+
+ template<typename T>
+ Q(Q, T&&, int) {}
+
+ Q(int, long, double, void*) {}
+};
+
+static int randomInt() { return 4; }
+
+static bool
+TestMakeUnique()
+{
+ UniquePtr<int> a1(MakeUnique<int>());
+ UniquePtr<long> a2(MakeUnique<long>(4));
+
+ // no args, easy
+ UniquePtr<Q> q0(MakeUnique<Q>());
+
+ // temporary bound to const lval ref
+ UniquePtr<Q> q1(MakeUnique<Q>(Q()));
+
+ // passing through a non-const lval ref
+ UniquePtr<Q> q2(MakeUnique<Q>(*q1, 'c'));
+
+ // pass by copying, forward a temporary, pass by value
+ UniquePtr<Q> q3(MakeUnique<Q>(Q(), UniquePtr<int>(), randomInt()));
+
+ // various type mismatching to test "fuzzy" forwarding
+ UniquePtr<Q> q4(MakeUnique<Q>('s', 66LL, 3.141592654, &q3));
+
+ UniquePtr<char[]> c1(MakeUnique<char[]>(5));
+
+ return true;
+}
+
+int
+main()
+{
+ TestDeleterType();
+
+ if (!TestDefaultFree()) {
+ return 1;
+ }
+ if (!TestFreeClass()) {
+ return 1;
+ }
+ if (!TestReferenceDeleter()) {
+ return 1;
+ }
+ if (!TestFunctionReferenceDeleter()) {
+ return 1;
+ }
+ if (!TestVector()) {
+ return 1;
+ }
+ if (!TestArray()) {
+ return 1;
+ }
+ if (!TestMakeUnique()) {
+ return 1;
+ }
+ return 0;
+}
diff --git a/mfbt/tests/TestVariant.cpp b/mfbt/tests/TestVariant.cpp
new file mode 100644
index 000000000..d47df70cb
--- /dev/null
+++ b/mfbt/tests/TestVariant.cpp
@@ -0,0 +1,188 @@
+/* -*- 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/UniquePtr.h"
+#include "mozilla/Variant.h"
+
+using mozilla::MakeUnique;
+using mozilla::UniquePtr;
+using mozilla::Variant;
+
+struct Destroyer {
+ static int destroyedCount;
+ ~Destroyer() {
+ destroyedCount++;
+ }
+};
+
+int Destroyer::destroyedCount = 0;
+
+static void
+testSimple()
+{
+ printf("testSimple\n");
+ Variant<uint32_t, uint64_t> v(uint64_t(1));
+ MOZ_RELEASE_ASSERT(v.is<uint64_t>());
+ MOZ_RELEASE_ASSERT(!v.is<uint32_t>());
+ MOZ_RELEASE_ASSERT(v.as<uint64_t>() == 1);
+}
+
+static void
+testCopy()
+{
+ printf("testCopy\n");
+ Variant<uint32_t, uint64_t> v1(uint64_t(1));
+ Variant<uint32_t, uint64_t> v2(v1);
+ MOZ_RELEASE_ASSERT(v2.is<uint64_t>());
+ MOZ_RELEASE_ASSERT(!v2.is<uint32_t>());
+ MOZ_RELEASE_ASSERT(v2.as<uint64_t>() == 1);
+
+ Variant<uint32_t, uint64_t> v3(uint32_t(10));
+ v3 = v2;
+ MOZ_RELEASE_ASSERT(v3.is<uint64_t>());
+ MOZ_RELEASE_ASSERT(v3.as<uint64_t>() == 1);
+}
+
+static void
+testMove()
+{
+ printf("testMove\n");
+ Variant<UniquePtr<int>, char> v1(MakeUnique<int>(5));
+ Variant<UniquePtr<int>, char> v2(Move(v1));
+
+ MOZ_RELEASE_ASSERT(v2.is<UniquePtr<int>>());
+ MOZ_RELEASE_ASSERT(*v2.as<UniquePtr<int>>() == 5);
+
+ MOZ_RELEASE_ASSERT(v1.is<UniquePtr<int>>());
+ MOZ_RELEASE_ASSERT(v1.as<UniquePtr<int>>() == nullptr);
+
+ Destroyer::destroyedCount = 0;
+ {
+ Variant<char, UniquePtr<Destroyer>> v3(MakeUnique<Destroyer>());
+ Variant<char, UniquePtr<Destroyer>> v4(Move(v3));
+
+ Variant<char, UniquePtr<Destroyer>> v5('a');
+ v5 = Move(v4);
+
+ auto ptr = v5.extract<UniquePtr<Destroyer>>();
+ MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 0);
+ }
+ MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 1);
+}
+
+static void
+testDestructor()
+{
+ printf("testDestructor\n");
+ Destroyer::destroyedCount = 0;
+
+ {
+ Destroyer d;
+
+ {
+ Variant<char, UniquePtr<char[]>, Destroyer> v(d);
+ MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 0); // None detroyed yet.
+ }
+
+ MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 1); // v's copy of d is destroyed.
+ }
+
+ MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 2); // d is destroyed.
+}
+
+static void
+testEquality()
+{
+ printf("testEquality\n");
+ using V = Variant<char, int>;
+
+ V v0('a');
+ V v1('b');
+ V v2('b');
+ V v3(42);
+ V v4(27);
+ V v5(27);
+ V v6(int('b'));
+
+ MOZ_RELEASE_ASSERT(v0 != v1);
+ MOZ_RELEASE_ASSERT(v1 == v2);
+ MOZ_RELEASE_ASSERT(v2 != v3);
+ MOZ_RELEASE_ASSERT(v3 != v4);
+ MOZ_RELEASE_ASSERT(v4 == v5);
+ MOZ_RELEASE_ASSERT(v1 != v6);
+
+ MOZ_RELEASE_ASSERT(v0 == v0);
+ MOZ_RELEASE_ASSERT(v1 == v1);
+ MOZ_RELEASE_ASSERT(v2 == v2);
+ MOZ_RELEASE_ASSERT(v3 == v3);
+ MOZ_RELEASE_ASSERT(v4 == v4);
+ MOZ_RELEASE_ASSERT(v5 == v5);
+ MOZ_RELEASE_ASSERT(v6 == v6);
+}
+
+struct Describer
+{
+ static const char* little;
+ static const char* medium;
+ static const char* big;
+
+ const char* match(const uint8_t&) { return little; }
+ const char* match(const uint32_t&) { return medium; }
+ const char* match(const uint64_t&) { return big; }
+};
+
+const char* Describer::little = "little";
+const char* Describer::medium = "medium";
+const char* Describer::big = "big";
+
+static void
+testMatching()
+{
+ printf("testMatching\n");
+ using V = Variant<uint8_t, uint32_t, uint64_t>;
+
+ Describer desc;
+
+ V v1(uint8_t(1));
+ V v2(uint32_t(2));
+ V v3(uint64_t(3));
+
+ MOZ_RELEASE_ASSERT(v1.match(desc) == Describer::little);
+ MOZ_RELEASE_ASSERT(v2.match(desc) == Describer::medium);
+ MOZ_RELEASE_ASSERT(v3.match(desc) == Describer::big);
+
+ const V& constRef1 = v1;
+ const V& constRef2 = v2;
+ const V& constRef3 = v3;
+
+ MOZ_RELEASE_ASSERT(constRef1.match(desc) == Describer::little);
+ MOZ_RELEASE_ASSERT(constRef2.match(desc) == Describer::medium);
+ MOZ_RELEASE_ASSERT(constRef3.match(desc) == Describer::big);
+}
+
+static void
+testRvalueMatcher()
+{
+ printf("testRvalueMatcher\n");
+ using V = Variant<uint8_t, uint32_t, uint64_t>;
+ V v(uint8_t(1));
+ MOZ_RELEASE_ASSERT(v.match(Describer()) == Describer::little);
+}
+
+int
+main()
+{
+ testSimple();
+ testCopy();
+ testMove();
+ testDestructor();
+ testEquality();
+ testMatching();
+ testRvalueMatcher();
+
+ printf("TestVariant OK!\n");
+ return 0;
+}
diff --git a/mfbt/tests/TestVector.cpp b/mfbt/tests/TestVector.cpp
new file mode 100644
index 000000000..d969bcbc2
--- /dev/null
+++ b/mfbt/tests/TestVector.cpp
@@ -0,0 +1,358 @@
+/* -*- 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/Move.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
+
+using mozilla::detail::VectorTesting;
+using mozilla::MakeUnique;
+using mozilla::Move;
+using mozilla::UniquePtr;
+using mozilla::Vector;
+
+struct mozilla::detail::VectorTesting
+{
+ static void testReserved();
+ static void testConstRange();
+ static void testEmplaceBack();
+ static void testReverse();
+ static void testExtractRawBuffer();
+ static void testExtractOrCopyRawBuffer();
+};
+
+void
+mozilla::detail::VectorTesting::testReserved()
+{
+#ifdef DEBUG
+ Vector<bool> bv;
+ MOZ_RELEASE_ASSERT(bv.reserved() == 0);
+
+ MOZ_RELEASE_ASSERT(bv.append(true));
+ MOZ_RELEASE_ASSERT(bv.reserved() == 1);
+
+ Vector<bool> otherbv;
+ MOZ_RELEASE_ASSERT(otherbv.append(false));
+ MOZ_RELEASE_ASSERT(otherbv.append(true));
+ MOZ_RELEASE_ASSERT(bv.appendAll(otherbv));
+ MOZ_RELEASE_ASSERT(bv.reserved() == 3);
+
+ MOZ_RELEASE_ASSERT(bv.reserve(5));
+ MOZ_RELEASE_ASSERT(bv.reserved() == 5);
+
+ MOZ_RELEASE_ASSERT(bv.reserve(1));
+ MOZ_RELEASE_ASSERT(bv.reserved() == 5);
+
+ Vector<bool> bv2(Move(bv));
+ MOZ_RELEASE_ASSERT(bv.reserved() == 0);
+ MOZ_RELEASE_ASSERT(bv2.reserved() == 5);
+
+ bv2.clearAndFree();
+ MOZ_RELEASE_ASSERT(bv2.reserved() == 0);
+
+ Vector<int, 42> iv;
+ MOZ_RELEASE_ASSERT(iv.reserved() == 0);
+
+ MOZ_RELEASE_ASSERT(iv.append(17));
+ MOZ_RELEASE_ASSERT(iv.reserved() == 1);
+
+ Vector<int, 42> otheriv;
+ MOZ_RELEASE_ASSERT(otheriv.append(42));
+ MOZ_RELEASE_ASSERT(otheriv.append(37));
+ MOZ_RELEASE_ASSERT(iv.appendAll(otheriv));
+ MOZ_RELEASE_ASSERT(iv.reserved() == 3);
+
+ MOZ_RELEASE_ASSERT(iv.reserve(5));
+ MOZ_RELEASE_ASSERT(iv.reserved() == 5);
+
+ MOZ_RELEASE_ASSERT(iv.reserve(1));
+ MOZ_RELEASE_ASSERT(iv.reserved() == 5);
+
+ MOZ_RELEASE_ASSERT(iv.reserve(55));
+ MOZ_RELEASE_ASSERT(iv.reserved() == 55);
+
+ Vector<int, 42> iv2(Move(iv));
+ MOZ_RELEASE_ASSERT(iv.reserved() == 0);
+ MOZ_RELEASE_ASSERT(iv2.reserved() == 55);
+
+ iv2.clearAndFree();
+ MOZ_RELEASE_ASSERT(iv2.reserved() == 0);
+#endif
+}
+
+void
+mozilla::detail::VectorTesting::testConstRange()
+{
+#ifdef DEBUG
+ Vector<int> vec;
+
+ for (int i = 0; i < 10; i++) {
+ MOZ_RELEASE_ASSERT(vec.append(i));
+ }
+
+ const auto &vecRef = vec;
+
+ Vector<int>::ConstRange range = vecRef.all();
+ for (int i = 0; i < 10; i++) {
+ MOZ_RELEASE_ASSERT(!range.empty());
+ MOZ_RELEASE_ASSERT(range.front() == i);
+ range.popFront();
+ }
+#endif
+}
+
+namespace {
+
+struct S
+{
+ size_t j;
+ UniquePtr<size_t> k;
+
+ static size_t constructCount;
+ static size_t moveCount;
+ static size_t destructCount;
+
+ static void resetCounts() {
+ constructCount = 0;
+ moveCount = 0;
+ destructCount = 0;
+ }
+
+ S(size_t j, size_t k)
+ : j(j)
+ , k(MakeUnique<size_t>(k))
+ {
+ constructCount++;
+ }
+
+ S(S&& rhs)
+ : j(rhs.j)
+ , k(Move(rhs.k))
+ {
+ rhs.j = 0;
+ rhs.k.reset(0);
+ moveCount++;
+ }
+
+ ~S() {
+ destructCount++;
+ }
+
+ S(const S&) = delete;
+ S& operator=(const S&) = delete;
+};
+
+size_t S::constructCount = 0;
+size_t S::moveCount = 0;
+size_t S::destructCount = 0;
+
+}
+
+void
+mozilla::detail::VectorTesting::testEmplaceBack()
+{
+ S::resetCounts();
+
+ Vector<S> vec;
+ MOZ_RELEASE_ASSERT(vec.reserve(20));
+
+ for (size_t i = 0; i < 10; i++) {
+ S s(i, i * i);
+ MOZ_RELEASE_ASSERT(vec.append(Move(s)));
+ }
+
+ MOZ_RELEASE_ASSERT(vec.length() == 10);
+ MOZ_RELEASE_ASSERT(S::constructCount == 10);
+ MOZ_RELEASE_ASSERT(S::moveCount == 10);
+
+ for (size_t i = 10; i < 20; i++) {
+ MOZ_RELEASE_ASSERT(vec.emplaceBack(i, i * i));
+ }
+
+ MOZ_RELEASE_ASSERT(vec.length() == 20);
+ MOZ_RELEASE_ASSERT(S::constructCount == 20);
+ MOZ_RELEASE_ASSERT(S::moveCount == 10);
+
+ for (size_t i = 0; i < 20; i++) {
+ MOZ_RELEASE_ASSERT(vec[i].j == i);
+ MOZ_RELEASE_ASSERT(*vec[i].k == i * i);
+ }
+}
+
+void
+mozilla::detail::VectorTesting::testReverse()
+{
+ // Use UniquePtr to make sure that reverse() can handler move-only types.
+ Vector<UniquePtr<uint8_t>, 0> vec;
+
+ // Reverse an odd number of elements.
+
+ for (uint8_t i = 0; i < 5; i++) {
+ auto p = MakeUnique<uint8_t>(i);
+ MOZ_RELEASE_ASSERT(p);
+ MOZ_RELEASE_ASSERT(vec.append(mozilla::Move(p)));
+ }
+
+ vec.reverse();
+
+ MOZ_RELEASE_ASSERT(*vec[0] == 4);
+ MOZ_RELEASE_ASSERT(*vec[1] == 3);
+ MOZ_RELEASE_ASSERT(*vec[2] == 2);
+ MOZ_RELEASE_ASSERT(*vec[3] == 1);
+ MOZ_RELEASE_ASSERT(*vec[4] == 0);
+
+ // Reverse an even number of elements.
+
+ vec.popBack();
+ vec.reverse();
+
+ MOZ_RELEASE_ASSERT(*vec[0] == 1);
+ MOZ_RELEASE_ASSERT(*vec[1] == 2);
+ MOZ_RELEASE_ASSERT(*vec[2] == 3);
+ MOZ_RELEASE_ASSERT(*vec[3] == 4);
+
+ // Reverse an empty vector.
+
+ vec.clear();
+ MOZ_RELEASE_ASSERT(vec.length() == 0);
+ vec.reverse();
+ MOZ_RELEASE_ASSERT(vec.length() == 0);
+
+ // Reverse a vector using only inline storage.
+
+ Vector<UniquePtr<uint8_t>, 5> vec2;
+ for (uint8_t i = 0; i < 5; i++) {
+ auto p = MakeUnique<uint8_t>(i);
+ MOZ_RELEASE_ASSERT(p);
+ MOZ_RELEASE_ASSERT(vec2.append(mozilla::Move(p)));
+ }
+
+ vec2.reverse();
+
+ MOZ_RELEASE_ASSERT(*vec2[0] == 4);
+ MOZ_RELEASE_ASSERT(*vec2[1] == 3);
+ MOZ_RELEASE_ASSERT(*vec2[2] == 2);
+ MOZ_RELEASE_ASSERT(*vec2[3] == 1);
+ MOZ_RELEASE_ASSERT(*vec2[4] == 0);
+}
+
+void
+mozilla::detail::VectorTesting::testExtractRawBuffer()
+{
+ S::resetCounts();
+
+ Vector<S, 5> vec;
+ MOZ_RELEASE_ASSERT(vec.reserve(5));
+ for (size_t i = 0; i < 5; i++) {
+ vec.infallibleEmplaceBack(i, i * i);
+ }
+ MOZ_RELEASE_ASSERT(vec.length() == 5);
+ MOZ_ASSERT(vec.reserved() == 5);
+ MOZ_RELEASE_ASSERT(S::constructCount == 5);
+ MOZ_RELEASE_ASSERT(S::moveCount == 0);
+ MOZ_RELEASE_ASSERT(S::destructCount == 0);
+
+ S* buf = vec.extractRawBuffer();
+ MOZ_RELEASE_ASSERT(!buf);
+ MOZ_RELEASE_ASSERT(vec.length() == 5);
+ MOZ_ASSERT(vec.reserved() == 5);
+ MOZ_RELEASE_ASSERT(S::constructCount == 5);
+ MOZ_RELEASE_ASSERT(S::moveCount == 0);
+ MOZ_RELEASE_ASSERT(S::destructCount == 0);
+
+ MOZ_RELEASE_ASSERT(vec.reserve(10));
+ for (size_t i = 5; i < 10; i++) {
+ vec.infallibleEmplaceBack(i, i * i);
+ }
+ MOZ_RELEASE_ASSERT(vec.length() == 10);
+ MOZ_ASSERT(vec.reserved() == 10);
+ MOZ_RELEASE_ASSERT(S::constructCount == 10);
+ MOZ_RELEASE_ASSERT(S::moveCount == 5);
+ MOZ_RELEASE_ASSERT(S::destructCount == 5);
+
+ buf = vec.extractRawBuffer();
+ MOZ_RELEASE_ASSERT(buf);
+ MOZ_RELEASE_ASSERT(vec.length() == 0);
+ MOZ_ASSERT(vec.reserved() == 0);
+ MOZ_RELEASE_ASSERT(S::constructCount == 10);
+ MOZ_RELEASE_ASSERT(S::moveCount == 5);
+ MOZ_RELEASE_ASSERT(S::destructCount == 5);
+
+ for (size_t i = 0; i < 10; i++) {
+ MOZ_RELEASE_ASSERT(buf[i].j == i);
+ MOZ_RELEASE_ASSERT(*buf[i].k == i * i);
+ }
+
+ free(buf);
+}
+
+void
+mozilla::detail::VectorTesting::testExtractOrCopyRawBuffer()
+{
+ S::resetCounts();
+
+ Vector<S, 5> vec;
+ MOZ_RELEASE_ASSERT(vec.reserve(5));
+ for (size_t i = 0; i < 5; i++) {
+ vec.infallibleEmplaceBack(i, i * i);
+ }
+ MOZ_RELEASE_ASSERT(vec.length() == 5);
+ MOZ_ASSERT(vec.reserved() == 5);
+ MOZ_RELEASE_ASSERT(S::constructCount == 5);
+ MOZ_RELEASE_ASSERT(S::moveCount == 0);
+ MOZ_RELEASE_ASSERT(S::destructCount == 0);
+
+ S* buf = vec.extractOrCopyRawBuffer();
+ MOZ_RELEASE_ASSERT(buf);
+ MOZ_RELEASE_ASSERT(vec.length() == 0);
+ MOZ_ASSERT(vec.reserved() == 0);
+ MOZ_RELEASE_ASSERT(S::constructCount == 5);
+ MOZ_RELEASE_ASSERT(S::moveCount == 5);
+ MOZ_RELEASE_ASSERT(S::destructCount == 5);
+
+ for (size_t i = 0; i < 5; i++) {
+ MOZ_RELEASE_ASSERT(buf[i].j == i);
+ MOZ_RELEASE_ASSERT(*buf[i].k == i * i);
+ }
+
+ S::resetCounts();
+
+ MOZ_RELEASE_ASSERT(vec.reserve(10));
+ for (size_t i = 0; i < 10; i++) {
+ vec.infallibleEmplaceBack(i, i * i);
+ }
+ MOZ_RELEASE_ASSERT(vec.length() == 10);
+ MOZ_ASSERT(vec.reserved() == 10);
+ MOZ_RELEASE_ASSERT(S::constructCount == 10);
+ MOZ_RELEASE_ASSERT(S::moveCount == 0);
+ MOZ_RELEASE_ASSERT(S::destructCount == 0);
+
+ buf = vec.extractOrCopyRawBuffer();
+ MOZ_RELEASE_ASSERT(buf);
+ MOZ_RELEASE_ASSERT(vec.length() == 0);
+ MOZ_ASSERT(vec.reserved() == 0);
+ MOZ_RELEASE_ASSERT(S::constructCount == 10);
+ MOZ_RELEASE_ASSERT(S::moveCount == 0);
+ MOZ_RELEASE_ASSERT(S::destructCount == 0);
+
+ for (size_t i = 0; i < 10; i++) {
+ MOZ_RELEASE_ASSERT(buf[i].j == i);
+ MOZ_RELEASE_ASSERT(*buf[i].k == i * i);
+ }
+
+ free(buf);
+}
+
+int
+main()
+{
+ VectorTesting::testReserved();
+ VectorTesting::testConstRange();
+ VectorTesting::testEmplaceBack();
+ VectorTesting::testReverse();
+ VectorTesting::testExtractRawBuffer();
+ VectorTesting::testExtractOrCopyRawBuffer();
+}
diff --git a/mfbt/tests/TestWeakPtr.cpp b/mfbt/tests/TestWeakPtr.cpp
new file mode 100644
index 000000000..15060f00d
--- /dev/null
+++ b/mfbt/tests/TestWeakPtr.cpp
@@ -0,0 +1,123 @@
+/* -*- 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/WeakPtr.h"
+
+using mozilla::SupportsWeakPtr;
+using mozilla::WeakPtr;
+
+// To have a class C support weak pointers, inherit from SupportsWeakPtr<C>.
+class C : public SupportsWeakPtr<C>
+{
+public:
+ MOZ_DECLARE_WEAKREFERENCE_TYPENAME(C)
+
+ int mNum;
+
+ C()
+ : mNum(0)
+ {}
+
+ ~C()
+ {
+ // Setting mNum in the destructor allows us to test against use-after-free below
+ mNum = 0xDEAD;
+ }
+
+ void act() {}
+
+ bool isConst() {
+ return false;
+ }
+
+ bool isConst() const {
+ return true;
+ }
+};
+
+bool isConst(C*)
+{
+ return false;
+}
+
+bool isConst(const C*)
+{
+ return true;
+}
+
+int
+main()
+{
+ C* c1 = new C;
+ MOZ_RELEASE_ASSERT(c1->mNum == 0);
+
+ // Get weak pointers to c1. The first time,
+ // a reference-counted WeakReference object is created that
+ // can live beyond the lifetime of 'c1'. The WeakReference
+ // object will be notified of 'c1's destruction.
+ WeakPtr<C> w1 = c1;
+ // Test a weak pointer for validity before using it.
+ MOZ_RELEASE_ASSERT(w1);
+ MOZ_RELEASE_ASSERT(w1 == c1);
+ w1->mNum = 1;
+ w1->act();
+
+ // Test taking another WeakPtr<C> to c1
+ WeakPtr<C> w2 = c1;
+ MOZ_RELEASE_ASSERT(w2);
+ MOZ_RELEASE_ASSERT(w2 == c1);
+ MOZ_RELEASE_ASSERT(w2 == w1);
+ MOZ_RELEASE_ASSERT(w2->mNum == 1);
+
+ // Test a WeakPtr<const C>
+ WeakPtr<const C> w3const = c1;
+ MOZ_RELEASE_ASSERT(w3const);
+ MOZ_RELEASE_ASSERT(w3const == c1);
+ MOZ_RELEASE_ASSERT(w3const == w1);
+ MOZ_RELEASE_ASSERT(w3const == w2);
+ MOZ_RELEASE_ASSERT(w3const->mNum == 1);
+
+ // Test const-correctness of operator-> and operator T*
+ MOZ_RELEASE_ASSERT(!w1->isConst());
+ MOZ_RELEASE_ASSERT(w3const->isConst());
+ MOZ_RELEASE_ASSERT(!isConst(w1));
+ MOZ_RELEASE_ASSERT(isConst(w3const));
+
+ // Test that when a WeakPtr is destroyed, it does not destroy the object that it points to,
+ // and it does not affect other WeakPtrs pointing to the same object (e.g. it does not
+ // destroy the WeakReference object).
+ {
+ WeakPtr<C> w4local = c1;
+ MOZ_RELEASE_ASSERT(w4local == c1);
+ }
+ // Now w4local has gone out of scope. If that had destroyed c1, then the following would fail
+ // for sure (see C::~C()).
+ MOZ_RELEASE_ASSERT(c1->mNum == 1);
+ // Check that w4local going out of scope hasn't affected other WeakPtr's pointing to c1
+ MOZ_RELEASE_ASSERT(w1 == c1);
+ MOZ_RELEASE_ASSERT(w2 == c1);
+
+ // Now construct another C object and test changing what object a WeakPtr points to
+ C* c2 = new C;
+ c2->mNum = 2;
+ MOZ_RELEASE_ASSERT(w2->mNum == 1); // w2 was pointing to c1
+ w2 = c2;
+ MOZ_RELEASE_ASSERT(w2);
+ MOZ_RELEASE_ASSERT(w2 == c2);
+ MOZ_RELEASE_ASSERT(w2 != c1);
+ MOZ_RELEASE_ASSERT(w2 != w1);
+ MOZ_RELEASE_ASSERT(w2->mNum == 2);
+
+ // Destroying the underlying object clears weak pointers to it.
+ // It should not affect pointers that are not currently pointing to it.
+ delete c1;
+ MOZ_RELEASE_ASSERT(!w1, "Deleting an object should clear WeakPtr's to it.");
+ MOZ_RELEASE_ASSERT(!w3const, "Deleting an object should clear WeakPtr's to it.");
+ MOZ_RELEASE_ASSERT(w2, "Deleting an object should not clear WeakPtr that are not pointing to it.");
+
+ delete c2;
+ MOZ_RELEASE_ASSERT(!w2, "Deleting an object should clear WeakPtr's to it.");
+}
diff --git a/mfbt/tests/TestXorShift128PlusRNG.cpp b/mfbt/tests/TestXorShift128PlusRNG.cpp
new file mode 100644
index 000000000..dbe83d049
--- /dev/null
+++ b/mfbt/tests/TestXorShift128PlusRNG.cpp
@@ -0,0 +1,115 @@
+/* -*- 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 <math.h>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/XorShift128PlusRNG.h"
+
+using mozilla::non_crypto::XorShift128PlusRNG;
+
+static void
+TestDumbSequence()
+{
+ XorShift128PlusRNG rng(1, 4);
+
+ // Calculated by hand following the algorithm given in the paper. The upper
+ // bits are mostly zero because we started with a poor seed; once it has run
+ // for a while, we'll get an even mix of ones and zeros in all 64 bits.
+ MOZ_RELEASE_ASSERT(rng.next() == 0x800049);
+ MOZ_RELEASE_ASSERT(rng.next() == 0x3000186);
+ MOZ_RELEASE_ASSERT(rng.next() == 0x400003001145);
+
+ // Using ldexp here lets us write out the mantissa in hex, so we can compare
+ // them with the results generated by hand.
+ MOZ_RELEASE_ASSERT(rng.nextDouble()
+ == ldexp(static_cast<double>(0x1400003105049), -53));
+ MOZ_RELEASE_ASSERT(rng.nextDouble()
+ == ldexp(static_cast<double>(0x2000802e49146), -53));
+ MOZ_RELEASE_ASSERT(rng.nextDouble()
+ == ldexp(static_cast<double>(0x248300468544d), -53));
+}
+
+static size_t
+Population(uint64_t n)
+{
+ size_t pop = 0;
+
+ while (n > 0) {
+ n &= n-1; // Clear the rightmost 1-bit in n.
+ pop++;
+ }
+
+ return pop;
+}
+
+static void
+TestPopulation()
+{
+ XorShift128PlusRNG rng(698079309544035222ULL, 6012389156611637584ULL);
+
+ // Give it some time to warm up; it should tend towards more
+ // even distributions of zeros and ones.
+ for (size_t i = 0; i < 40; i++)
+ rng.next();
+
+ for (size_t i = 0; i < 40; i++) {
+ size_t pop = Population(rng.next());
+ MOZ_RELEASE_ASSERT(24 <= pop && pop <= 40);
+ }
+}
+
+static void
+TestSetState()
+{
+ static const uint64_t seed[2] = { 1795644156779822404ULL, 14162896116325912595ULL };
+ XorShift128PlusRNG rng(seed[0], seed[1]);
+
+ const size_t n = 10;
+ uint64_t log[n];
+
+ for (size_t i = 0; i < n; i++)
+ log[i] = rng.next();
+
+ rng.setState(seed[0], seed[1]);
+
+ for (size_t i = 0; i < n; i++)
+ MOZ_RELEASE_ASSERT(log[i] == rng.next());
+}
+
+static void
+TestDoubleDistribution()
+{
+ XorShift128PlusRNG rng(0xa207aaede6859736, 0xaca6ca5060804791);
+
+ const size_t n = 100;
+ size_t bins[n];
+ mozilla::PodArrayZero(bins);
+
+ // This entire file runs in 0.006s on my laptop. Generating
+ // more numbers lets us put tighter bounds on the bins.
+ for (size_t i = 0; i < 100000; i++) {
+ double d = rng.nextDouble();
+ MOZ_RELEASE_ASSERT(0.0 <= d && d < 1.0);
+ bins[(int) (d * n)]++;
+ }
+
+ for (size_t i = 0; i < n; i++) {
+ MOZ_RELEASE_ASSERT(900 <= bins[i] && bins[i] <= 1100);
+ }
+}
+
+int
+main()
+{
+ TestDumbSequence();
+ TestPopulation();
+ TestSetState();
+ TestDoubleDistribution();
+
+ return 0;
+}
diff --git a/mfbt/tests/moz.build b/mfbt/tests/moz.build
new file mode 100644
index 000000000..f96117e03
--- /dev/null
+++ b/mfbt/tests/moz.build
@@ -0,0 +1,76 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+CppUnitTests([
+ 'TestArray',
+ 'TestArrayUtils',
+ 'TestAtomics',
+ 'TestBinarySearch',
+ 'TestBloomFilter',
+ 'TestBufferList',
+ 'TestCasting',
+ 'TestCeilingFloor',
+ 'TestCheckedInt',
+ 'TestCountPopulation',
+ 'TestCountZeroes',
+ 'TestEndian',
+ 'TestEnumeratedArray',
+ 'TestEnumSet',
+ 'TestEnumTypeTraits',
+ 'TestFastBernoulliTrial',
+ 'TestFloatingPoint',
+ 'TestFunction',
+ 'TestIntegerPrintfMacros',
+ 'TestIntegerRange',
+ 'TestJSONWriter',
+ 'TestLinkedList',
+ 'TestMacroArgs',
+ 'TestMacroForEach',
+ 'TestMathAlgorithms',
+ 'TestMaybe',
+ 'TestNotNull',
+ 'TestPair',
+ 'TestRange',
+ 'TestRefPtr',
+ 'TestRollingMean',
+ 'TestSaturate',
+ 'TestScopeExit',
+ 'TestSegmentedVector',
+ 'TestSHA1',
+ 'TestSplayTree',
+ 'TestTemplateLib',
+ 'TestTuple',
+ 'TestTypedEnum',
+ 'TestTypeTraits',
+ 'TestUniquePtr',
+ 'TestVariant',
+ 'TestVector',
+ 'TestWeakPtr',
+ 'TestXorShift128PlusRNG',
+])
+
+if not CONFIG['MOZ_ASAN']:
+ CppUnitTests([
+ 'TestPoisonArea',
+ ])
+
+# Since we link directly with MFBT object files, define IMPL_MFBT
+DEFINES['IMPL_MFBT'] = True
+
+DISABLE_STL_WRAPPING = True
+
+if CONFIG['_MSC_VER']:
+ CXXFLAGS += [
+ '-wd4275', # non dll-interface class used as base for dll-interface class
+ '-wd4530', # C++ exception handler used, but unwind semantics are not enabled
+ ]
+
+USE_LIBS += [
+ 'mfbt',
+]
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']