summaryrefslogtreecommitdiffstats
path: root/mfbt/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /mfbt/tests
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--mfbt/tests/TestAlgorithm.cpp68
-rw-r--r--mfbt/tests/TestArray.cpp31
-rw-r--r--mfbt/tests/TestArrayUtils.cpp301
-rw-r--r--mfbt/tests/TestAtomicBitfields.cpp189
-rw-r--r--mfbt/tests/TestAtomics.cpp274
-rw-r--r--mfbt/tests/TestBinarySearch.cpp158
-rw-r--r--mfbt/tests/TestBitSet.cpp117
-rw-r--r--mfbt/tests/TestBloomFilter.cpp142
-rw-r--r--mfbt/tests/TestBufferList.cpp372
-rw-r--r--mfbt/tests/TestCasting.cpp255
-rw-r--r--mfbt/tests/TestCeilingFloor.cpp81
-rw-r--r--mfbt/tests/TestCheckedInt.cpp615
-rw-r--r--mfbt/tests/TestCompactPair.cpp160
-rw-r--r--mfbt/tests/TestCountPopulation.cpp30
-rw-r--r--mfbt/tests/TestCountZeroes.cpp92
-rw-r--r--mfbt/tests/TestDefineEnum.cpp78
-rw-r--r--mfbt/tests/TestDoublyLinkedList.cpp306
-rw-r--r--mfbt/tests/TestEndian.cpp501
-rw-r--r--mfbt/tests/TestEnumSet.cpp306
-rw-r--r--mfbt/tests/TestEnumTypeTraits.cpp159
-rw-r--r--mfbt/tests/TestEnumeratedArray.cpp46
-rw-r--r--mfbt/tests/TestFastBernoulliTrial.cpp177
-rw-r--r--mfbt/tests/TestFloatingPoint.cpp730
-rw-r--r--mfbt/tests/TestFunctionRef.cpp144
-rw-r--r--mfbt/tests/TestFunctionTypeTraits.cpp232
-rw-r--r--mfbt/tests/TestHashTable.cpp103
-rw-r--r--mfbt/tests/TestIntegerRange.cpp150
-rw-r--r--mfbt/tests/TestJSONWriter.cpp657
-rw-r--r--mfbt/tests/TestLinkedList.cpp399
-rw-r--r--mfbt/tests/TestMacroArgs.cpp38
-rw-r--r--mfbt/tests/TestMacroForEach.cpp44
-rw-r--r--mfbt/tests/TestMathAlgorithms.cpp545
-rw-r--r--mfbt/tests/TestMaybe.cpp1473
-rw-r--r--mfbt/tests/TestNonDereferenceable.cpp171
-rw-r--r--mfbt/tests/TestNotNull.cpp386
-rw-r--r--mfbt/tests/TestPoisonArea.cpp530
-rw-r--r--mfbt/tests/TestRandomNum.cpp61
-rw-r--r--mfbt/tests/TestRange.cpp29
-rw-r--r--mfbt/tests/TestRefPtr.cpp131
-rw-r--r--mfbt/tests/TestResult.cpp870
-rw-r--r--mfbt/tests/TestRollingMean.cpp114
-rw-r--r--mfbt/tests/TestSHA1.cpp204
-rw-r--r--mfbt/tests/TestSIMD.cpp631
-rw-r--r--mfbt/tests/TestSPSCQueue.cpp302
-rw-r--r--mfbt/tests/TestSaturate.cpp181
-rw-r--r--mfbt/tests/TestScopeExit.cpp55
-rw-r--r--mfbt/tests/TestSegmentedVector.cpp388
-rw-r--r--mfbt/tests/TestSmallPointerArray.cpp237
-rw-r--r--mfbt/tests/TestSplayTree.cpp208
-rw-r--r--mfbt/tests/TestTextUtils.cpp1064
-rw-r--r--mfbt/tests/TestThreadSafeWeakPtr.cpp127
-rw-r--r--mfbt/tests/TestTypedEnum.cpp502
-rw-r--r--mfbt/tests/TestUniquePtr.cpp609
-rw-r--r--mfbt/tests/TestUtf8.cpp755
-rw-r--r--mfbt/tests/TestVariant.cpp1153
-rw-r--r--mfbt/tests/TestVector.cpp792
-rw-r--r--mfbt/tests/TestWeakPtr.cpp145
-rw-r--r--mfbt/tests/TestWinArchDefs.cpp58
-rw-r--r--mfbt/tests/TestWrappingOperations.cpp587
-rw-r--r--mfbt/tests/TestXorShift128PlusRNG.cpp101
-rw-r--r--mfbt/tests/gtest/TestAlgorithm.cpp191
-rw-r--r--mfbt/tests/gtest/TestBuffer.cpp96
-rw-r--r--mfbt/tests/gtest/TestInitializedOnce.cpp200
-rw-r--r--mfbt/tests/gtest/TestLinkedList.cpp78
-rw-r--r--mfbt/tests/gtest/TestMainThreadWeakPtr.cpp42
-rw-r--r--mfbt/tests/gtest/TestMozDbg.cpp170
-rw-r--r--mfbt/tests/gtest/TestResultExtensions.cpp579
-rw-r--r--mfbt/tests/gtest/TestReverseIterator.cpp104
-rw-r--r--mfbt/tests/gtest/TestSpan.cpp2355
-rw-r--r--mfbt/tests/gtest/TestTainting.cpp485
-rw-r--r--mfbt/tests/gtest/moz.build32
-rw-r--r--mfbt/tests/moz.build117
72 files changed, 23813 insertions, 0 deletions
diff --git a/mfbt/tests/TestAlgorithm.cpp b/mfbt/tests/TestAlgorithm.cpp
new file mode 100644
index 0000000000..c5b0ffff12
--- /dev/null
+++ b/mfbt/tests/TestAlgorithm.cpp
@@ -0,0 +1,68 @@
+/* -*- 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/Algorithm.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+
+#include <iterator>
+
+static constexpr bool even(int32_t n) { return !(n & 1); }
+static constexpr bool odd(int32_t n) { return (n & 1); }
+
+using namespace mozilla;
+
+void TestAllOf() {
+ using std::begin;
+ using std::end;
+
+ constexpr static int32_t arr1[3] = {1, 2, 3};
+ MOZ_RELEASE_ASSERT(!AllOf(begin(arr1), end(arr1), even));
+ MOZ_RELEASE_ASSERT(!AllOf(begin(arr1), end(arr1), odd));
+ static_assert(!AllOf(arr1, arr1 + ArrayLength(arr1), even), "1-1");
+ static_assert(!AllOf(arr1, arr1 + ArrayLength(arr1), odd), "1-2");
+
+ constexpr static int32_t arr2[3] = {1, 3, 5};
+ MOZ_RELEASE_ASSERT(!AllOf(begin(arr2), end(arr2), even));
+ MOZ_RELEASE_ASSERT(AllOf(begin(arr2), end(arr2), odd));
+ static_assert(!AllOf(arr2, arr2 + ArrayLength(arr2), even), "2-1");
+ static_assert(AllOf(arr2, arr2 + ArrayLength(arr2), odd), "2-2");
+
+ constexpr static int32_t arr3[3] = {2, 4, 6};
+ MOZ_RELEASE_ASSERT(AllOf(begin(arr3), end(arr3), even));
+ MOZ_RELEASE_ASSERT(!AllOf(begin(arr3), end(arr3), odd));
+ static_assert(AllOf(arr3, arr3 + ArrayLength(arr3), even), "3-1");
+ static_assert(!AllOf(arr3, arr3 + ArrayLength(arr3), odd), "3-2");
+}
+
+void TestAnyOf() {
+ using std::begin;
+ using std::end;
+
+ // The Android NDK's STL doesn't support `constexpr` `std::array::begin`, see
+ // bug 1677484. Hence using a raw array here.
+ constexpr int32_t arr1[1] = {0};
+ static_assert(!AnyOf(arr1, arr1, even));
+ static_assert(!AnyOf(arr1, arr1, odd));
+
+ constexpr int32_t arr2[] = {1};
+ static_assert(!AnyOf(begin(arr2), end(arr2), even));
+ static_assert(AnyOf(begin(arr2), end(arr2), odd));
+
+ constexpr int32_t arr3[] = {2};
+ static_assert(AnyOf(begin(arr3), end(arr3), even));
+ static_assert(!AnyOf(begin(arr3), end(arr3), odd));
+
+ constexpr int32_t arr4[] = {1, 2};
+ static_assert(AnyOf(begin(arr4), end(arr4), even));
+ static_assert(AnyOf(begin(arr4), end(arr4), odd));
+}
+
+int main() {
+ TestAllOf();
+ TestAnyOf();
+ return 0;
+}
diff --git a/mfbt/tests/TestArray.cpp b/mfbt/tests/TestArray.cpp
new file mode 100644
index 0000000000..ff41001e0a
--- /dev/null
+++ b/mfbt/tests/TestArray.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/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;
+}
diff --git a/mfbt/tests/TestArrayUtils.cpp b/mfbt/tests/TestArrayUtils.cpp
new file mode 100644
index 0000000000..b50531a3a8
--- /dev/null
+++ b/mfbt/tests/TestArrayUtils.cpp
@@ -0,0 +1,301 @@
+/* -*- 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/TestAtomicBitfields.cpp b/mfbt/tests/TestAtomicBitfields.cpp
new file mode 100644
index 0000000000..237dbde538
--- /dev/null
+++ b/mfbt/tests/TestAtomicBitfields.cpp
@@ -0,0 +1,189 @@
+/* -*- 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/AtomicBitfields.h"
+
+// This is a big macro mess, so let's summarize what's in here right up front:
+//
+// |TestDocumentationExample| is intended to be a copy-paste of the example
+// in the macro's documentation, to make sure it's correct.
+//
+//
+// |TestJammedWithFlags| tests using every bit of the type for bool flags.
+// 64-bit isn't tested due to macro limitations.
+//
+//
+// |TestLopsided| tests an instance with the following configuration:
+//
+// * a 1-bit boolean
+// * an (N-1)-bit uintN_t
+//
+// It tests both orderings of these fields.
+//
+// Hopefully these are enough to cover all the nasty boundary conditions
+// (that still compile).
+
+// ==================== TestDocumentationExample ========================
+
+struct MyType {
+ MOZ_ATOMIC_BITFIELDS(mAtomicFields, 8,
+ ((bool, IsDownloaded, 1), (uint32_t, SomeData, 2),
+ (uint8_t, OtherData, 5)))
+
+ int32_t aNormalInteger;
+
+ explicit MyType(uint32_t aSomeData) : aNormalInteger(7) {
+ StoreSomeData(aSomeData);
+ // Other bitfields were already default initialized to 0/false
+ }
+};
+
+void TestDocumentationExample() {
+ MyType val(3);
+
+ if (!val.LoadIsDownloaded()) {
+ val.StoreOtherData(2);
+ val.StoreIsDownloaded(true);
+ }
+}
+
+// ====================== TestJammedWithFlags =========================
+
+#define TIMES_8(aFunc, aSeparator, aArgs) \
+ MOZ_FOR_EACH_SEPARATED(aFunc, aSeparator, aArgs, (1, 2, 3, 4, 5, 6, 7, 8))
+#define TIMES_16(aFunc, aSeparator, aArgs) \
+ MOZ_FOR_EACH_SEPARATED( \
+ aFunc, aSeparator, aArgs, \
+ (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16))
+#define TIMES_32(aFunc, aSeparator, aArgs) \
+ MOZ_FOR_EACH_SEPARATED( \
+ aFunc, aSeparator, aArgs, \
+ (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, \
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32))
+
+#define CHECK_BOOL(aIndex) \
+ MOZ_ASSERT(val.LoadFlag##aIndex() == false); \
+ val.StoreFlag##aIndex(true); \
+ MOZ_ASSERT(val.LoadFlag##aIndex() == true); \
+ val.StoreFlag##aIndex(false); \
+ MOZ_ASSERT(val.LoadFlag##aIndex() == false);
+
+#define GENERATE_TEST_JAMMED_WITH_FLAGS(aSize) \
+ void TestJammedWithFlags##aSize() { \
+ JammedWithFlags##aSize val; \
+ TIMES_##aSize(CHECK_BOOL, (;), ()); \
+ }
+
+#define TEST_JAMMED_WITH_FLAGS(aSize) TestJammedWithFlags##aSize();
+
+// ========================= TestLopsided ===========================
+
+#define GENERATE_TEST_LOPSIDED_FUNC(aSide, aSize) \
+ void TestLopsided##aSide##aSize() { \
+ Lopsided##aSide##aSize val; \
+ MOZ_ASSERT(val.LoadHappyLittleBit() == false); \
+ MOZ_ASSERT(val.LoadLargeAndInCharge() == 0); \
+ val.StoreHappyLittleBit(true); \
+ MOZ_ASSERT(val.LoadHappyLittleBit() == true); \
+ MOZ_ASSERT(val.LoadLargeAndInCharge() == 0); \
+ val.StoreLargeAndInCharge(1); \
+ MOZ_ASSERT(val.LoadHappyLittleBit() == true); \
+ MOZ_ASSERT(val.LoadLargeAndInCharge() == 1); \
+ val.StoreLargeAndInCharge(0); \
+ MOZ_ASSERT(val.LoadHappyLittleBit() == true); \
+ MOZ_ASSERT(val.LoadLargeAndInCharge() == 0); \
+ uint##aSize##_t size = aSize; \
+ uint##aSize##_t int_max = (~(1ull << (size - 1))) - 1; \
+ val.StoreLargeAndInCharge(int_max); \
+ MOZ_ASSERT(val.LoadHappyLittleBit() == true); \
+ MOZ_ASSERT(val.LoadLargeAndInCharge() == int_max); \
+ val.StoreHappyLittleBit(false); \
+ MOZ_ASSERT(val.LoadHappyLittleBit() == false); \
+ MOZ_ASSERT(val.LoadLargeAndInCharge() == int_max); \
+ val.StoreLargeAndInCharge(int_max); \
+ MOZ_ASSERT(val.LoadHappyLittleBit() == false); \
+ MOZ_ASSERT(val.LoadLargeAndInCharge() == int_max); \
+ }
+
+#define GENERATE_TEST_LOPSIDED(aSize) \
+ struct LopsidedA##aSize { \
+ MOZ_ATOMIC_BITFIELDS(mAtomicFields, aSize, \
+ ((bool, HappyLittleBit, 1), \
+ (uint##aSize##_t, LargeAndInCharge, ((aSize)-1)))) \
+ }; \
+ struct LopsidedB##aSize { \
+ MOZ_ATOMIC_BITFIELDS(mAtomicFields, aSize, \
+ ((uint##aSize##_t, LargeAndInCharge, ((aSize)-1)), \
+ (bool, HappyLittleBit, 1))) \
+ }; \
+ GENERATE_TEST_LOPSIDED_FUNC(A, aSize); \
+ GENERATE_TEST_LOPSIDED_FUNC(B, aSize);
+
+#define TEST_LOPSIDED(aSize) \
+ TestLopsidedA##aSize(); \
+ TestLopsidedB##aSize();
+
+// ==================== generate and run the tests ======================
+
+// There's an unknown bug in clang-cl-9 (used for win64-ccov) that makes
+// generating these with the TIMES_N macro not work. So these are written out
+// explicitly to unbork CI.
+struct JammedWithFlags8 {
+ MOZ_ATOMIC_BITFIELDS(mAtomicFields, 8,
+ ((bool, Flag1, 1), (bool, Flag2, 1), (bool, Flag3, 1),
+ (bool, Flag4, 1), (bool, Flag5, 1), (bool, Flag6, 1),
+ (bool, Flag7, 1), (bool, Flag8, 1)))
+};
+
+struct JammedWithFlags16 {
+ MOZ_ATOMIC_BITFIELDS(mAtomicFields, 16,
+ ((bool, Flag1, 1), (bool, Flag2, 1), (bool, Flag3, 1),
+ (bool, Flag4, 1), (bool, Flag5, 1), (bool, Flag6, 1),
+ (bool, Flag7, 1), (bool, Flag8, 1), (bool, Flag9, 1),
+ (bool, Flag10, 1), (bool, Flag11, 1), (bool, Flag12, 1),
+ (bool, Flag13, 1), (bool, Flag14, 1), (bool, Flag15, 1),
+ (bool, Flag16, 1)))
+};
+
+struct JammedWithFlags32 {
+ MOZ_ATOMIC_BITFIELDS(mAtomicFields, 32,
+ ((bool, Flag1, 1), (bool, Flag2, 1), (bool, Flag3, 1),
+ (bool, Flag4, 1), (bool, Flag5, 1), (bool, Flag6, 1),
+ (bool, Flag7, 1), (bool, Flag8, 1), (bool, Flag9, 1),
+ (bool, Flag10, 1), (bool, Flag11, 1), (bool, Flag12, 1),
+ (bool, Flag13, 1), (bool, Flag14, 1), (bool, Flag15, 1),
+ (bool, Flag16, 1), (bool, Flag17, 1), (bool, Flag18, 1),
+ (bool, Flag19, 1), (bool, Flag20, 1), (bool, Flag21, 1),
+ (bool, Flag22, 1), (bool, Flag23, 1), (bool, Flag24, 1),
+ (bool, Flag25, 1), (bool, Flag26, 1), (bool, Flag27, 1),
+ (bool, Flag28, 1), (bool, Flag29, 1), (bool, Flag30, 1),
+ (bool, Flag31, 1), (bool, Flag32, 1)))
+};
+
+GENERATE_TEST_JAMMED_WITH_FLAGS(8)
+GENERATE_TEST_JAMMED_WITH_FLAGS(16)
+GENERATE_TEST_JAMMED_WITH_FLAGS(32)
+// MOZ_FOR_EACH_64 doesn't exist :(
+
+GENERATE_TEST_LOPSIDED(8)
+GENERATE_TEST_LOPSIDED(16)
+GENERATE_TEST_LOPSIDED(32)
+GENERATE_TEST_LOPSIDED(64)
+
+int main() {
+ TestDocumentationExample();
+
+ TEST_JAMMED_WITH_FLAGS(8);
+ TEST_JAMMED_WITH_FLAGS(16);
+ TEST_JAMMED_WITH_FLAGS(32);
+
+ TEST_LOPSIDED(8);
+ TEST_LOPSIDED(16);
+ TEST_LOPSIDED(32);
+ TEST_LOPSIDED(64);
+ return 0;
+}
diff --git a/mfbt/tests/TestAtomics.cpp b/mfbt/tests/TestAtomics.cpp
new file mode 100644
index 0000000000..7d333d37c1
--- /dev/null
+++ b/mfbt/tests/TestAtomics.cpp
@@ -0,0 +1,274 @@
+/* -*- 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 0000000000..3cd28b309f
--- /dev/null
+++ b/mfbt/tests/TestBinarySearch.cpp
@@ -0,0 +1,158 @@
+/* -*- 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"
+
+#include <cstdlib>
+
+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);
+}
+
+static void TestEqualRange() {
+ struct CompareN {
+ int mVal;
+ explicit CompareN(int n) : mVal(n) {}
+ int operator()(int aVal) const { return mVal - aVal; }
+ };
+
+ constexpr int kMaxNumber = 100;
+ constexpr int kMaxRepeat = 2;
+
+ Vector<int> sortedArray;
+ MOZ_RELEASE_ASSERT(sortedArray.reserve(kMaxNumber * kMaxRepeat));
+
+ // Make a sorted array by appending the loop counter [0, kMaxRepeat] times
+ // in each iteration. The array will be something like:
+ // [0, 0, 1, 1, 2, 2, 8, 9, ..., kMaxNumber]
+ for (int i = 0; i <= kMaxNumber; ++i) {
+ int repeat = rand() % (kMaxRepeat + 1);
+ for (int j = 0; j < repeat; ++j) {
+ MOZ_RELEASE_ASSERT(sortedArray.emplaceBack(i));
+ }
+ }
+
+ for (int i = -1; i < kMaxNumber + 1; ++i) {
+ auto bounds = EqualRange(sortedArray, 0, sortedArray.length(), CompareN(i));
+
+ MOZ_RELEASE_ASSERT(bounds.first <= sortedArray.length());
+ MOZ_RELEASE_ASSERT(bounds.second <= sortedArray.length());
+ MOZ_RELEASE_ASSERT(bounds.first <= bounds.second);
+
+ if (bounds.first == 0) {
+ MOZ_RELEASE_ASSERT(sortedArray[0] >= i);
+ } else if (bounds.first == sortedArray.length()) {
+ MOZ_RELEASE_ASSERT(sortedArray[sortedArray.length() - 1] < i);
+ } else {
+ MOZ_RELEASE_ASSERT(sortedArray[bounds.first - 1] < i);
+ MOZ_RELEASE_ASSERT(sortedArray[bounds.first] >= i);
+ }
+
+ if (bounds.second == 0) {
+ MOZ_RELEASE_ASSERT(sortedArray[0] > i);
+ } else if (bounds.second == sortedArray.length()) {
+ MOZ_RELEASE_ASSERT(sortedArray[sortedArray.length() - 1] <= i);
+ } else {
+ MOZ_RELEASE_ASSERT(sortedArray[bounds.second - 1] <= i);
+ MOZ_RELEASE_ASSERT(sortedArray[bounds.second] > i);
+ }
+ }
+}
+
+int main() {
+ TestBinarySearch();
+ TestBinarySearchIf();
+ TestEqualRange();
+ return 0;
+}
diff --git a/mfbt/tests/TestBitSet.cpp b/mfbt/tests/TestBitSet.cpp
new file mode 100644
index 0000000000..2bd1923a15
--- /dev/null
+++ b/mfbt/tests/TestBitSet.cpp
@@ -0,0 +1,117 @@
+/* -*- 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/BitSet.h"
+
+using mozilla::BitSet;
+
+template <typename Storage>
+class BitSetSuite {
+ template <size_t N>
+ using TestBitSet = BitSet<N, Storage>;
+
+ static constexpr size_t kBitsPerWord = sizeof(Storage) * 8;
+
+ static constexpr Storage kAllBitsSet = ~Storage{0};
+
+ public:
+ void testLength() {
+ MOZ_RELEASE_ASSERT(TestBitSet<1>().Storage().LengthBytes() ==
+ sizeof(Storage));
+
+ MOZ_RELEASE_ASSERT(TestBitSet<1>().Storage().Length() == 1);
+ MOZ_RELEASE_ASSERT(TestBitSet<kBitsPerWord>().Storage().Length() == 1);
+ MOZ_RELEASE_ASSERT(TestBitSet<kBitsPerWord + 1>().Storage().Length() == 2);
+ }
+
+ void testConstruct() {
+ MOZ_RELEASE_ASSERT(TestBitSet<1>().Storage()[0] == 0);
+ MOZ_RELEASE_ASSERT(TestBitSet<kBitsPerWord>().Storage()[0] == 0);
+ MOZ_RELEASE_ASSERT(TestBitSet<kBitsPerWord + 1>().Storage()[0] == 0);
+ MOZ_RELEASE_ASSERT(TestBitSet<kBitsPerWord + 1>().Storage()[1] == 0);
+
+ TestBitSet<1> bitset1;
+ bitset1.SetAll();
+ TestBitSet<kBitsPerWord> bitsetW;
+ bitsetW.SetAll();
+ TestBitSet<kBitsPerWord + 1> bitsetW1;
+ bitsetW1.SetAll();
+
+ MOZ_RELEASE_ASSERT(bitset1.Storage()[0] == 1);
+ MOZ_RELEASE_ASSERT(bitsetW.Storage()[0] == kAllBitsSet);
+ MOZ_RELEASE_ASSERT(bitsetW1.Storage()[0] == kAllBitsSet);
+ MOZ_RELEASE_ASSERT(bitsetW1.Storage()[1] == 1);
+
+ MOZ_RELEASE_ASSERT(TestBitSet<1>(bitset1).Storage()[0] == 1);
+ MOZ_RELEASE_ASSERT(TestBitSet<kBitsPerWord>(bitsetW).Storage()[0] ==
+ kAllBitsSet);
+ MOZ_RELEASE_ASSERT(TestBitSet<kBitsPerWord + 1>(bitsetW1).Storage()[0] ==
+ kAllBitsSet);
+ MOZ_RELEASE_ASSERT(TestBitSet<kBitsPerWord + 1>(bitsetW1).Storage()[1] ==
+ 1);
+
+ MOZ_RELEASE_ASSERT(TestBitSet<1>(bitset1.Storage()).Storage()[0] == 1);
+ MOZ_RELEASE_ASSERT(
+ TestBitSet<kBitsPerWord>(bitsetW.Storage()).Storage()[0] ==
+ kAllBitsSet);
+ MOZ_RELEASE_ASSERT(
+ TestBitSet<kBitsPerWord + 1>(bitsetW1.Storage()).Storage()[0] ==
+ kAllBitsSet);
+ MOZ_RELEASE_ASSERT(
+ TestBitSet<kBitsPerWord + 1>(bitsetW1.Storage()).Storage()[1] == 1);
+ }
+
+ void testSetBit() {
+ TestBitSet<kBitsPerWord + 2> bitset;
+ MOZ_RELEASE_ASSERT(!bitset.Test(3));
+ MOZ_RELEASE_ASSERT(!bitset[3]);
+ MOZ_RELEASE_ASSERT(!bitset.Test(kBitsPerWord + 1));
+ MOZ_RELEASE_ASSERT(!bitset[kBitsPerWord + 1]);
+
+ bitset[3] = true;
+ MOZ_RELEASE_ASSERT(bitset.Test(3));
+ MOZ_RELEASE_ASSERT(bitset[3]);
+
+ bitset[kBitsPerWord + 1] = true;
+ MOZ_RELEASE_ASSERT(bitset.Test(3));
+ MOZ_RELEASE_ASSERT(bitset[3]);
+ MOZ_RELEASE_ASSERT(bitset.Test(kBitsPerWord + 1));
+ MOZ_RELEASE_ASSERT(bitset[kBitsPerWord + 1]);
+
+ bitset.ResetAll();
+ for (size_t i = 0; i < decltype(bitset)::Size(); i++) {
+ MOZ_RELEASE_ASSERT(!bitset[i]);
+ }
+
+ bitset.SetAll();
+ for (size_t i = 0; i < decltype(bitset)::Size(); i++) {
+ MOZ_RELEASE_ASSERT(bitset[i]);
+ }
+
+ // Test trailing unused bits are not set by SetAll().
+ MOZ_RELEASE_ASSERT(bitset.Storage()[1] == 3);
+
+ bitset.ResetAll();
+ for (size_t i = 0; i < decltype(bitset)::Size(); i++) {
+ MOZ_RELEASE_ASSERT(!bitset[i]);
+ }
+ }
+
+ void runTests() {
+ testLength();
+ testConstruct();
+ testSetBit();
+ }
+};
+
+int main() {
+ BitSetSuite<uint8_t>().runTests();
+ BitSetSuite<uint32_t>().runTests();
+ BitSetSuite<uint64_t>().runTests();
+
+ return 0;
+}
diff --git a/mfbt/tests/TestBloomFilter.cpp b/mfbt/tests/TestBloomFilter.cpp
new file mode 100644
index 0000000000..a233858826
--- /dev/null
+++ b/mfbt/tests/TestBloomFilter.cpp
@@ -0,0 +1,142 @@
+/* -*- 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 "mozilla/UniquePtr.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+using mozilla::BitBloomFilter;
+using mozilla::CountingBloomFilter;
+
+class FilterChecker {
+ public:
+ explicit FilterChecker(uint32_t aHash) : mHash(aHash) {}
+
+ uint32_t hash() const { return mHash; }
+
+ private:
+ uint32_t mHash;
+};
+
+void testBitBloomFilter() {
+ const mozilla::UniquePtr filter =
+ mozilla::MakeUnique<BitBloomFilter<12, FilterChecker>>();
+ MOZ_RELEASE_ASSERT(filter);
+
+ FilterChecker one(1);
+ FilterChecker two(0x20000);
+
+ filter->add(&one);
+ MOZ_RELEASE_ASSERT(filter->mightContain(&one), "Filter should contain 'one'");
+
+ MOZ_RELEASE_ASSERT(!filter->mightContain(&two),
+ "Filter claims to contain 'two' when it should not");
+
+ // Test multiple addition
+ filter->add(&two);
+ MOZ_RELEASE_ASSERT(filter->mightContain(&two),
+ "Filter should contain 'two' after 'two' is added");
+ filter->add(&two);
+ MOZ_RELEASE_ASSERT(filter->mightContain(&two),
+ "Filter should contain 'two' after 'two' is added again");
+
+ filter->clear();
+
+ MOZ_RELEASE_ASSERT(!filter->mightContain(&one), "clear() failed to work");
+ MOZ_RELEASE_ASSERT(!filter->mightContain(&two), "clear() failed to work");
+}
+
+void testCountingBloomFilter() {
+ const mozilla::UniquePtr filter =
+ mozilla::MakeUnique<CountingBloomFilter<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");
+}
+
+int main() {
+ testBitBloomFilter();
+ testCountingBloomFilter();
+
+ return 0;
+}
diff --git a/mfbt/tests/TestBufferList.cpp b/mfbt/tests/TestBufferList.cpp
new file mode 100644
index 0000000000..9c0d69d7d6
--- /dev/null
+++ b/mfbt/tests/TestBufferList.cpp
@@ -0,0 +1,372 @@
+/* -*- 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;
+ }
+
+ template <typename T>
+ void free_(T* aPtr, size_t aNumElems = 0) {
+ 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);
+ MOZ_ALWAYS_TRUE(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;
+ }
+ MOZ_ALWAYS_TRUE(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);
+ MOZ_ALWAYS_TRUE(bl2.WriteBytes(toWrite, kSmallWrite));
+ MOZ_ALWAYS_TRUE(bl2.WriteBytes(toWrite, kSmallWrite));
+ MOZ_ALWAYS_TRUE(bl2.WriteBytes(toWrite, kSmallWrite));
+
+ bl = std::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());
+
+ // RangeLength.
+
+ BufferList bl12(0, 0, 8);
+ MOZ_ALWAYS_TRUE(bl12.WriteBytes("abcdefgh", 8));
+ MOZ_ALWAYS_TRUE(bl12.WriteBytes("12345678", 8));
+
+ // |iter| is at position 0 (1st segment).
+ iter = bl12.Iter();
+ iter1 = bl12.Iter();
+ MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 0);
+ MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
+ MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 4);
+ MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
+ MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 8);
+ MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
+ MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 12);
+ MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 3));
+ MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 15);
+ MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 1));
+ MOZ_RELEASE_ASSERT(iter1.Done());
+
+ // |iter| is at position 1 (1st segment).
+ iter = bl12.Iter();
+ iter1 = bl12.Iter();
+ MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl12, 1));
+ MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 1));
+ MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 0);
+ MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
+ MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 4);
+ MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
+ MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 8);
+ MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
+ MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 12);
+ MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 2));
+ MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 14);
+ MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 1));
+ MOZ_RELEASE_ASSERT(iter1.Done());
+
+ // |iter| is at position 8 (2nd segment).
+ iter = bl12.Iter();
+ iter1 = bl12.Iter();
+ MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl12, 8));
+ MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 8));
+ MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 0);
+ MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
+ MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 4);
+ MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 3));
+ MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 7);
+ MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 1));
+ MOZ_RELEASE_ASSERT(iter1.Done());
+
+ // |iter| is at position 9 (2nd segment).
+ iter = bl12.Iter();
+ iter1 = bl12.Iter();
+ MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl12, 9));
+ MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 9));
+ MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 0);
+ MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
+ MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 4);
+ MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 2));
+ MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 6);
+ MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 1));
+ MOZ_RELEASE_ASSERT(iter1.Done());
+
+ BufferList bl13(0, 0, 8);
+ MOZ_ALWAYS_TRUE(bl13.WriteBytes("abcdefgh", 8));
+ MOZ_ALWAYS_TRUE(bl13.WriteBytes("12345678", 8));
+ MOZ_ALWAYS_TRUE(bl13.WriteBytes("ABCDEFGH", 8));
+ MOZ_RELEASE_ASSERT(bl13.Size() == 24);
+
+ // At segment border
+ iter = bl13.Iter();
+ MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl13, 8));
+ MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 16);
+ MOZ_RELEASE_ASSERT(iter.Done());
+ MOZ_RELEASE_ASSERT(bl13.Size() == 8);
+
+ // Restore state
+ MOZ_ALWAYS_TRUE(bl13.WriteBytes("12345678", 8));
+ MOZ_ALWAYS_TRUE(bl13.WriteBytes("ABCDEFGH", 8));
+ MOZ_RELEASE_ASSERT(bl13.Size() == 24);
+
+ // Before segment border
+ iter = bl13.Iter();
+ MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl13, 7));
+ MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 17);
+ MOZ_RELEASE_ASSERT(iter.Done());
+ MOZ_RELEASE_ASSERT(bl13.Size() == 7);
+
+ // Restore state
+ MOZ_ALWAYS_TRUE(bl13.WriteBytes("h", 1));
+ MOZ_ALWAYS_TRUE(bl13.WriteBytes("12345678", 8));
+ MOZ_ALWAYS_TRUE(bl13.WriteBytes("ABCDEFGH", 8));
+ MOZ_RELEASE_ASSERT(bl13.Size() == 24);
+
+ // In last segment
+ iter = bl13.Iter();
+ MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl13, 20));
+ MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 4);
+ MOZ_RELEASE_ASSERT(iter.Done());
+ MOZ_RELEASE_ASSERT(bl13.Size() == 20);
+
+ // No-op truncate
+ MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 0);
+ MOZ_RELEASE_ASSERT(iter.Done());
+ MOZ_RELEASE_ASSERT(bl13.Size() == 20);
+
+ // No-op truncate with fresh iterator
+ iter = bl13.Iter();
+ MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl13, 20));
+ MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 0);
+ MOZ_RELEASE_ASSERT(iter.Done());
+ MOZ_RELEASE_ASSERT(bl13.Size() == 20);
+
+ // Truncate at start of buffer
+ iter = bl13.Iter();
+ MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 20);
+ MOZ_RELEASE_ASSERT(iter.Done());
+ MOZ_RELEASE_ASSERT(bl13.Size() == 0);
+
+ // No-op truncate at start of buffer
+ iter = bl13.Iter();
+ MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 0);
+ MOZ_RELEASE_ASSERT(iter.Done());
+ MOZ_RELEASE_ASSERT(bl13.Size() == 0);
+
+ return 0;
+}
diff --git a/mfbt/tests/TestCasting.cpp b/mfbt/tests/TestCasting.cpp
new file mode 100644
index 0000000000..9b040956c7
--- /dev/null
+++ b/mfbt/tests/TestCasting.cpp
@@ -0,0 +1,255 @@
+/* -*- 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 "mozilla/ThreadSafety.h"
+
+#include <stdint.h>
+#include <cstdint>
+#include <limits>
+#include <type_traits>
+
+using mozilla::AssertedCast;
+using mozilla::BitwiseCast;
+using mozilla::detail::IsInBounds;
+
+static const uint8_t floatMantissaBitsPlusOne = 24;
+static const uint8_t doubleMantissaBitsPlusOne = 53;
+
+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)));
+}
+
+template <typename In, typename Out>
+void checkBoundariesFloating(In aEpsilon = {}, Out aIntegerOffset = {}) {
+ // Check the max value of the input float can't be represented as an integer.
+ // This is true for all floating point and integer width.
+ MOZ_RELEASE_ASSERT((!IsInBounds<In, Out>(std::numeric_limits<In>::max())));
+ // Check that the max value of the integer, as a float, minus an offset that
+ // depends on the magnitude, can be represented as an integer.
+ MOZ_RELEASE_ASSERT((IsInBounds<In, Out>(
+ static_cast<In>(std::numeric_limits<Out>::max() - aIntegerOffset))));
+ // Check that the max value of the integer, plus a number that depends on the
+ // magnitude of the number, can't be represented as this integer (because it
+ // becomes too big).
+ MOZ_RELEASE_ASSERT((!IsInBounds<In, Out>(
+ aEpsilon + static_cast<In>(std::numeric_limits<Out>::max()))));
+ if constexpr (std::is_signed_v<In>) {
+ // Same for negative numbers.
+ MOZ_RELEASE_ASSERT(
+ (!IsInBounds<In, Out>(std::numeric_limits<In>::lowest())));
+ MOZ_RELEASE_ASSERT((IsInBounds<In, Out>(
+ static_cast<In>(std::numeric_limits<Out>::lowest()))));
+ MOZ_RELEASE_ASSERT((!IsInBounds<In, Out>(
+ static_cast<In>(std::numeric_limits<Out>::lowest()) - aEpsilon)));
+ } else {
+ // Check for negative floats and unsigned integer types.
+ MOZ_RELEASE_ASSERT((!IsInBounds<In, Out>(static_cast<In>(-1))));
+ }
+}
+
+void TestFloatConversion() {
+ MOZ_RELEASE_ASSERT((!IsInBounds<uint64_t, float>(UINT64_MAX)));
+ MOZ_RELEASE_ASSERT((!IsInBounds<uint32_t, float>(UINT32_MAX)));
+ MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, float>(UINT16_MAX)));
+ MOZ_RELEASE_ASSERT((IsInBounds<uint8_t, float>(UINT8_MAX)));
+
+ MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, float>(INT64_MAX)));
+ MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, float>(INT64_MIN)));
+ MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, float>(INT32_MAX)));
+ MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, float>(INT32_MIN)));
+ MOZ_RELEASE_ASSERT((IsInBounds<int16_t, float>(INT16_MAX)));
+ MOZ_RELEASE_ASSERT((IsInBounds<int16_t, float>(INT16_MIN)));
+ MOZ_RELEASE_ASSERT((IsInBounds<int8_t, float>(INT8_MAX)));
+ MOZ_RELEASE_ASSERT((IsInBounds<int8_t, float>(INT8_MIN)));
+
+ MOZ_RELEASE_ASSERT((!IsInBounds<uint64_t, double>(UINT64_MAX)));
+ MOZ_RELEASE_ASSERT((IsInBounds<uint32_t, double>(UINT32_MAX)));
+ MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, double>(UINT16_MAX)));
+ MOZ_RELEASE_ASSERT((IsInBounds<uint8_t, double>(UINT8_MAX)));
+
+ MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, double>(INT64_MAX)));
+ MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, double>(INT64_MIN)));
+ MOZ_RELEASE_ASSERT((IsInBounds<int32_t, double>(INT32_MAX)));
+ MOZ_RELEASE_ASSERT((IsInBounds<int32_t, double>(INT32_MIN)));
+ MOZ_RELEASE_ASSERT((IsInBounds<int16_t, double>(INT16_MAX)));
+ MOZ_RELEASE_ASSERT((IsInBounds<int16_t, double>(INT16_MIN)));
+ MOZ_RELEASE_ASSERT((IsInBounds<int8_t, double>(INT8_MAX)));
+ MOZ_RELEASE_ASSERT((IsInBounds<int8_t, double>(INT8_MIN)));
+
+ // Floor check
+ MOZ_RELEASE_ASSERT((IsInBounds<float, uint64_t>(4.3)));
+ MOZ_RELEASE_ASSERT((AssertedCast<uint64_t>(4.3f) == 4u));
+ MOZ_RELEASE_ASSERT((IsInBounds<float, uint32_t>(4.3)));
+ MOZ_RELEASE_ASSERT((AssertedCast<uint32_t>(4.3f) == 4u));
+ MOZ_RELEASE_ASSERT((IsInBounds<float, uint16_t>(4.3)));
+ MOZ_RELEASE_ASSERT((AssertedCast<uint16_t>(4.3f) == 4u));
+ MOZ_RELEASE_ASSERT((IsInBounds<float, uint8_t>(4.3)));
+ MOZ_RELEASE_ASSERT((AssertedCast<uint8_t>(4.3f) == 4u));
+
+ MOZ_RELEASE_ASSERT((IsInBounds<float, int64_t>(4.3)));
+ MOZ_RELEASE_ASSERT((AssertedCast<int64_t>(4.3f) == 4u));
+ MOZ_RELEASE_ASSERT((IsInBounds<float, int32_t>(4.3)));
+ MOZ_RELEASE_ASSERT((AssertedCast<int32_t>(4.3f) == 4u));
+ MOZ_RELEASE_ASSERT((IsInBounds<float, int16_t>(4.3)));
+ MOZ_RELEASE_ASSERT((AssertedCast<int16_t>(4.3f) == 4u));
+ MOZ_RELEASE_ASSERT((IsInBounds<float, int8_t>(4.3)));
+ MOZ_RELEASE_ASSERT((AssertedCast<int8_t>(4.3f) == 4u));
+
+ MOZ_RELEASE_ASSERT((IsInBounds<float, int64_t>(-4.3)));
+ MOZ_RELEASE_ASSERT((AssertedCast<int64_t>(-4.3f) == -4));
+ MOZ_RELEASE_ASSERT((IsInBounds<float, int32_t>(-4.3)));
+ MOZ_RELEASE_ASSERT((AssertedCast<int32_t>(-4.3f) == -4));
+ MOZ_RELEASE_ASSERT((IsInBounds<float, int16_t>(-4.3)));
+ MOZ_RELEASE_ASSERT((AssertedCast<int16_t>(-4.3f) == -4));
+ MOZ_RELEASE_ASSERT((IsInBounds<float, int8_t>(-4.3)));
+ MOZ_RELEASE_ASSERT((AssertedCast<int8_t>(-4.3f) == -4));
+
+ // Bound check for float to unsigned integer conversion. The parameters are
+ // espilons and offsets allowing to check boundaries, that depend on the
+ // magnitude of the numbers.
+ checkBoundariesFloating<double, uint64_t>(2049.);
+ checkBoundariesFloating<double, uint32_t>(1.);
+ checkBoundariesFloating<double, uint16_t>(1.);
+ checkBoundariesFloating<double, uint8_t>(1.);
+ // Large number because of the lack of precision of floats at this magnitude
+ checkBoundariesFloating<float, uint64_t>(1.1e12f);
+ checkBoundariesFloating<float, uint32_t>(1.f, 128u);
+ checkBoundariesFloating<float, uint16_t>(1.f);
+ checkBoundariesFloating<float, uint8_t>(1.f);
+
+ checkBoundariesFloating<double, int64_t>(1025.);
+ checkBoundariesFloating<double, int32_t>(1.);
+ checkBoundariesFloating<double, int16_t>(1.);
+ checkBoundariesFloating<double, int8_t>(1.);
+ // Large number because of the lack of precision of floats at this magnitude
+ checkBoundariesFloating<float, int64_t>(1.1e12f);
+ checkBoundariesFloating<float, int32_t>(256.f, 64u);
+ checkBoundariesFloating<float, int16_t>(1.f);
+ checkBoundariesFloating<float, int8_t>(1.f);
+
+ // Integer to floating point, boundary cases
+ MOZ_RELEASE_ASSERT(!(IsInBounds<int64_t, float>(
+ int64_t(std::pow(2, floatMantissaBitsPlusOne)) + 1)));
+ MOZ_RELEASE_ASSERT((IsInBounds<int64_t, float>(
+ int64_t(std::pow(2, floatMantissaBitsPlusOne)))));
+ MOZ_RELEASE_ASSERT((IsInBounds<int64_t, float>(
+ int64_t(std::pow(2, floatMantissaBitsPlusOne)) - 1)));
+
+ MOZ_RELEASE_ASSERT(!(IsInBounds<int64_t, float>(
+ int64_t(-std::pow(2, floatMantissaBitsPlusOne)) - 1)));
+ MOZ_RELEASE_ASSERT((IsInBounds<int64_t, float>(
+ int64_t(-std::pow(2, floatMantissaBitsPlusOne)))));
+ MOZ_RELEASE_ASSERT((IsInBounds<int64_t, float>(
+ int64_t(-std::pow(2, floatMantissaBitsPlusOne)) + 1)));
+
+ MOZ_RELEASE_ASSERT(!(IsInBounds<int64_t, double>(
+ uint64_t(std::pow(2, doubleMantissaBitsPlusOne)) + 1)));
+ MOZ_RELEASE_ASSERT((IsInBounds<int64_t, double>(
+ uint64_t(std::pow(2, doubleMantissaBitsPlusOne)))));
+ MOZ_RELEASE_ASSERT((IsInBounds<int64_t, double>(
+ uint64_t(std::pow(2, doubleMantissaBitsPlusOne)) - 1)));
+
+ MOZ_RELEASE_ASSERT(!(IsInBounds<int64_t, double>(
+ int64_t(-std::pow(2, doubleMantissaBitsPlusOne)) - 1)));
+ MOZ_RELEASE_ASSERT((IsInBounds<int64_t, double>(
+ int64_t(-std::pow(2, doubleMantissaBitsPlusOne)))));
+ MOZ_RELEASE_ASSERT((IsInBounds<int64_t, double>(
+ int64_t(-std::pow(2, doubleMantissaBitsPlusOne)) + 1)));
+
+ MOZ_RELEASE_ASSERT(!(IsInBounds<uint64_t, double>(UINT64_MAX)));
+ MOZ_RELEASE_ASSERT(!(IsInBounds<int64_t, double>(INT64_MAX)));
+ MOZ_RELEASE_ASSERT(!(IsInBounds<int64_t, double>(INT64_MIN)));
+
+ MOZ_RELEASE_ASSERT(
+ !(IsInBounds<double, float>(std::numeric_limits<double>::max())));
+ MOZ_RELEASE_ASSERT(
+ !(IsInBounds<double, float>(-std::numeric_limits<double>::max())));
+}
+
+int main() {
+ TestBitwiseCast();
+
+ TestSameSize();
+ TestToBiggerSize();
+ TestToSmallerSize();
+ TestFloatConversion();
+
+ return 0;
+}
diff --git a/mfbt/tests/TestCeilingFloor.cpp b/mfbt/tests/TestCeilingFloor.cpp
new file mode 100644
index 0000000000..7bdd6ea27c
--- /dev/null
+++ b/mfbt/tests/TestCeilingFloor.cpp
@@ -0,0 +1,81 @@
+/* -*- 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 0000000000..309c882d3b
--- /dev/null
+++ b/mfbt/tests/TestCheckedInt.cpp
@@ -0,0 +1,615 @@
+/* -*- 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>
+#include <type_traits>
+
+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), \
+ std::is_signed_v<T>)
+
+#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(std::is_signed_v<typename detail::TwiceBiggerType<T>::Type>) ==
+ bool(std::is_signed_v<T>));
+ }
+};
+
+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 = std::is_signed_v<T>;
+ VERIFY(bool(isTSigned) == !bool(T(-1) > T(0)));
+
+ testTwiceBiggerType<T>::run();
+
+ using unsignedT = std::make_unsigned_t<T>;
+
+ VERIFY(sizeof(unsignedT) == sizeof(T));
+ VERIFY(std::is_signed_v<unsignedT> == false);
+
+ const CheckedInt<T> max(std::numeric_limits<T>::max());
+ const CheckedInt<T> min(std::numeric_limits<T>::min());
+
+ // 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 = std::is_signed_v<U>; \
+ 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(std::numeric_limits<T>::max()) PostVExpr + one.value())); \
+ } \
+ VERIFY_IS_VALID_IF( \
+ CheckedInt<T>(std::numeric_limits<U>::max()), \
+ (sizeof(T) > sizeof(U) || \
+ ((sizeof(T) == sizeof(U)) && (isUSigned || !isTSigned)))); \
+ VERIFY_IS_VALID_IF(CheckedInt<T>(std::numeric_limits<U>::min()), \
+ 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/TestCompactPair.cpp b/mfbt/tests/TestCompactPair.cpp
new file mode 100644
index 0000000000..66300c338a
--- /dev/null
+++ b/mfbt/tests/TestCompactPair.cpp
@@ -0,0 +1,160 @@
+/* -*- 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 <type_traits>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/CompactPair.h"
+
+using mozilla::CompactPair;
+using mozilla::MakeCompactPair;
+
+// Sizes aren't part of the guaranteed CompactPair interface, but we want to
+// verify our attempts at compactness through EBO are moderately functional,
+// *somewhere*.
+#define INSTANTIATE(T1, T2, name, size) \
+ CompactPair<T1, T2> name##_1(T1(0), T2(0)); \
+ static_assert(sizeof(name##_1.first()) > 0, \
+ "first method should work on CompactPair<" #T1 ", " #T2 ">"); \
+ \
+ static_assert(sizeof(name##_1.second()) > 0, \
+ "second method should work on CompactPair<" #T1 ", " #T2 ">"); \
+ \
+ static_assert(sizeof(name##_1) == (size), \
+ "CompactPair<" #T1 ", " #T2 "> has an unexpected size"); \
+ \
+ CompactPair<T2, T1> name##_2(T2(0), T1(0)); \
+ static_assert(sizeof(name##_2.first()) > 0, \
+ "first method should work on CompactPair<" #T2 ", " #T1 ">"); \
+ \
+ static_assert(sizeof(name##_2.second()) > 0, \
+ "second method should work on CompactPair<" #T2 ", " #T1 ">"); \
+ \
+ static_assert(sizeof(name##_2) == (size), \
+ "CompactPair<" #T2 ", " #T1 "> has an unexpected size");
+
+static constexpr std::size_t sizemax(std::size_t a, std::size_t b) {
+ return (a > b) ? a : b;
+}
+
+INSTANTIATE(int, int, prim1, 2 * sizeof(int));
+INSTANTIATE(int, long, prim2,
+ sizeof(long) + sizemax(sizeof(int), alignof(long)));
+
+struct EmptyClass {
+ explicit EmptyClass(int) {}
+};
+struct NonEmpty {
+ char mC;
+ explicit NonEmpty(int) : mC('\0') {}
+};
+
+INSTANTIATE(int, EmptyClass, both1, sizeof(int));
+INSTANTIATE(int, NonEmpty, both2, sizeof(int) + alignof(int));
+INSTANTIATE(EmptyClass, NonEmpty, both3, 1);
+
+struct A {
+ char dummy;
+ explicit A(int) : dummy('\0') {}
+};
+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 EmptyNonMovableNonDefaultConstructible {
+ explicit EmptyNonMovableNonDefaultConstructible(int) {}
+
+ EmptyNonMovableNonDefaultConstructible(
+ const EmptyNonMovableNonDefaultConstructible&) = delete;
+ EmptyNonMovableNonDefaultConstructible(
+ EmptyNonMovableNonDefaultConstructible&&) = delete;
+ EmptyNonMovableNonDefaultConstructible& operator=(
+ const EmptyNonMovableNonDefaultConstructible&) = delete;
+ EmptyNonMovableNonDefaultConstructible& operator=(
+ EmptyNonMovableNonDefaultConstructible&&) = delete;
+};
+
+static void TestInPlaceConstruction() {
+ constexpr int firstValue = 42;
+ constexpr int secondValue = 43;
+
+ {
+ const CompactPair<EmptyNonMovableNonDefaultConstructible, int> pair{
+ std::piecewise_construct, std::tuple(firstValue),
+ std::tuple(secondValue)};
+ MOZ_RELEASE_ASSERT(pair.second() == secondValue);
+ }
+
+ {
+ const CompactPair<int, EmptyNonMovableNonDefaultConstructible> pair{
+ std::piecewise_construct, std::tuple(firstValue),
+ std::tuple(secondValue)};
+ MOZ_RELEASE_ASSERT(pair.first() == firstValue);
+ }
+
+ {
+ const CompactPair<int, int> pair{std::piecewise_construct,
+ std::tuple(firstValue),
+ std::tuple(secondValue)};
+ MOZ_RELEASE_ASSERT(pair.first() == firstValue);
+ MOZ_RELEASE_ASSERT(pair.second() == secondValue);
+ }
+
+ {
+ const CompactPair<EmptyNonMovableNonDefaultConstructible,
+ EmptyNonMovableNonDefaultConstructible>
+ pair{std::piecewise_construct, std::tuple(firstValue),
+ std::tuple(secondValue)};
+
+ // nothing to assert here...
+ }
+}
+
+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. CompactPair 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 CompactPairHelper 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 MakeCompactPair generates CompactPair objects of the correct
+ // types.
+ static_assert(
+ std::is_same_v<decltype(MakeCompactPair(A(0), B(0))), CompactPair<A, B>>,
+ "MakeCompactPair should strip rvalue references");
+ static_assert(
+ std::is_same_v<decltype(MakeCompactPair(a, b)), CompactPair<A, B>>,
+ "MakeCompactPair should strip lvalue references");
+ static_assert(std::is_same_v<decltype(MakeCompactPair(constA, constB)),
+ CompactPair<A, B>>,
+ "MakeCompactPair should strip CV-qualifiers");
+
+ // Check that copy assignment and move assignment work.
+ a = constA;
+ a = A(0);
+
+ TestInPlaceConstruction();
+
+ return 0;
+}
diff --git a/mfbt/tests/TestCountPopulation.cpp b/mfbt/tests/TestCountPopulation.cpp
new file mode 100644
index 0000000000..23234bbe5a
--- /dev/null
+++ b/mfbt/tests/TestCountPopulation.cpp
@@ -0,0 +1,30 @@
+/* -*- 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 0000000000..4c8effc9cd
--- /dev/null
+++ b/mfbt/tests/TestCountZeroes.cpp
@@ -0,0 +1,92 @@
+/* -*- 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/TestDefineEnum.cpp b/mfbt/tests/TestDefineEnum.cpp
new file mode 100644
index 0000000000..b5fbe3a0fd
--- /dev/null
+++ b/mfbt/tests/TestDefineEnum.cpp
@@ -0,0 +1,78 @@
+/* -*- 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/DefineEnum.h"
+
+// Sanity test for MOZ_DEFINE_ENUM.
+
+MOZ_DEFINE_ENUM(TestEnum1, (EnumeratorA, EnumeratorB, EnumeratorC));
+
+static_assert(EnumeratorA == 0, "Unexpected enumerator value");
+static_assert(EnumeratorB == 1, "Unexpected enumerator value");
+static_assert(EnumeratorC == 2, "Unexpected enumerator value");
+static_assert(kHighestTestEnum1 == EnumeratorC, "Incorrect highest value");
+static_assert(kTestEnum1Count == 3, "Incorrect enumerator count");
+
+// Sanity test for MOZ_DEFINE_ENUM_CLASS.
+
+MOZ_DEFINE_ENUM_CLASS(TestEnum2, (A, B, C));
+
+static_assert(TestEnum2::A == TestEnum2(0), "Unexpected enumerator value");
+static_assert(TestEnum2::B == TestEnum2(1), "Unexpected enumerator value");
+static_assert(TestEnum2::C == TestEnum2(2), "Unexpected enumerator value");
+static_assert(kHighestTestEnum2 == TestEnum2::C, "Incorrect highest value");
+static_assert(kTestEnum2Count == 3, "Incorrect enumerator count");
+
+// TODO: Test that the _WITH_BASE variants generate enumerators with the
+// correct underlying types. To do this, we need an |UnderlyingType|
+// type trait, which needs compiler support (recent versions of
+// compilers in the GCC family provide an |__underlying_type| builtin
+// for this purpose.
+
+// Sanity test for MOZ_DEFINE_ENUM[_CLASS]_AT_CLASS_SCOPE.
+
+struct TestClass {
+ // clang-format off
+ MOZ_DEFINE_ENUM_AT_CLASS_SCOPE(
+ TestEnum3, (
+ EnumeratorA,
+ EnumeratorB,
+ EnumeratorC
+ ));
+
+ MOZ_DEFINE_ENUM_CLASS_AT_CLASS_SCOPE(
+ TestEnum4, (
+ A,
+ B,
+ C
+ ));
+ // clang-format on
+
+ static_assert(EnumeratorA == 0, "Unexpected enumerator value");
+ static_assert(EnumeratorB == 1, "Unexpected enumerator value");
+ static_assert(EnumeratorC == 2, "Unexpected enumerator value");
+ static_assert(sHighestTestEnum3 == EnumeratorC, "Incorrect highest value");
+ static_assert(sTestEnum3Count == 3, "Incorrect enumerator count");
+
+ static_assert(TestEnum4::A == TestEnum4(0), "Unexpected enumerator value");
+ static_assert(TestEnum4::B == TestEnum4(1), "Unexpected enumerator value");
+ static_assert(TestEnum4::C == TestEnum4(2), "Unexpected enumerator value");
+ static_assert(sHighestTestEnum4 == TestEnum4::C, "Incorrect highest value");
+ static_assert(sTestEnum4Count == 3, "Incorrect enumerator count");
+};
+
+// Test that MOZ_DEFINE_ENUM doesn't allow giving enumerators initializers.
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+MOZ_DEFINE_ENUM_CLASS(EnumWithInitializer1, (A = -1, B, C))
+MOZ_DEFINE_ENUM_CLASS(EnumWithInitializer2, (A = 1, B, C))
+MOZ_DEFINE_ENUM_CLASS(EnumWithInitializer3, (A, B = 6, C))
+#endif
+
+int main() {
+ // Nothing to do here, all tests are static_asserts.
+ return 0;
+}
diff --git a/mfbt/tests/TestDoublyLinkedList.cpp b/mfbt/tests/TestDoublyLinkedList.cpp
new file mode 100644
index 0000000000..3065b15ddb
--- /dev/null
+++ b/mfbt/tests/TestDoublyLinkedList.cpp
@@ -0,0 +1,306 @@
+/* -*- 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/DoublyLinkedList.h"
+
+using mozilla::DoublyLinkedList;
+using mozilla::DoublyLinkedListElement;
+
+struct SomeClass : public DoublyLinkedListElement<SomeClass> {
+ unsigned int mValue;
+ explicit SomeClass(int aValue) : mValue(aValue) {}
+ void incr() { ++mValue; }
+ bool operator==(const SomeClass& other) const {
+ return mValue == other.mValue;
+ }
+};
+
+template <typename ListType, size_t N>
+static void CheckListValues(ListType& list, unsigned int (&values)[N]) {
+ size_t count = 0;
+ for (auto& x : list) {
+ MOZ_RELEASE_ASSERT(x.mValue == values[count]);
+ ++count;
+ }
+ MOZ_RELEASE_ASSERT(count == N);
+}
+
+static void TestDoublyLinkedList() {
+ DoublyLinkedList<SomeClass> list;
+
+ SomeClass one(1), two(2), three(3);
+
+ MOZ_RELEASE_ASSERT(list.isEmpty());
+ MOZ_RELEASE_ASSERT(!list.begin());
+ MOZ_RELEASE_ASSERT(!list.end());
+
+ for (SomeClass& x : list) {
+ MOZ_RELEASE_ASSERT(x.mValue);
+ MOZ_RELEASE_ASSERT(false);
+ }
+
+ list.pushFront(&one);
+ {
+ unsigned int check[]{1};
+ CheckListValues(list, check);
+ }
+
+ MOZ_RELEASE_ASSERT(list.contains(one));
+ MOZ_RELEASE_ASSERT(!list.contains(two));
+ MOZ_RELEASE_ASSERT(!list.contains(three));
+
+ MOZ_RELEASE_ASSERT(!list.isEmpty());
+ MOZ_RELEASE_ASSERT(list.begin()->mValue == 1);
+ MOZ_RELEASE_ASSERT(!list.end());
+
+ list.pushFront(&two);
+ {
+ unsigned int check[]{2, 1};
+ CheckListValues(list, check);
+ }
+
+ MOZ_RELEASE_ASSERT(list.begin()->mValue == 2);
+ MOZ_RELEASE_ASSERT(!list.end());
+ MOZ_RELEASE_ASSERT(!list.contains(three));
+
+ list.pushBack(&three);
+ {
+ unsigned int check[]{2, 1, 3};
+ CheckListValues(list, check);
+ }
+
+ MOZ_RELEASE_ASSERT(list.begin()->mValue == 2);
+ MOZ_RELEASE_ASSERT(!list.end());
+
+ list.remove(&one);
+ {
+ unsigned int check[]{2, 3};
+ CheckListValues(list, check);
+ }
+
+ list.insertBefore(list.find(three), &one);
+ {
+ unsigned int check[]{2, 1, 3};
+ CheckListValues(list, check);
+ }
+
+ list.remove(&three);
+ {
+ unsigned int check[]{2, 1};
+ CheckListValues(list, check);
+ }
+
+ list.insertBefore(list.find(two), &three);
+ {
+ unsigned int check[]{3, 2, 1};
+ CheckListValues(list, check);
+ }
+
+ list.remove(&three);
+ {
+ unsigned int check[]{2, 1};
+ CheckListValues(list, check);
+ }
+
+ list.insertBefore(++list.find(two), &three);
+ {
+ unsigned int check[]{2, 3, 1};
+ CheckListValues(list, check);
+ }
+
+ list.remove(&one);
+ {
+ unsigned int check[]{2, 3};
+ CheckListValues(list, check);
+ }
+
+ list.remove(&two);
+ {
+ unsigned int check[]{3};
+ CheckListValues(list, check);
+ }
+
+ list.insertBefore(list.find(three), &two);
+ {
+ unsigned int check[]{2, 3};
+ CheckListValues(list, check);
+ }
+
+ list.remove(&three);
+ {
+ unsigned int check[]{2};
+ CheckListValues(list, check);
+ }
+
+ list.remove(&two);
+ MOZ_RELEASE_ASSERT(list.isEmpty());
+
+ list.pushBack(&three);
+ {
+ unsigned int check[]{3};
+ CheckListValues(list, check);
+ }
+
+ list.pushFront(&two);
+ {
+ unsigned int check[]{2, 3};
+ CheckListValues(list, check);
+ }
+
+ // This should modify the values of |two| and |three| as pointers to them are
+ // stored in the list, not copies.
+ for (SomeClass& x : list) {
+ x.incr();
+ }
+
+ MOZ_RELEASE_ASSERT(*list.begin() == two);
+ MOZ_RELEASE_ASSERT(*++list.begin() == three);
+
+ SomeClass four(4);
+ MOZ_RELEASE_ASSERT(++list.begin() == list.find(four));
+}
+
+struct InTwoLists {
+ explicit InTwoLists(unsigned int aValue) : mValue(aValue) {}
+ DoublyLinkedListElement<InTwoLists> mListOne;
+ DoublyLinkedListElement<InTwoLists> mListTwo;
+ unsigned int mValue;
+
+ struct GetListOneTrait {
+ static DoublyLinkedListElement<InTwoLists>& Get(InTwoLists* aThis) {
+ return aThis->mListOne;
+ }
+ };
+};
+
+namespace mozilla {
+
+template <>
+struct GetDoublyLinkedListElement<InTwoLists> {
+ static DoublyLinkedListElement<InTwoLists>& Get(InTwoLists* aThis) {
+ return aThis->mListTwo;
+ }
+};
+
+} // namespace mozilla
+
+static void TestCustomAccessor() {
+ DoublyLinkedList<InTwoLists, InTwoLists::GetListOneTrait> listOne;
+ DoublyLinkedList<InTwoLists> listTwo;
+
+ InTwoLists one(1);
+ InTwoLists two(2);
+
+ listOne.pushBack(&one);
+ listOne.pushBack(&two);
+ {
+ unsigned int check[]{1, 2};
+ CheckListValues(listOne, check);
+ }
+
+ listTwo.pushBack(&one);
+ listTwo.pushBack(&two);
+ {
+ unsigned int check[]{1, 2};
+ CheckListValues(listOne, check);
+ }
+ {
+ unsigned int check[]{1, 2};
+ CheckListValues(listTwo, check);
+ }
+
+ (void)listTwo.popBack();
+ {
+ unsigned int check[]{1, 2};
+ CheckListValues(listOne, check);
+ }
+ {
+ unsigned int check[]{1};
+ CheckListValues(listTwo, check);
+ }
+
+ (void)listOne.popBack();
+ {
+ unsigned int check[]{1};
+ CheckListValues(listOne, check);
+ }
+ {
+ unsigned int check[]{1};
+ CheckListValues(listTwo, check);
+ }
+}
+
+static void TestSafeDoubleLinkedList() {
+ mozilla::SafeDoublyLinkedList<SomeClass> list;
+ auto* elt1 = new SomeClass(0);
+ auto* elt2 = new SomeClass(0);
+ auto* elt3 = new SomeClass(0);
+ auto* elt4 = new SomeClass(0);
+ list.pushBack(elt1);
+ list.pushBack(elt2);
+ list.pushBack(elt3);
+ auto iter = list.begin();
+
+ // basic tests for iterator validity
+ MOZ_RELEASE_ASSERT(
+ &*iter == elt1,
+ "iterator returned by begin() must point to the first element!");
+ MOZ_RELEASE_ASSERT(
+ &*(iter.next()) == elt2,
+ "iterator returned by begin() must have the second element as 'next'!");
+ list.remove(elt2);
+ MOZ_RELEASE_ASSERT(
+ &*(iter.next()) == elt3,
+ "After removal of the 2nd element 'next' must point to the 3rd element!");
+ ++iter;
+ MOZ_RELEASE_ASSERT(
+ &*iter == elt3,
+ "After advancing one step the current element must be the 3rd one!");
+ MOZ_RELEASE_ASSERT(!iter.next(), "This is the last element of the list!");
+ list.pushBack(elt4);
+ MOZ_RELEASE_ASSERT(&*(iter.next()) == elt4,
+ "After adding an element at the end of the list the "
+ "iterator must be updated!");
+
+ // advance to last element, then remove last element
+ ++iter;
+ list.popBack();
+ MOZ_RELEASE_ASSERT(bool(iter) == false,
+ "After removing the last element, the iterator pointing "
+ "to the last element must be empty!");
+
+ // iterate the whole remaining list, increment values
+ for (auto& el : list) {
+ el.incr();
+ }
+ MOZ_RELEASE_ASSERT(elt1->mValue == 1);
+ MOZ_RELEASE_ASSERT(elt2->mValue == 0);
+ MOZ_RELEASE_ASSERT(elt3->mValue == 1);
+ MOZ_RELEASE_ASSERT(elt4->mValue == 0);
+
+ // Removing the first element of the list while iterating must empty the
+ // iterator
+ for (auto it = list.begin(); it != list.end(); ++it) {
+ MOZ_RELEASE_ASSERT(bool(it) == true, "The iterator must contain a value!");
+ list.popFront();
+ MOZ_RELEASE_ASSERT(
+ bool(it) == false,
+ "After removing the first element, the iterator must be empty!");
+ }
+
+ delete elt1;
+ delete elt2;
+ delete elt3;
+ delete elt4;
+}
+
+int main() {
+ TestDoublyLinkedList();
+ TestCustomAccessor();
+ TestSafeDoubleLinkedList();
+ return 0;
+}
diff --git a/mfbt/tests/TestEndian.cpp b/mfbt/tests/TestEndian.cpp
new file mode 100644
index 0000000000..7f275375f6
--- /dev/null
+++ b/mfbt/tests/TestEndian.cpp
@@ -0,0 +1,501 @@
+/* -*- 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] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08};
+ 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] = {0x0102, 0x0304, 0x0506, 0x0708,
+ 0x0102, 0x0304, 0x0506, 0x0708};
+ 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] = {0x01020304, 0x05060708, 0x01020304,
+ 0x05060708};
+ 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] = {0x0102030405060708,
+ 0x0102030405060708};
+ 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]) == 0x0201);
+ MOZ_RELEASE_ASSERT(BigEndian::readUint16(&unsigned_bytes[0]) == 0x0102);
+
+ MOZ_RELEASE_ASSERT(LittleEndian::readUint32(&unsigned_bytes[0]) ==
+ 0x04030201U);
+ MOZ_RELEASE_ASSERT(BigEndian::readUint32(&unsigned_bytes[0]) == 0x01020304U);
+
+ MOZ_RELEASE_ASSERT(LittleEndian::readUint64(&unsigned_bytes[0]) ==
+ 0x0807060504030201ULL);
+ MOZ_RELEASE_ASSERT(BigEndian::readUint64(&unsigned_bytes[0]) ==
+ 0x0102030405060708ULL);
+
+ if (sizeof(uintptr_t) == 8) {
+ // MSVC warning C4309 is "'static_cast': truncation of constant value" and
+ // will hit for the literal casts below in 32-bit builds -- in dead code,
+ // because only the other arm of this |if| runs. Turn off the warning for
+ // these two uses in dead code.
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable : 4309)
+#endif
+ MOZ_RELEASE_ASSERT(LittleEndian::readUintptr(&unsigned_bytes[0]) ==
+ static_cast<uintptr_t>(0x0807060504030201ULL));
+ MOZ_RELEASE_ASSERT(BigEndian::readUintptr(&unsigned_bytes[0]) ==
+ static_cast<uintptr_t>(0x0102030405060708ULL));
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+ } else {
+ MOZ_RELEASE_ASSERT(LittleEndian::readUintptr(&unsigned_bytes[0]) ==
+ 0x04030201U);
+ MOZ_RELEASE_ASSERT(BigEndian::readUintptr(&unsigned_bytes[0]) ==
+ 0x01020304U);
+ }
+
+ LittleEndian::writeUint16(&buffer[0], 0x0201);
+ MOZ_RELEASE_ASSERT(memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint16_t)) ==
+ 0);
+ BigEndian::writeUint16(&buffer[0], 0x0102);
+ MOZ_RELEASE_ASSERT(memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint16_t)) ==
+ 0);
+
+ LittleEndian::writeUint32(&buffer[0], 0x04030201U);
+ MOZ_RELEASE_ASSERT(memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint32_t)) ==
+ 0);
+ BigEndian::writeUint32(&buffer[0], 0x01020304U);
+ MOZ_RELEASE_ASSERT(memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint32_t)) ==
+ 0);
+
+ LittleEndian::writeUint64(&buffer[0], 0x0807060504030201ULL);
+ MOZ_RELEASE_ASSERT(memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint64_t)) ==
+ 0);
+ BigEndian::writeUint64(&buffer[0], 0x0102030405060708ULL);
+ MOZ_RELEASE_ASSERT(memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint64_t)) ==
+ 0);
+
+ memset(&buffer[0], 0xff, sizeof(buffer));
+ LittleEndian::writeUintptr(&buffer[0], uintptr_t(0x0807060504030201ULL));
+ MOZ_RELEASE_ASSERT(
+ memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uintptr_t)) == 0);
+ if (sizeof(uintptr_t) == 4) {
+ MOZ_RELEASE_ASSERT(LittleEndian::readUint32(&buffer[4]) == 0xffffffffU);
+ }
+
+ memset(&buffer[0], 0xff, sizeof(buffer));
+ if (sizeof(uintptr_t) == 8) {
+ BigEndian::writeUintptr(&buffer[0], uintptr_t(0x0102030405060708ULL));
+ } else {
+ BigEndian::writeUintptr(&buffer[0], uintptr_t(0x01020304U));
+ MOZ_RELEASE_ASSERT(LittleEndian::readUint32(&buffer[4]) == 0xffffffffU);
+ }
+ MOZ_RELEASE_ASSERT(
+ memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uintptr_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));
+
+ if (sizeof(uintptr_t) == 8) {
+ MOZ_RELEASE_ASSERT(LittleEndian::readIntptr(&signed_bytes[0]) ==
+ intptr_t(0xf8f7f6f5f4f3f2f1LL));
+ MOZ_RELEASE_ASSERT(BigEndian::readIntptr(&signed_bytes[0]) ==
+ intptr_t(0xf1f2f3f4f5f6f7f8LL));
+ } else {
+ MOZ_RELEASE_ASSERT(LittleEndian::readIntptr(&signed_bytes[0]) ==
+ intptr_t(0xf4f3f2f1));
+ MOZ_RELEASE_ASSERT(BigEndian::readIntptr(&signed_bytes[0]) ==
+ intptr_t(0xf1f2f3f4));
+ }
+
+ 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);
+
+ memset(&buffer[0], 0xff, sizeof(buffer));
+ LittleEndian::writeIntptr(&buffer[0], intptr_t(0xf8f7f6f5f4f3f2f1LL));
+ MOZ_RELEASE_ASSERT(memcmp(&signed_bytes[0], &buffer[0], sizeof(intptr_t)) ==
+ 0);
+ if (sizeof(intptr_t) == 4) {
+ MOZ_RELEASE_ASSERT(LittleEndian::readUint32(&buffer[4]) == 0xffffffffU);
+ }
+
+ memset(&buffer[0], 0xff, sizeof(buffer));
+ if (sizeof(intptr_t) == 8) {
+ BigEndian::writeIntptr(&buffer[0], intptr_t(0xf1f2f3f4f5f6f7f8LL));
+ } else {
+ BigEndian::writeIntptr(&buffer[0], intptr_t(0xf1f2f3f4));
+ MOZ_RELEASE_ASSERT(LittleEndian::readUint32(&buffer[4]) == 0xffffffffU);
+ }
+ MOZ_RELEASE_ASSERT(memcmp(&signed_bytes[0], &buffer[0], sizeof(intptr_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 0000000000..c47710a715
--- /dev/null
+++ b/mfbt/tests/TestEnumSet.cpp
@@ -0,0 +1,306 @@
+/* -*- 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/BitSet.h"
+#include "mozilla/EnumSet.h"
+#include "mozilla/Vector.h"
+
+#include <type_traits>
+
+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,
+
+ SEA_BIRD_COUNT
+};
+
+enum class SmallEnum : uint8_t {
+ Foo,
+ Bar,
+};
+
+enum class BigEnum : uint64_t {
+ Foo,
+ Bar = 35,
+};
+
+template <typename Storage = typename std::make_unsigned<
+ typename std::underlying_type<SeaBird>::type>::type>
+class EnumSetSuite {
+ public:
+ using EnumSetSeaBird = EnumSet<SeaBird, Storage>;
+
+ 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();
+ testBigEnum();
+ }
+
+ private:
+ void testEnumSetLayout() {
+#ifndef DEBUG
+ static_assert(sizeof(EnumSet<SmallEnum>) == sizeof(SmallEnum),
+ "EnumSet should be no bigger than the enum by default");
+ static_assert(sizeof(EnumSet<SmallEnum, uint32_t>) == sizeof(uint32_t),
+ "EnumSet should be able to have its size overriden.");
+ static_assert(std::is_trivially_copyable_v<EnumSet<SmallEnum>>,
+ "EnumSet should be lightweight outside of debug.");
+#endif
+ }
+
+ 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() {
+ EnumSetSeaBird 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() {
+ EnumSetSeaBird 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() {
+ EnumSetSeaBird 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() {
+ EnumSetSeaBird procellariidae;
+ procellariidae += mPetrelProcellariidae;
+ procellariidae += mNonPetrelProcellariidae;
+ MOZ_RELEASE_ASSERT(procellariidae.size() == 5);
+
+ // Both procellariidae and mPetrels include GADFLY_PERTEL and TRUE_PETREL
+ EnumSetSeaBird procellariiformes;
+ procellariiformes += mDiomedeidae;
+ procellariiformes += procellariidae;
+ procellariiformes += mPetrels;
+ MOZ_RELEASE_ASSERT(procellariiformes.size() == 8);
+ }
+
+ void testUnion() {
+ EnumSetSeaBird procellariidae =
+ mPetrelProcellariidae + mNonPetrelProcellariidae;
+ MOZ_RELEASE_ASSERT(procellariidae.size() == 5);
+
+ // Both procellariidae and mPetrels include GADFLY_PETREL and TRUE_PETREL
+ EnumSetSeaBird procellariiformes = mDiomedeidae + procellariidae + mPetrels;
+ MOZ_RELEASE_ASSERT(procellariiformes.size() == 8);
+ }
+
+ void testRemoveFrom() {
+ EnumSetSeaBird 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() {
+ EnumSetSeaBird 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() {
+ EnumSetSeaBird 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() {
+ EnumSetSeaBird 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() {
+ EnumSetSeaBird 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() {
+ EnumSetSeaBird 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() {
+ EnumSetSeaBird likes = mPetrels & mPetrelProcellariidae;
+ MOZ_RELEASE_ASSERT(likes == EnumSetSeaBird(GADFLY_PETREL, TRUE_PETREL));
+ }
+
+ void testDuplicates() {
+ EnumSetSeaBird 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() {
+ EnumSetSeaBird 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() {
+ EnumSetSeaBird empty{};
+ MOZ_RELEASE_ASSERT(empty.size() == 0);
+ MOZ_RELEASE_ASSERT(empty.isEmpty());
+
+ EnumSetSeaBird 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));
+ }
+
+ void testBigEnum() {
+ EnumSet<BigEnum> set;
+ set += BigEnum::Bar;
+ MOZ_RELEASE_ASSERT(set.serialize() ==
+ (uint64_t(1) << uint64_t(BigEnum::Bar)));
+ }
+
+ EnumSetSeaBird mAlcidae;
+ EnumSetSeaBird mDiomedeidae;
+ EnumSetSeaBird mPetrelProcellariidae;
+ EnumSetSeaBird mNonPetrelProcellariidae;
+ EnumSetSeaBird mPetrels;
+};
+
+int main() {
+ EnumSetSuite<uint32_t> suite1;
+ suite1.runTests();
+
+ EnumSetSuite<BitSet<SEA_BIRD_COUNT>> suite2;
+ suite2.runTests();
+ return 0;
+}
diff --git a/mfbt/tests/TestEnumTypeTraits.cpp b/mfbt/tests/TestEnumTypeTraits.cpp
new file mode 100644
index 0000000000..1065c92a7b
--- /dev/null
+++ b/mfbt/tests/TestEnumTypeTraits.cpp
@@ -0,0 +1,159 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/EnumTypeTraits.h"
+
+#include <cstdint>
+
+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");
+}
+
+void TestFitForTypes() {
+ // 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>();
+}
+
+// -
+
+template <typename T, typename U>
+static constexpr void AssertSameTypeAndValue(T a, U b) {
+ static_assert(std::is_same_v<T, U>);
+ MOZ_ASSERT(a == b);
+}
+
+void TestUnderlyingValue() {
+ enum class Pet : int16_t { Cat, Dog, Fish };
+ enum class Plant { Flower, Tree, Vine };
+
+ AssertSameTypeAndValue(UnderlyingValue(Pet::Cat), int16_t(0));
+ AssertSameTypeAndValue(UnderlyingValue(Pet::Dog), int16_t(1));
+ AssertSameTypeAndValue(UnderlyingValue(Pet::Fish), int16_t(2));
+
+ AssertSameTypeAndValue(UnderlyingValue(Plant::Flower), int(0));
+ AssertSameTypeAndValue(UnderlyingValue(Plant::Tree), int(1));
+ AssertSameTypeAndValue(UnderlyingValue(Plant::Vine), int(2));
+}
+
+// -
+
+int main() {
+ TestFitForTypes();
+ TestUnderlyingValue();
+ return 0;
+}
diff --git a/mfbt/tests/TestEnumeratedArray.cpp b/mfbt/tests/TestEnumeratedArray.cpp
new file mode 100644
index 0000000000..dfc1a37f17
--- /dev/null
+++ b/mfbt/tests/TestEnumeratedArray.cpp
@@ -0,0 +1,46 @@
+/* -*- 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"
+
+using mozilla::EnumeratedArray;
+
+enum class AnimalSpecies { Cow, Sheep, Pig, Count };
+
+using TestArray = EnumeratedArray<AnimalSpecies, AnimalSpecies::Count, int>;
+
+void TestInitialValueByConstructor() {
+ // Style 1
+ TestArray 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
+ TestArray 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
+ TestArray 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);
+}
+
+void TestAssignment() {
+ TestArray headCount{8, 9, 10};
+ TestArray headCount2;
+ headCount2 = headCount;
+ MOZ_RELEASE_ASSERT(headCount2[AnimalSpecies::Cow] == 8);
+ MOZ_RELEASE_ASSERT(headCount2[AnimalSpecies::Sheep] == 9);
+ MOZ_RELEASE_ASSERT(headCount2[AnimalSpecies::Pig] == 10);
+}
+
+int main() {
+ TestInitialValueByConstructor();
+ TestAssignment();
+ return 0;
+}
diff --git a/mfbt/tests/TestFastBernoulliTrial.cpp b/mfbt/tests/TestFastBernoulliTrial.cpp
new file mode 100644
index 0000000000..f85d33b2db
--- /dev/null
+++ b/mfbt/tests/TestFastBernoulliTrial.cpp
@@ -0,0 +1,177 @@
+/* -*- 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 0000000000..44918cd1c5
--- /dev/null
+++ b/mfbt/tests/TestFloatingPoint.cpp
@@ -0,0 +1,730 @@
+/* -*- 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/FloatingPoint.h"
+
+#include <math.h>
+
+using mozilla::ExponentComponent;
+using mozilla::FloatingPoint;
+using mozilla::FuzzyEqualsAdditive;
+using mozilla::FuzzyEqualsMultiplicative;
+using mozilla::IsFloat32Representable;
+using mozilla::IsNegative;
+using mozilla::IsNegativeZero;
+using mozilla::IsPositiveZero;
+using mozilla::NegativeInfinity;
+using mozilla::NumberEqualsInt32;
+using mozilla::NumberEqualsInt64;
+using mozilla::NumberIsInt32;
+using mozilla::NumberIsInt64;
+using mozilla::NumbersAreIdentical;
+using mozilla::PositiveInfinity;
+using mozilla::SpecificNaN;
+using mozilla::UnspecifiedNaN;
+using std::exp2;
+using std::exp2f;
+
+#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();
+}
+
+// Used to test Number{Is,Equals}{Int32,Int64} for -0.0, the only case where
+// NumberEquals* and NumberIs* aren't equivalent.
+template <typename T>
+static void TestEqualsIsForNegativeZero() {
+ T negZero = T(-0.0);
+
+ int32_t i32;
+ A(!NumberIsInt32(negZero, &i32));
+ A(NumberEqualsInt32(negZero, &i32));
+ A(i32 == 0);
+
+ int64_t i64;
+ A(!NumberIsInt64(negZero, &i64));
+ A(NumberEqualsInt64(negZero, &i64));
+ A(i64 == 0);
+}
+
+// Used to test Number{Is,Equals}{Int32,Int64} for int32 values.
+template <typename T>
+static void TestEqualsIsForInt32(T aVal) {
+ int32_t i32;
+ A(NumberIsInt32(aVal, &i32));
+ MOZ_ASSERT(i32 == aVal);
+ A(NumberEqualsInt32(aVal, &i32));
+ MOZ_ASSERT(i32 == aVal);
+
+ int64_t i64;
+ A(NumberIsInt64(aVal, &i64));
+ MOZ_ASSERT(i64 == aVal);
+ A(NumberEqualsInt64(aVal, &i64));
+ MOZ_ASSERT(i64 == aVal);
+};
+
+// Used to test Number{Is,Equals}{Int32,Int64} for values that fit in int64 but
+// not int32.
+template <typename T>
+static void TestEqualsIsForInt64(T aVal) {
+ int32_t i32;
+ A(!NumberIsInt32(aVal, &i32));
+ A(!NumberEqualsInt32(aVal, &i32));
+
+ int64_t i64;
+ A(NumberIsInt64(aVal, &i64));
+ MOZ_ASSERT(i64 == aVal);
+ A(NumberEqualsInt64(aVal, &i64));
+ MOZ_ASSERT(i64 == aVal);
+};
+
+// Used to test Number{Is,Equals}{Int32,Int64} for values that aren't equal to
+// any int32 or int64.
+template <typename T>
+static void TestEqualsIsForNonInteger(T aVal) {
+ int32_t i32;
+ A(!NumberIsInt32(aVal, &i32));
+ A(!NumberEqualsInt32(aVal, &i32));
+
+ int64_t i64;
+ A(!NumberIsInt64(aVal, &i64));
+ A(!NumberEqualsInt64(aVal, &i64));
+};
+
+static void TestDoublesPredicates() {
+ A(std::isnan(UnspecifiedNaN<double>()));
+ A(std::isnan(SpecificNaN<double>(1, 17)));
+ ;
+ A(std::isnan(SpecificNaN<double>(0, 0xfffffffffff0fULL)));
+ A(!std::isnan(PositiveInfinity<double>()));
+ A(!std::isnan(NegativeInfinity<double>()));
+
+ A(std::isinf(PositiveInfinity<double>()));
+ A(std::isinf(NegativeInfinity<double>()));
+ A(!std::isinf(UnspecifiedNaN<double>()));
+
+ A(!std::isfinite(PositiveInfinity<double>()));
+ A(!std::isfinite(NegativeInfinity<double>()));
+ A(!std::isfinite(UnspecifiedNaN<double>()));
+
+ 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));
+
+ // Edge case: negative zero.
+ TestEqualsIsForNegativeZero<double>();
+
+ // Int32 values.
+ auto testInt32 = TestEqualsIsForInt32<double>;
+ testInt32(0.0);
+ testInt32(1.0);
+ testInt32(INT32_MIN);
+ testInt32(INT32_MAX);
+
+ // Int64 values that don't fit in int32.
+ auto testInt64 = TestEqualsIsForInt64<double>;
+ testInt64(2147483648);
+ testInt64(2147483649);
+ testInt64(-2147483649);
+ testInt64(INT64_MIN);
+ // Note: INT64_MAX can't be represented exactly as double. Use a large double
+ // very close to it.
+ testInt64(9223372036854772000.0);
+
+ constexpr double MinSafeInteger = -9007199254740991.0;
+ constexpr double MaxSafeInteger = 9007199254740991.0;
+ testInt64(MinSafeInteger);
+ testInt64(MaxSafeInteger);
+
+ // Doubles that aren't equal to any int32 or int64.
+ auto testNonInteger = TestEqualsIsForNonInteger<double>;
+ testNonInteger(NegativeInfinity<double>());
+ testNonInteger(PositiveInfinity<double>());
+ testNonInteger(UnspecifiedNaN<double>());
+ testNonInteger(-double(1ULL << 52) + 0.5);
+ testNonInteger(double(1ULL << 52) - 0.5);
+ testNonInteger(double(INT32_MAX) + 0.1);
+ testNonInteger(double(INT32_MIN) - 0.1);
+ testNonInteger(0.5);
+ testNonInteger(-0.0001);
+ testNonInteger(-9223372036854778000.0);
+ testNonInteger(9223372036854776000.0);
+
+ // Sanity-check that the IEEE-754 double-precision-derived literals used in
+ // testing here work as we intend them to.
+ A(exp2(-1075.0) == 0.0);
+ A(exp2(-1074.0) != 0.0);
+ testNonInteger(exp2(-1074.0));
+ testNonInteger(2 * exp2(-1074.0));
+
+ A(1.0 - exp2(-54.0) == 1.0);
+ A(1.0 - exp2(-53.0) != 1.0);
+ testNonInteger(1.0 - exp2(-53.0));
+ testNonInteger(1.0 - exp2(-52.0));
+
+ A(1.0 + exp2(-53.0) == 1.0f);
+ A(1.0 + exp2(-52.0) != 1.0f);
+ testNonInteger(1.0 + exp2(-52.0));
+}
+
+static void TestFloatsPredicates() {
+ A(std::isnan(UnspecifiedNaN<float>()));
+ A(std::isnan(SpecificNaN<float>(1, 17)));
+ ;
+ A(std::isnan(SpecificNaN<float>(0, 0x7fff0fUL)));
+ A(!std::isnan(PositiveInfinity<float>()));
+ A(!std::isnan(NegativeInfinity<float>()));
+
+ A(std::isinf(PositiveInfinity<float>()));
+ A(std::isinf(NegativeInfinity<float>()));
+ A(!std::isinf(UnspecifiedNaN<float>()));
+
+ A(!std::isfinite(PositiveInfinity<float>()));
+ A(!std::isfinite(NegativeInfinity<float>()));
+ A(!std::isfinite(UnspecifiedNaN<float>()));
+
+ 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));
+
+ // Edge case: negative zero.
+ TestEqualsIsForNegativeZero<float>();
+
+ // Int32 values.
+ auto testInt32 = TestEqualsIsForInt32<float>;
+ testInt32(0.0f);
+ testInt32(1.0f);
+ testInt32(INT32_MIN);
+ testInt32(float(2147483648 - 128)); // max int32_t fitting in float
+ const int32_t BIG = 2097151;
+ testInt32(BIG);
+
+ // Int64 values that don't fit in int32.
+ auto testInt64 = TestEqualsIsForInt64<float>;
+ testInt64(INT64_MIN);
+ testInt64(9007199254740992.0f);
+ testInt64(-float(2147483648) - 256);
+ testInt64(float(2147483648));
+ testInt64(float(2147483648) + 256);
+
+ // Floats that aren't equal to any int32 or int64.
+ auto testNonInteger = TestEqualsIsForNonInteger<float>;
+ testNonInteger(NegativeInfinity<float>());
+ testNonInteger(PositiveInfinity<float>());
+ testNonInteger(UnspecifiedNaN<float>());
+ testNonInteger(0.5f);
+ testNonInteger(1.5f);
+ testNonInteger(-0.0001f);
+ testNonInteger(-19223373116872850000.0f);
+ testNonInteger(19223373116872850000.0f);
+ testNonInteger(float(BIG) + 0.1f);
+
+ A(powf(2.0f, -150.0f) == 0.0f);
+ A(powf(2.0f, -149.0f) != 0.0f);
+ testNonInteger(powf(2.0f, -149.0f));
+ testNonInteger(2 * powf(2.0f, -149.0f));
+
+ A(1.0f - powf(2.0f, -25.0f) == 1.0f);
+ A(1.0f - powf(2.0f, -24.0f) != 1.0f);
+ testNonInteger(1.0f - powf(2.0f, -24.0f));
+ testNonInteger(1.0f - powf(2.0f, -23.0f));
+
+ A(1.0f + powf(2.0f, -24.0f) == 1.0f);
+ A(1.0f + powf(2.0f, -23.0f) != 1.0f);
+ testNonInteger(1.0f + powf(2.0f, -23.0f));
+}
+
+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();
+}
+
+static void TestIsFloat32Representable() {
+ // Zeroes are representable.
+ A(IsFloat32Representable(+0.0));
+ A(IsFloat32Representable(-0.0));
+
+ // NaN and infinities are representable.
+ A(IsFloat32Representable(UnspecifiedNaN<double>()));
+ A(IsFloat32Representable(SpecificNaN<double>(0, 1)));
+ A(IsFloat32Representable(SpecificNaN<double>(0, 71389)));
+ A(IsFloat32Representable(SpecificNaN<double>(0, (uint64_t(1) << 52) - 2)));
+ A(IsFloat32Representable(SpecificNaN<double>(1, 1)));
+ A(IsFloat32Representable(SpecificNaN<double>(1, 71389)));
+ A(IsFloat32Representable(SpecificNaN<double>(1, (uint64_t(1) << 52) - 2)));
+ A(IsFloat32Representable(PositiveInfinity<double>()));
+ A(IsFloat32Representable(NegativeInfinity<double>()));
+
+ // Sanity-check that the IEEE-754 double-precision-derived literals used in
+ // testing here work as we intend them to.
+ A(exp2(-1075.0) == 0.0);
+ A(exp2(-1074.0) != 0.0);
+
+ for (double littleExp = -1074.0; littleExp < -149.0; littleExp++) {
+ // Powers of two representable as doubles but not as floats aren't
+ // representable.
+ A(!IsFloat32Representable(exp2(littleExp)));
+ }
+
+ // Sanity-check that the IEEE-754 single-precision-derived literals used in
+ // testing here work as we intend them to.
+ A(exp2f(-150.0f) == 0.0);
+ A(exp2f(-149.0f) != 0.0);
+
+ // Exact powers of two within the available range are representable.
+ for (double exponent = -149.0; exponent < 128.0; exponent++) {
+ A(IsFloat32Representable(exp2(exponent)));
+ }
+
+ // Powers of two above the available range aren't representable.
+ for (double bigExp = 128.0; bigExp < 1024.0; bigExp++) {
+ A(!IsFloat32Representable(exp2(bigExp)));
+ }
+
+ // Various denormal (i.e. super-small) doubles with MSB and LSB as far apart
+ // as possible are representable (but taken one bit further apart are not
+ // representable).
+ //
+ // Note that the final iteration tests non-denormal with exponent field
+ // containing (biased) 1, as |oneTooSmall| and |widestPossible| happen still
+ // to be correct for that exponent due to the extra bit of precision in the
+ // implicit-one bit.
+ double oneTooSmall = exp2(-150.0);
+ for (double denormExp = -149.0;
+ denormExp < 1 - double(FloatingPoint<double>::kExponentBias) + 1;
+ denormExp++) {
+ double baseDenorm = exp2(denormExp);
+ double tooWide = baseDenorm + oneTooSmall;
+ A(!IsFloat32Representable(tooWide));
+
+ double widestPossible = baseDenorm;
+ if (oneTooSmall * 2.0 != baseDenorm) {
+ widestPossible += oneTooSmall * 2.0;
+ }
+
+ A(IsFloat32Representable(widestPossible));
+ }
+
+ // Finally, check certain interesting/special values for basic sanity.
+ A(!IsFloat32Representable(2147483647.0));
+ A(!IsFloat32Representable(-2147483647.0));
+}
+
+#undef A
+
+int main() {
+ TestAreIdentical();
+ TestExponentComponent();
+ TestPredicates();
+ TestAreApproximatelyEqual();
+ TestIsFloat32Representable();
+ return 0;
+}
diff --git a/mfbt/tests/TestFunctionRef.cpp b/mfbt/tests/TestFunctionRef.cpp
new file mode 100644
index 0000000000..0ae1d4f193
--- /dev/null
+++ b/mfbt/tests/TestFunctionRef.cpp
@@ -0,0 +1,144 @@
+/* -*- 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/FunctionRef.h"
+#include "mozilla/UniquePtr.h"
+
+using mozilla::FunctionRef;
+
+#define CHECK(c) \
+ do { \
+ bool cond = !!(c); \
+ MOZ_RELEASE_ASSERT(cond, "Failed assertion: " #c); \
+ } while (false)
+
+int addConstRefs(const int& arg1, const int& arg2) { return arg1 + arg2; }
+
+void incrementPointer(int* arg) { (*arg)++; }
+
+int increment(int arg) { return arg + 1; }
+
+int incrementUnique(mozilla::UniquePtr<int> ptr) { return *ptr + 1; }
+
+static bool helloWorldCalled = false;
+
+void helloWorld() { helloWorldCalled = true; }
+
+struct S {
+ static int increment(int arg) { return arg + 1; }
+};
+
+struct Incrementor {
+ int operator()(int arg) { return arg + 1; }
+};
+
+template <typename Fn>
+struct Caller;
+
+template <typename Fn, typename... Params>
+std::invoke_result_t<Fn, Params...> CallFunctionRef(FunctionRef<Fn> aRef,
+ Params... aParams) {
+ return aRef(std::forward<Params>(aParams)...);
+}
+
+static void TestNonmemberFunction() {
+ CHECK(CallFunctionRef<int(int)>(increment, 42) == 43);
+}
+
+static void TestStaticMemberFunction() {
+ CHECK(CallFunctionRef<int(int)>(&S::increment, 42) == 43);
+}
+
+static void TestFunctionObject() {
+ auto incrementor = Incrementor();
+ CHECK(CallFunctionRef<int(int)>(incrementor, 42) == 43);
+}
+
+static void TestFunctionObjectTemporary() {
+ CHECK(CallFunctionRef<int(int)>(Incrementor(), 42) == 43);
+}
+
+static void TestLambda() {
+ // Test non-capturing lambda
+ auto lambda1 = [](int arg) { return arg + 1; };
+ CHECK(CallFunctionRef<int(int)>(lambda1, 42) == 43);
+
+ // Test capturing lambda
+ int one = 1;
+ auto lambda2 = [one](int arg) { return arg + one; };
+ CHECK(CallFunctionRef<int(int)>(lambda2, 42) == 43);
+
+ CHECK(CallFunctionRef<int(int)>([](int arg) { return arg + 1; }, 42) == 43);
+}
+
+static void TestOperatorBool() {
+ auto ToBool = [](FunctionRef<int(int)> aRef) {
+ return static_cast<bool>(aRef);
+ };
+ CHECK(!ToBool({}));
+ CHECK(ToBool(increment));
+ CHECK(!ToBool(nullptr));
+}
+
+static void TestReferenceParameters() {
+ int x = 1;
+ int y = 2;
+ CHECK(CallFunctionRef<int(const int&, const int&)>(addConstRefs, x, y) == 3);
+}
+
+static void TestVoidNoParameters() {
+ CHECK(!helloWorldCalled);
+ CallFunctionRef<void()>(helloWorld);
+ CHECK(helloWorldCalled);
+}
+
+static void TestPointerParameters() {
+ int x = 1;
+ CallFunctionRef<void(int*)>(incrementPointer, &x);
+ CHECK(x == 2);
+}
+
+static void TestImplicitFunctorTypeConversion() {
+ auto incrementor = Incrementor();
+ short x = 1;
+ CHECK(CallFunctionRef<long(short)>(incrementor, x) == 2);
+}
+
+static void TestImplicitLambdaTypeConversion() {
+ short x = 1;
+ CHECK(CallFunctionRef<long(short)>([](short arg) { return arg + 1; }, x) ==
+ 2);
+}
+
+static void TestImplicitFunctionPointerTypeConversion() {
+ short x = 1;
+ CHECK(CallFunctionRef<long(short)>(&increment, x) == 2);
+}
+
+static void TestMoveOnlyArguments() {
+ CHECK(CallFunctionRef<int(mozilla::UniquePtr<int>)>(
+ &incrementUnique, mozilla::MakeUnique<int>(5)) == 6);
+}
+
+int main() {
+ TestNonmemberFunction();
+ TestStaticMemberFunction();
+ TestFunctionObject();
+ TestFunctionObjectTemporary();
+ TestLambda();
+ TestOperatorBool();
+ TestReferenceParameters();
+ TestPointerParameters();
+ TestVoidNoParameters();
+ TestImplicitFunctorTypeConversion();
+ TestImplicitLambdaTypeConversion();
+ TestImplicitFunctionPointerTypeConversion();
+ TestMoveOnlyArguments();
+
+ printf("TestFunctionRef OK!\n");
+ return 0;
+}
diff --git a/mfbt/tests/TestFunctionTypeTraits.cpp b/mfbt/tests/TestFunctionTypeTraits.cpp
new file mode 100644
index 0000000000..eb9593fbbf
--- /dev/null
+++ b/mfbt/tests/TestFunctionTypeTraits.cpp
@@ -0,0 +1,232 @@
+/* -*- 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/FunctionTypeTraits.h"
+
+#include <functional>
+
+using mozilla::FunctionTypeTraits;
+
+void f0() {}
+
+int f1(char) { return 0; }
+
+#ifdef NS_HAVE_STDCALL
+void NS_STDCALL f0s() {}
+
+int NS_STDCALL f1s(char) { return 0; }
+#endif // NS_HAVE_STDCALL
+
+struct S {
+ void f0() {}
+ void f0c() const {}
+ int f1(char) { return 0; }
+ int f1c(char) const { return 0; }
+#ifdef NS_HAVE_STDCALL
+ void NS_STDCALL f0s() {}
+ void NS_STDCALL f0cs() const {}
+ int NS_STDCALL f1s(char) { return 0; }
+ int NS_STDCALL f1cs(char) const { return 0; }
+#endif // NS_HAVE_STDCALL
+};
+
+static_assert(
+ std::is_same<typename FunctionTypeTraits<decltype(f0)>::ReturnType,
+ void>::value,
+ "f0 returns void");
+static_assert(FunctionTypeTraits<decltype(f0)>::arity == 0,
+ "f0 takes no parameters");
+static_assert(
+ std::is_same<
+ typename FunctionTypeTraits<decltype(f0)>::template ParameterType<0>,
+ void>::value,
+ "f0 has no first parameter");
+
+static_assert(
+ std::is_same<typename FunctionTypeTraits<decltype(&S::f0)>::ReturnType,
+ void>::value,
+ "S::f0 returns void");
+static_assert(FunctionTypeTraits<decltype(&S::f0)>::arity == 0,
+ "S::f0 takes no parameters");
+static_assert(std::is_same<typename FunctionTypeTraits<
+ decltype(&S::f0)>::template ParameterType<0>,
+ void>::value,
+ "S::f0 has no first parameter");
+
+static_assert(
+ std::is_same<typename FunctionTypeTraits<decltype(&S::f0c)>::ReturnType,
+ void>::value,
+ "S::f0c returns void");
+static_assert(FunctionTypeTraits<decltype(&S::f0c)>::arity == 0,
+ "S::f0c takes no parameters");
+static_assert(std::is_same<typename FunctionTypeTraits<
+ decltype(&S::f0c)>::template ParameterType<0>,
+ void>::value,
+ "S::f0c has no first parameter");
+
+static_assert(
+ std::is_same<typename FunctionTypeTraits<decltype(f1)>::ReturnType,
+ int>::value,
+ "f1 returns int");
+static_assert(FunctionTypeTraits<decltype(f1)>::arity == 1,
+ "f1 takes one parameter");
+static_assert(
+ std::is_same<
+ typename FunctionTypeTraits<decltype(f1)>::template ParameterType<0>,
+ char>::value,
+ "f1 takes a char");
+
+static_assert(
+ std::is_same<typename FunctionTypeTraits<decltype(&S::f1)>::ReturnType,
+ int>::value,
+ "S::f1 returns int");
+static_assert(FunctionTypeTraits<decltype(&S::f1)>::arity == 1,
+ "S::f1 takes one parameter");
+static_assert(std::is_same<typename FunctionTypeTraits<
+ decltype(&S::f1)>::template ParameterType<0>,
+ char>::value,
+ "S::f1 takes a char");
+
+static_assert(
+ std::is_same<typename FunctionTypeTraits<decltype(&S::f1c)>::ReturnType,
+ int>::value,
+ "S::f1c returns int");
+static_assert(FunctionTypeTraits<decltype(&S::f1c)>::arity == 1,
+ "S::f1c takes one parameter");
+static_assert(std::is_same<typename FunctionTypeTraits<
+ decltype(&S::f1c)>::template ParameterType<0>,
+ char>::value,
+ "S::f1c takes a char");
+
+#ifdef NS_HAVE_STDCALL
+static_assert(
+ std::is_same<typename FunctionTypeTraits<decltype(f0s)>::ReturnType,
+ void>::value,
+ "f0s returns void");
+static_assert(FunctionTypeTraits<decltype(f0s)>::arity == 0,
+ "f0s takes no parameters");
+static_assert(
+ std::is_same<
+ typename FunctionTypeTraits<decltype(f0s)>::template ParameterType<0>,
+ void>::value,
+ "f0s has no first parameter");
+
+static_assert(
+ std::is_same<typename FunctionTypeTraits<decltype(&S::f0s)>::ReturnType,
+ void>::value,
+ "S::f0s returns void");
+static_assert(FunctionTypeTraits<decltype(&S::f0s)>::arity == 0,
+ "S::f0s takes no parameters");
+static_assert(std::is_same<typename FunctionTypeTraits<
+ decltype(&S::f0s)>::template ParameterType<0>,
+ void>::value,
+ "S::f0s has no first parameter");
+
+static_assert(
+ std::is_same<typename FunctionTypeTraits<decltype(&S::f0cs)>::ReturnType,
+ void>::value,
+ "S::f0cs returns void");
+static_assert(FunctionTypeTraits<decltype(&S::f0cs)>::arity == 0,
+ "S::f0cs takes no parameters");
+static_assert(std::is_same<typename FunctionTypeTraits<
+ decltype(&S::f0cs)>::template ParameterType<0>,
+ void>::value,
+ "S::f0cs has no first parameter");
+
+static_assert(
+ std::is_same<typename FunctionTypeTraits<decltype(f1s)>::ReturnType,
+ int>::value,
+ "f1s returns int");
+static_assert(FunctionTypeTraits<decltype(f1s)>::arity == 1,
+ "f1s takes one parameter");
+static_assert(
+ std::is_same<
+ typename FunctionTypeTraits<decltype(f1s)>::template ParameterType<0>,
+ char>::value,
+ "f1s takes a char");
+
+static_assert(
+ std::is_same<typename FunctionTypeTraits<decltype(&S::f1s)>::ReturnType,
+ int>::value,
+ "S::f1s returns int");
+static_assert(FunctionTypeTraits<decltype(&S::f1s)>::arity == 1,
+ "S::f1s takes one parameter");
+static_assert(std::is_same<typename FunctionTypeTraits<
+ decltype(&S::f1s)>::template ParameterType<0>,
+ char>::value,
+ "S::f1s takes a char");
+
+static_assert(
+ std::is_same<typename FunctionTypeTraits<decltype(&S::f1cs)>::ReturnType,
+ int>::value,
+ "S::f1cs returns int");
+static_assert(FunctionTypeTraits<decltype(&S::f1cs)>::arity == 1,
+ "S::f1cs takes one parameter");
+static_assert(std::is_same<typename FunctionTypeTraits<
+ decltype(&S::f1cs)>::template ParameterType<0>,
+ char>::value,
+ "S::f1cs takes a char");
+#endif // NS_HAVE_STDCALL
+
+template <typename F>
+void TestVoidVoid(F&&) {
+ static_assert(
+ std::is_same<typename FunctionTypeTraits<F>::ReturnType, void>::value,
+ "Should return void");
+ static_assert(FunctionTypeTraits<F>::arity == 0, "Should take no parameters");
+ static_assert(
+ std::is_same<typename FunctionTypeTraits<F>::template ParameterType<0>,
+ void>::value,
+ "Should have no first parameter");
+}
+
+template <typename F>
+void TestIntChar(F&&) {
+ static_assert(
+ std::is_same<typename FunctionTypeTraits<F>::ReturnType, int>::value,
+ "Should return int");
+ static_assert(FunctionTypeTraits<F>::arity == 1, "Should take one parameter");
+ static_assert(
+ std::is_same<typename FunctionTypeTraits<F>::template ParameterType<0>,
+ char>::value,
+ "Should take a char");
+}
+
+int main() {
+ TestVoidVoid(f0);
+ TestVoidVoid(&f0);
+ TestVoidVoid(&S::f0);
+ TestVoidVoid(&S::f0c);
+ TestVoidVoid([]() {});
+ std::function<void()> ff0 = f0;
+ TestVoidVoid(ff0);
+
+ TestIntChar(f1);
+ TestIntChar(&f1);
+ TestIntChar(&S::f1);
+ TestIntChar(&S::f1c);
+ TestIntChar([](char) { return 0; });
+ std::function<int(char)> ff1 = f1;
+ TestIntChar(ff1);
+
+#ifdef NS_HAVE_STDCALL
+ TestVoidVoid(f0s);
+ TestVoidVoid(&f0s);
+ TestVoidVoid(&S::f0s);
+ TestVoidVoid(&S::f0cs);
+ std::function<void()> ff0s = f0s;
+ TestVoidVoid(ff0s);
+
+ TestIntChar(f1s);
+ TestIntChar(&f1s);
+ TestIntChar(&S::f1s);
+ TestIntChar(&S::f1cs);
+ std::function<int(char)> ff1s = f1s;
+ TestIntChar(ff1s);
+#endif // NS_HAVE_STDCALL
+
+ return 0;
+}
diff --git a/mfbt/tests/TestHashTable.cpp b/mfbt/tests/TestHashTable.cpp
new file mode 100644
index 0000000000..c648184040
--- /dev/null
+++ b/mfbt/tests/TestHashTable.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/HashTable.h"
+#include "mozilla/PairHash.h"
+
+#include <utility>
+
+void TestMoveConstructor() {
+ using namespace mozilla;
+
+ HashMap<int, int> map;
+ MOZ_RELEASE_ASSERT(map.putNew(3, 32));
+ MOZ_RELEASE_ASSERT(map.putNew(4, 42));
+ MOZ_RELEASE_ASSERT(map.count() == 2);
+ MOZ_RELEASE_ASSERT(!map.empty());
+ MOZ_RELEASE_ASSERT(!map.lookup(2));
+ MOZ_RELEASE_ASSERT(map.lookup(3)->value() == 32);
+ MOZ_RELEASE_ASSERT(map.lookup(4)->value() == 42);
+
+ HashMap<int, int> moved = std::move(map);
+ MOZ_RELEASE_ASSERT(moved.count() == 2);
+ MOZ_RELEASE_ASSERT(!moved.empty());
+ MOZ_RELEASE_ASSERT(!moved.lookup(2));
+ MOZ_RELEASE_ASSERT(moved.lookup(3)->value() == 32);
+ MOZ_RELEASE_ASSERT(moved.lookup(4)->value() == 42);
+
+ MOZ_RELEASE_ASSERT(map.empty());
+ MOZ_RELEASE_ASSERT(!map.count());
+}
+
+enum SimpleEnum { SIMPLE_1, SIMPLE_2 };
+
+enum class ClassEnum : int {
+ CLASS_ENUM_1,
+ CLASS_ENUM_2,
+};
+
+void TestEnumHash() {
+ using namespace mozilla;
+
+ HashMap<SimpleEnum, int> map;
+ MOZ_RELEASE_ASSERT(map.put(SIMPLE_1, 1));
+ MOZ_RELEASE_ASSERT(map.put(SIMPLE_2, 2));
+
+ MOZ_RELEASE_ASSERT(map.lookup(SIMPLE_1)->value() == 1);
+ MOZ_RELEASE_ASSERT(map.lookup(SIMPLE_2)->value() == 2);
+
+ HashMap<ClassEnum, int> map2;
+ MOZ_RELEASE_ASSERT(map2.put(ClassEnum::CLASS_ENUM_1, 1));
+ MOZ_RELEASE_ASSERT(map2.put(ClassEnum::CLASS_ENUM_2, 2));
+
+ MOZ_RELEASE_ASSERT(map2.lookup(ClassEnum::CLASS_ENUM_1)->value() == 1);
+ MOZ_RELEASE_ASSERT(map2.lookup(ClassEnum::CLASS_ENUM_2)->value() == 2);
+}
+
+void TestHashPair() {
+ using namespace mozilla;
+
+ // Test with std::pair
+ {
+ HashMap<std::pair<int, bool>, int, PairHasher<int, bool>> map;
+ std::pair<int, bool> key1 = std::make_pair(1, true);
+ MOZ_RELEASE_ASSERT(map.putNew(key1, 1));
+ MOZ_RELEASE_ASSERT(map.has(key1));
+ std::pair<int, bool> key2 = std::make_pair(1, false);
+ MOZ_RELEASE_ASSERT(map.putNew(key2, 1));
+ std::pair<int, bool> key3 = std::make_pair(2, false);
+ MOZ_RELEASE_ASSERT(map.putNew(key3, 2));
+ MOZ_RELEASE_ASSERT(map.has(key3));
+
+ MOZ_RELEASE_ASSERT(map.lookup(key1)->value() == 1);
+ MOZ_RELEASE_ASSERT(map.lookup(key2)->value() == 1);
+ MOZ_RELEASE_ASSERT(map.lookup(key3)->value() == 2);
+ }
+ // Test wtih compact pair
+ {
+ HashMap<mozilla::CompactPair<int, bool>, int, CompactPairHasher<int, bool>>
+ map;
+ mozilla::CompactPair<int, bool> key1 = mozilla::MakeCompactPair(1, true);
+ MOZ_RELEASE_ASSERT(map.putNew(key1, 1));
+ MOZ_RELEASE_ASSERT(map.has(key1));
+ mozilla::CompactPair<int, bool> key2 = mozilla::MakeCompactPair(1, false);
+ MOZ_RELEASE_ASSERT(map.putNew(key2, 1));
+ mozilla::CompactPair<int, bool> key3 = mozilla::MakeCompactPair(2, false);
+ MOZ_RELEASE_ASSERT(map.putNew(key3, 2));
+ MOZ_RELEASE_ASSERT(map.has(key3));
+
+ MOZ_RELEASE_ASSERT(map.lookup(key1)->value() == 1);
+ MOZ_RELEASE_ASSERT(map.lookup(key2)->value() == 1);
+ MOZ_RELEASE_ASSERT(map.lookup(key3)->value() == 2);
+ }
+}
+
+int main() {
+ TestMoveConstructor();
+ TestEnumHash();
+ TestHashPair();
+ return 0;
+}
diff --git a/mfbt/tests/TestIntegerRange.cpp b/mfbt/tests/TestIntegerRange.cpp
new file mode 100644
index 0000000000..3aad90fcc1
--- /dev/null
+++ b/mfbt/tests/TestIntegerRange.cpp
@@ -0,0 +1,150 @@
+/* -*- 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::IntegerRange;
+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 : IntegerRange(aN)) {
+ static_assert(std::is_same_v<decltype(i), IntType>,
+ "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(IntegerRange(aN))) {
+ static_assert(std::is_same_v<decltype(i), IntType>,
+ "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 : IntegerRange(aBegin, aEnd)) {
+ static_assert(std::is_same_v<decltype(i), IntType2>,
+ "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(IntegerRange(aBegin, aEnd))) {
+ static_assert(std::is_same_v<decltype(i), IntType2>,
+ "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 0000000000..a90732396f
--- /dev/null
+++ b/mfbt/tests/TestJSONWriter.cpp
@@ -0,0 +1,657 @@
+/* -*- 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>
+#include <string.h>
+
+using mozilla::JSONWriteFunc;
+using mozilla::JSONWriter;
+using mozilla::MakeStringSpan;
+using mozilla::MakeUnique;
+using mozilla::Span;
+
+// This writes all the output into a big buffer.
+struct StringWriteFunc final : public JSONWriteFunc {
+ std::string mString;
+
+ void Write(const mozilla::Span<const char>& aStr) final {
+ mString.append(aStr.data(), aStr.size());
+ }
+};
+
+void Check(JSONWriter& aWriter, const char* aExpected) {
+ JSONWriteFunc& func = aWriter.WriteFunc();
+ const std::string& actual = static_cast<StringWriteFunc&>(func).mString;
+ if (strcmp(aExpected, actual.c_str()) != 0) {
+ fprintf(stderr,
+ "---- EXPECTED ----\n<<<%s>>>\n"
+ "---- ACTUAL ----\n<<<%s>>>\n",
+ aExpected, actual.c_str());
+ 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\
+ \"string5\": \"hello\",\n\
+ \"string6\": \"\\\" \\\\ \\u0007 \\b \\t \",\n\
+ \"span1\": \"buf1\",\n\
+ \"span2\": \"buf2\",\n\
+ \"span3\": \"buf3\",\n\
+ \"span4\": \"buf\\n4\",\n\
+ \"span5\": \"MakeStringSpan\",\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.StringProperty("string5", "hello\0cut"); // '\0' marks the end.
+ w.StringProperty("string6", "\" \\ \a \b \t \0 \n \v \f \r");
+
+ const char buf1[] = {'b', 'u', 'f', '1'};
+ w.StringProperty("span1", buf1);
+ const char buf2[] = {'b', 'u', 'f', '2', '\0'};
+ w.StringProperty("span2", buf2);
+ const char buf3[] = {'b', 'u', 'f', '3', '\0', '?'};
+ w.StringProperty("span3", buf3);
+ const char buf4[] = {'b', 'u', 'f', '\n', '4', '\0', '?'};
+ w.StringProperty("span4", buf4);
+ w.StringProperty("span5", MakeStringSpan("MakeStringSpan"));
+
+ 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, 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\
+ \"hello\",\n\
+ \"\\\" \\\\ \\u0007 \\b \\t \",\n\
+ \"buf1\",\n\
+ \"buf2\",\n\
+ \"buf3\",\n\
+ \"buf\\n4\",\n\
+ \"MakeStringSpan\",\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.StringElement("hello\0cut"); // '\0' marks the end.
+ w.StringElement("\" \\ \a \b \t \0 \n \v \f \r");
+
+ const char buf1[] = {'b', 'u', 'f', '1'};
+ w.StringElement(buf1);
+ const char buf2[] = {'b', 'u', 'f', '2', '\0'};
+ w.StringElement(buf2);
+ const char buf3[] = {'b', 'u', 'f', '3', '\0', '?'};
+ w.StringElement(buf3);
+ const char buf4[] = {'b', 'u', 'f', '\n', '4', '\0', '?'};
+ w.StringElement(buf4);
+ w.StringElement(MakeStringSpan("MakeStringSpan"));
+
+ 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, 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, expected);
+}
+
+void TestOneLineJson() {
+ const char* expected =
+ "\
+{\"i\":1,\"array\":[null,[{}],{\"o\":{}},\"s\"],\"d\":3.33}\
+";
+
+ StringWriteFunc func;
+ JSONWriter w(func, JSONWriter::SingleLineStyle);
+
+ w.Start(w.MultiLineStyle); // style overridden from above
+
+ 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(); // No newline in this case.
+
+ Check(w, 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, 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, expected);
+}
+
+void TestEscapedPropertyNames() {
+ const char* expected =
+ "\
+{\"i\\t\": 1, \"array\\t\": [null, [{}], {\"o\\t\": {}}, \"s\"], \"d\": 3.33}\n\
+";
+
+ JSONWriter w(MakeUnique<StringWriteFunc>());
+
+ w.Start(w.SingleLineStyle);
+
+ w.IntProperty("i\t\0cut", 1); // '\0' marks the end.
+
+ w.StartArrayProperty("array\t");
+ {
+ w.NullElement();
+
+ w.StartArrayElement(w.MultiLineStyle); // style overridden from above
+ {
+ w.StartObjectElement();
+ w.EndObject();
+ }
+ w.EndArray();
+
+ w.StartObjectElement();
+ {
+ w.StartObjectProperty("o\t");
+ w.EndObject();
+ }
+ w.EndObject();
+
+ w.StringElement("s");
+ }
+ w.EndArray();
+
+ w.DoubleProperty("d\0\t", 3.33);
+
+ w.End();
+
+ Check(w, expected);
+}
+
+int main(void) {
+ TestBasicProperties();
+ TestBasicElements();
+ TestOneLineObject();
+ TestOneLineJson();
+ TestStringEscaping();
+ TestDeepNesting();
+ TestEscapedPropertyNames();
+
+ return 0;
+}
diff --git a/mfbt/tests/TestLinkedList.cpp b/mfbt/tests/TestLinkedList.cpp
new file mode 100644
index 0000000000..bb1ffe08c0
--- /dev/null
+++ b/mfbt/tests/TestLinkedList.cpp
@@ -0,0 +1,399 @@
+
+/* -*- 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::AutoCleanLinkedList;
+using mozilla::LinkedList;
+using mozilla::LinkedListElement;
+
+struct SomeClass : public LinkedListElement<SomeClass> {
+ unsigned int mValue;
+ explicit SomeClass(int aValue = 0) : mValue(aValue) {}
+ SomeClass(SomeClass&&) = default;
+ SomeClass& operator=(SomeClass&&) = default;
+ 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.length() == 0);
+ 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.contains(&one));
+ MOZ_RELEASE_ASSERT(!list.contains(&two));
+ MOZ_RELEASE_ASSERT(!list.contains(&three));
+
+ MOZ_RELEASE_ASSERT(!list.isEmpty());
+ MOZ_RELEASE_ASSERT(list.length() == 1);
+ 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.length() == 2);
+ 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.length() == 3);
+ 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.length() == 2);
+ 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);
+
+ const LinkedList<SomeClass>& constList = list;
+ for (const SomeClass* x : constList) {
+ MOZ_RELEASE_ASSERT(x);
+ }
+}
+
+static void TestExtendLists() {
+ AutoCleanLinkedList<SomeClass> list1, list2;
+
+ constexpr unsigned int N = 5;
+ for (unsigned int i = 0; i < N; ++i) {
+ list1.insertBack(new SomeClass(static_cast<int>(i)));
+
+ AutoCleanLinkedList<SomeClass> singleItemList;
+ singleItemList.insertFront(new SomeClass(static_cast<int>(i + N)));
+ list2.extendBack(std::move(singleItemList));
+ }
+ // list1 = { 0, 1, 2, 3, 4 }
+ // list2 = { 5, 6, 7, 8, 9 }
+
+ list1.extendBack(AutoCleanLinkedList<SomeClass>());
+ list1.extendBack(std::move(list2));
+
+ // Make sure the line above has properly emptied |list2|.
+ MOZ_RELEASE_ASSERT(list2.isEmpty()); // NOLINT(bugprone-use-after-move)
+
+ size_t i = 0;
+ for (SomeClass* x : list1) {
+ MOZ_RELEASE_ASSERT(x->mValue == i++);
+ }
+ MOZ_RELEASE_ASSERT(i == N * 2);
+}
+
+void TestSplice() {
+ AutoCleanLinkedList<SomeClass> list1, list2;
+ for (unsigned int i = 1; i <= 5; ++i) {
+ list1.insertBack(new SomeClass(static_cast<int>(i)));
+
+ AutoCleanLinkedList<SomeClass> singleItemList;
+ singleItemList.insertFront(new SomeClass(static_cast<int>(i * 10)));
+ list2.extendBack(std::move(singleItemList));
+ }
+ // list1 = { 1, 2, 3, 4, 5 }
+ // list2 = { 10, 20, 30, 40, 50 }
+
+ list1.splice(2, list2, 0, 5);
+
+ MOZ_RELEASE_ASSERT(list2.isEmpty());
+ unsigned int kExpected1[]{1, 2, 10, 20, 30, 40, 50, 3, 4, 5};
+ CheckListValues(list1, kExpected1);
+
+ // Since aSourceLen=100 exceeds list1's end, the function transfers
+ // three items [3, 4, 5].
+ list2.splice(0, list1, 7, 100);
+
+ unsigned int kExpected2[]{1, 2, 10, 20, 30, 40, 50};
+ unsigned int kExpected3[]{3, 4, 5};
+ CheckListValues(list1, kExpected2);
+ CheckListValues(list2, kExpected3);
+
+ // Since aDestinationPos=100 exceeds list2's end, the function transfers
+ // items to list2's end.
+ list2.splice(100, list1, 1, 1);
+
+ unsigned int kExpected4[]{1, 10, 20, 30, 40, 50};
+ unsigned int kExpected5[]{3, 4, 5, 2};
+ CheckListValues(list1, kExpected4);
+ CheckListValues(list2, kExpected5);
+}
+
+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 = std::move(c2);
+ MOZ_RELEASE_ASSERT(!c2.isInList());
+ MOZ_RELEASE_ASSERT(c3.isInList());
+
+ // Test move constructor for LinkedList.
+ LinkedList<SomeClass> list2(std::move(list1));
+ {
+ unsigned int check[]{1, 2};
+ CheckListValues(list2, check);
+ }
+ MOZ_RELEASE_ASSERT(list1.isEmpty());
+
+ // Test move assignment for LinkedList.
+ LinkedList<SomeClass> list3;
+ list3 = std::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 = std::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();
+ TestExtendLists();
+ TestSplice();
+ TestPrivate();
+ TestMove();
+ TestRemoveAndGet();
+ TestRefPtrList();
+ return 0;
+}
diff --git a/mfbt/tests/TestMacroArgs.cpp b/mfbt/tests/TestMacroArgs.cpp
new file mode 100644
index 0000000000..097ac9efa3
--- /dev/null
+++ b/mfbt/tests/TestMacroArgs.cpp
@@ -0,0 +1,38 @@
+/* -*- 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"
+
+static_assert(MOZ_ARG_COUNT() == 0, "");
+static_assert(MOZ_ARG_COUNT(a) == 1, "");
+static_assert(MOZ_ARG_COUNT(a, b) == 2, "");
+static_assert(MOZ_ARG_COUNT(a, b, c) == 3, "");
+
+static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(100) == 1000, "");
+static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(100, a) == 1001, "");
+static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(100, a, b) == 1002, "");
+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, 40, 50, 60, 70, 80, 90) == 10, "");
+static_assert(MOZ_ARG_2(10, 20, 30, 40, 50, 60, 70, 80, 90) == 20, "");
+static_assert(MOZ_ARG_3(10, 20, 30, 40, 50, 60, 70, 80, 90) == 30, "");
+static_assert(MOZ_ARG_4(10, 20, 30, 40, 50, 60, 70, 80, 90) == 40, "");
+static_assert(MOZ_ARG_5(10, 20, 30, 40, 50, 60, 70, 80, 90) == 50, "");
+static_assert(MOZ_ARG_6(10, 20, 30, 40, 50, 60, 70, 80, 90) == 60, "");
+static_assert(MOZ_ARG_7(10, 20, 30, 40, 50, 60, 70, 80, 90) == 70, "");
+static_assert(MOZ_ARG_8(10, 20, 30, 40, 50, 60, 70, 80, 90) == 80, "");
+
+int main() { return 0; }
diff --git a/mfbt/tests/TestMacroForEach.cpp b/mfbt/tests/TestMacroForEach.cpp
new file mode 100644
index 0000000000..11b75be810
--- /dev/null
+++ b/mfbt/tests/TestMacroForEach.cpp
@@ -0,0 +1,44 @@
+/* -*- 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(x) x
+#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, "");
+static_assert(MOZ_FOR_EACH_SEPARATED(HELPER_IDENTITY, (+), (), (10)) == 10, "");
+static_assert(MOZ_FOR_EACH_SEPARATED(HELPER_IDENTITY, (+), (), (1, 1, 1)) == 3,
+ "");
+
+#define HELPER_ONE_PLUS(x) HELPER_IDENTITY_PLUS(1)
+static_assert(MOZ_FOR_EACH(HELPER_ONE_PLUS, (), ()) 0 == 0, "");
+
+#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_DEFINE_PARAM(t, n) t n
+constexpr int test(MOZ_FOR_EACH_SEPARATED(HELPER_DEFINE_PARAM, (, ), (int, ),
+ (a, b, c))) {
+ return a + b + c;
+}
+static_assert(test(1, 2, 3) == 6, "");
+
+int main() {
+#define HELPER_IDENTITY_COMMA(k1, k2, x) k1, k2, x,
+ 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 0000000000..a21b286d0f
--- /dev/null
+++ b/mfbt/tests/TestMathAlgorithms.cpp
@@ -0,0 +1,545 @@
+/* -*- 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"
+
+#include <stdint.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");
+}
+
+void TestGCD() {
+ MOZ_ASSERT(mozilla::GCD(0, 0) == 0);
+
+ // clang-format off
+ // import random
+ // import math
+ //
+ // j = 0
+ // testcases = [
+ // { "name": "signed 64-bits integers", "upper": (2**63)-1, "suffix": "" },
+ // { "name": "unsigned 64-bits integers", "upper": (2**64)-1, "suffix": "u" },
+ // { "name": "signed 32-bits integers", "upper": (2**31)-1, "suffix": "" },
+ // { "name": "unsigned 32-bits integers", "upper": (2**32)-1, "suffix": "u" },
+ // ]
+ // for case in testcases:
+ // print("")
+ // print(f"// {case['name']}")
+ // while True:
+ // a = random.randrange(0, case["upper"])
+ // b = random.randrange(0, a)
+ // res = math.gcd(a, b)
+ // j+=1
+ // suffix = case["suffix"]
+ // print(f'MOZ_ASSERT(mozilla::GCD({a}{suffix}, {b}{suffix}) == {res}{suffix});')
+ // if j == 100:
+ // j = 0
+ // break
+ //
+ // clang-format on
+
+ // signed 64-bits integers
+ MOZ_ASSERT(mozilla::GCD(6855423437784447881, 5744152981668854128) == 1);
+ MOZ_ASSERT(mozilla::GCD(2560787397587345465, 208780102238346432) == 1);
+ MOZ_ASSERT(mozilla::GCD(5577889716064657494, 2159469434101077254) == 2);
+ MOZ_ASSERT(mozilla::GCD(5349904765384950054, 1944688623103480392) == 18);
+ MOZ_ASSERT(mozilla::GCD(6510887230309733540, 4404045615056449988) == 4);
+ MOZ_ASSERT(mozilla::GCD(5153663464686238190, 3495293373406661950) == 10);
+ MOZ_ASSERT(mozilla::GCD(8640438456651239176, 6172550763106125918) == 2);
+ MOZ_ASSERT(mozilla::GCD(4636330475123995525, 2504439215041170117) == 1);
+ MOZ_ASSERT(mozilla::GCD(3049680828923698889, 1798896016456058960) == 1);
+ MOZ_ASSERT(mozilla::GCD(6857469018143857254, 839235513850919013) == 3);
+ MOZ_ASSERT(mozilla::GCD(1667993323500460751, 403448480939209779) == 1);
+ MOZ_ASSERT(mozilla::GCD(2756773685517793960, 1001994517356200529) == 1);
+ MOZ_ASSERT(mozilla::GCD(5809484314452898314, 252378426271103138) == 2);
+ MOZ_ASSERT(mozilla::GCD(5756566734144094840, 3050839541929564330) == 10);
+ MOZ_ASSERT(mozilla::GCD(2669472117169059649, 1053394704248223342) == 1);
+ MOZ_ASSERT(mozilla::GCD(8486335744011214524, 4866724521619209633) == 3);
+ MOZ_ASSERT(mozilla::GCD(4841597191067437171, 1862876789330567260) == 1);
+ MOZ_ASSERT(mozilla::GCD(8940692064089049746, 6136664682975600685) == 1);
+ MOZ_ASSERT(mozilla::GCD(6274111242168941448, 688426762929457484) == 4);
+ MOZ_ASSERT(mozilla::GCD(7715132980994738435, 1456592620536615117) == 1);
+ MOZ_ASSERT(mozilla::GCD(5650339953233205545, 4406664870835551648) == 1);
+ MOZ_ASSERT(mozilla::GCD(7763657864638523008, 306878184260935929) == 1);
+ MOZ_ASSERT(mozilla::GCD(7776062097319502113, 7551650059636008893) == 1);
+ MOZ_ASSERT(mozilla::GCD(9158681410218029314, 5401644381866109508) == 2);
+ MOZ_ASSERT(mozilla::GCD(428865066965126615, 345306139889243757) == 1);
+ MOZ_ASSERT(mozilla::GCD(1334408785926182232, 736025095410140597) == 1);
+ MOZ_ASSERT(mozilla::GCD(9129011607893106326, 4818080883860535758) == 2);
+ MOZ_ASSERT(mozilla::GCD(5968300398911311896, 2550670869539540947) == 1);
+ MOZ_ASSERT(mozilla::GCD(5030190181362172874, 3861860193070954804) == 2);
+ MOZ_ASSERT(mozilla::GCD(5449912203994605772, 395450435226244945) == 1);
+ MOZ_ASSERT(mozilla::GCD(3510149608312823296, 1122015596295686144) == 512);
+ MOZ_ASSERT(mozilla::GCD(8822408923914428398, 3005499570530356734) == 2);
+ MOZ_ASSERT(mozilla::GCD(1894251920744324374, 29251650223056432) == 2);
+ MOZ_ASSERT(mozilla::GCD(1643262375132697825, 133049278064101269) == 1);
+ MOZ_ASSERT(mozilla::GCD(5979771268022611030, 5021008984454830630) == 10);
+ MOZ_ASSERT(mozilla::GCD(8551631013482492569, 3214028471848344275) == 1);
+ MOZ_ASSERT(mozilla::GCD(1374240599294724199, 1106817149419837791) == 1);
+ MOZ_ASSERT(mozilla::GCD(7877493197090616258, 3627451313613172281) == 3);
+ MOZ_ASSERT(mozilla::GCD(7323120572203017429, 5958183356236253053) == 1);
+ MOZ_ASSERT(mozilla::GCD(7356702947943126364, 1234023498733740170) == 2);
+ MOZ_ASSERT(mozilla::GCD(3533663535984312691, 1287666490057924782) == 1);
+ MOZ_ASSERT(mozilla::GCD(8249625410612436788, 1692674983510387167) == 1);
+ MOZ_ASSERT(mozilla::GCD(6590544882911640025, 6518468963976945930) == 5);
+ MOZ_ASSERT(mozilla::GCD(1161703442901270391, 72640111759506406) == 1);
+ MOZ_ASSERT(mozilla::GCD(3648054318401558456, 286110734809583843) == 1);
+ MOZ_ASSERT(mozilla::GCD(7445158880116265073, 4921289272987608741) == 3);
+ MOZ_ASSERT(mozilla::GCD(8052135113655284875, 6319225376882653323) == 1);
+ MOZ_ASSERT(mozilla::GCD(1272523803145322419, 669368693174176828) == 1);
+ MOZ_ASSERT(mozilla::GCD(762600464449954636, 258101161586809942) == 2);
+ MOZ_ASSERT(mozilla::GCD(8711570456095175409, 3217102356729157526) == 1);
+ MOZ_ASSERT(mozilla::GCD(8596472485422071677, 6590296624757765441) == 1);
+ MOZ_ASSERT(mozilla::GCD(8830210169177656300, 4853400012200083924) == 4);
+ MOZ_ASSERT(mozilla::GCD(2241405940749418043, 1414859858059940275) == 1);
+ MOZ_ASSERT(mozilla::GCD(6645372226653882826, 1089866326575332751) == 1);
+ MOZ_ASSERT(mozilla::GCD(4972052091595687646, 3420503469411720440) == 2);
+ MOZ_ASSERT(mozilla::GCD(8796611232338780872, 8344997795629414169) == 1);
+ MOZ_ASSERT(mozilla::GCD(4109837086789844244, 2749395249398063222) == 2);
+ MOZ_ASSERT(mozilla::GCD(7099065868279436275, 3485530390566515044) == 1);
+ MOZ_ASSERT(mozilla::GCD(1041731907675308955, 561481363772326233) == 9);
+ MOZ_ASSERT(mozilla::GCD(5882271298652803063, 5189002859026699540) == 1);
+ MOZ_ASSERT(mozilla::GCD(835073783923421192, 56853706366082462) == 2);
+ MOZ_ASSERT(mozilla::GCD(2514946180207195049, 1934146334993787393) == 1);
+ MOZ_ASSERT(mozilla::GCD(8975439209128912747, 1377234541321015082) == 1);
+ MOZ_ASSERT(mozilla::GCD(7039355952603350033, 6501349986472883135) == 1);
+ MOZ_ASSERT(mozilla::GCD(3747474677542899887, 2583298074596991574) == 1);
+ MOZ_ASSERT(mozilla::GCD(8176323250144977780, 4706420973964948943) == 1);
+ MOZ_ASSERT(mozilla::GCD(8748260715055109420, 7094433080013425893) == 1);
+ MOZ_ASSERT(mozilla::GCD(2192085035443314042, 1964458338792492837) == 3);
+ MOZ_ASSERT(mozilla::GCD(4387059045133366080, 1521989527531982075) == 5);
+ MOZ_ASSERT(mozilla::GCD(5735277355594712161, 1564786041102368131) == 1);
+ MOZ_ASSERT(mozilla::GCD(3898210686025675418, 1252531932064281967) == 7);
+ MOZ_ASSERT(mozilla::GCD(1886253648955280570, 235795900409586307) == 7);
+ MOZ_ASSERT(mozilla::GCD(862214669576776425, 90702464427080315) == 5);
+ MOZ_ASSERT(mozilla::GCD(2831206027654482398, 2543050780384667441) == 1);
+ MOZ_ASSERT(mozilla::GCD(3561377609788845927, 2837335262531584639) == 1);
+ MOZ_ASSERT(mozilla::GCD(1973347825404473626, 634138253455209313) == 1);
+ MOZ_ASSERT(mozilla::GCD(6447708134022060248, 4346890077474767787) == 19);
+ MOZ_ASSERT(mozilla::GCD(1690365172062143048, 678324119874104971) == 1);
+ MOZ_ASSERT(mozilla::GCD(2900650911116509049, 818833306053988358) == 1);
+ MOZ_ASSERT(mozilla::GCD(4126258648185074937, 2190040072639642009) == 1);
+ MOZ_ASSERT(mozilla::GCD(7310083765892765377, 3615506256861011852) == 1);
+ MOZ_ASSERT(mozilla::GCD(1482494462925181129, 568665115985247457) == 1);
+ MOZ_ASSERT(mozilla::GCD(2675477464881771327, 1476381757716745502) == 1);
+ MOZ_ASSERT(mozilla::GCD(6437060864565620566, 266707802567839796) == 2);
+ MOZ_ASSERT(mozilla::GCD(3800292251587454230, 245022706279648741) == 1);
+ MOZ_ASSERT(mozilla::GCD(3549515343757259493, 1328377263505490456) == 1);
+ MOZ_ASSERT(mozilla::GCD(8324574140787708570, 393444007055415700) == 10);
+ MOZ_ASSERT(mozilla::GCD(4373054321374923750, 1031193918836627100) == 150);
+ MOZ_ASSERT(mozilla::GCD(1370218692062991327, 682070501541164452) == 1);
+ MOZ_ASSERT(mozilla::GCD(4728813669404513421, 2346998232227619529) == 1);
+ MOZ_ASSERT(mozilla::GCD(320864023853706984, 50178854177191437) == 3);
+ MOZ_ASSERT(mozilla::GCD(5424710852893793602, 4237974770221703674) == 2);
+ MOZ_ASSERT(mozilla::GCD(5167582806125634015, 3538730725111557853) == 79);
+ MOZ_ASSERT(mozilla::GCD(7197930858946883500, 6668556859540800605) == 5);
+ MOZ_ASSERT(mozilla::GCD(2900089593575477549, 2554913303396097824) == 1);
+ MOZ_ASSERT(mozilla::GCD(1397576820519717048, 847997331257829237) == 3);
+ MOZ_ASSERT(mozilla::GCD(3939714364354053162, 1374067007308181723) == 1);
+ MOZ_ASSERT(mozilla::GCD(1065626084531260890, 664198963621954813) == 317);
+ MOZ_ASSERT(mozilla::GCD(5912876357514418196, 5112700044139286313) == 1);
+ MOZ_ASSERT(mozilla::GCD(2654316726913809362, 588030922713986903) == 1);
+
+ // unsigned 64-bits integers
+ MOZ_ASSERT(mozilla::GCD(16747832015348854198u, 10986175599217457242u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(14011882763672869646u, 1150181481133900726u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(6605029198216299492u, 2540177763690679863u) == 3u);
+ MOZ_ASSERT(mozilla::GCD(8723446333453359635u, 5501999887069319528u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2056609692029140361u, 1456692183174011231u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(3979920159703007405u, 2102351633956912159u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(9463892761763926474u, 5727651032816755587u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(15074653294321365395u, 7500084005319994862u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(7596876989397200146u, 2100623677138635163u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(15788975435035111366u, 13949507094186899135u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(11511089994271140687u, 11202842908571961185u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(5238481506779057035u, 1275096406977139452u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(10319988989820236521u, 6004256112028859859u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(15363016657999062582u, 13709656670722381934u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(1212882338768103987u, 400304873392680016u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(14516701884936382582u, 9474965125574306885u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(4684990176797036518u, 2826010316418750908u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(1257550743165743081u, 501524040422212694u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2684107647237574540u, 1059404913392538915u) == 5u);
+ MOZ_ASSERT(mozilla::GCD(9075798209725656040u, 8460431147770771484u) == 4u);
+ MOZ_ASSERT(mozilla::GCD(8849414266308239550u, 2100344973594953676u) == 6u);
+ MOZ_ASSERT(mozilla::GCD(18235452615524492166u, 6948238589518088517u) == 3u);
+ MOZ_ASSERT(mozilla::GCD(15050298436941428700u, 1467533438133155187u) == 3u);
+ MOZ_ASSERT(mozilla::GCD(8834598722016252963u, 4311275747815972852u) == 17u);
+ MOZ_ASSERT(mozilla::GCD(9356558625132137133u, 2037947968328350721u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(3849613153563955590u, 742698742609310596u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(14456988562990139501u, 10112205238651656021u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(12307508681986233124u, 9812326358082292497u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(1542509761845906606u, 753342053499303952u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(3002452874498902380u, 1551203246991573851u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(6995746439795805457u, 1188069610619158471u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2746395460341933223u, 2567350813567392270u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(13780256804547757349u, 3248441336598733689u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(11585262422698980788u, 9223319679416307971u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(12061506913736835258u, 4388981418731026638u) == 54u);
+ MOZ_ASSERT(mozilla::GCD(7926097431519628264u, 6609465824726553267u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(4869073093357623730u, 127092341961569309u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2415749375652736599u, 1225333195065764619u) == 3u);
+ MOZ_ASSERT(mozilla::GCD(12396258519293261927u, 7854932518032305093u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(8482841866529133449u, 8041279973223483861u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(6256232276718808317u, 218093546248209886u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(8708964372422992556u, 5925839455605803265u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(7079489553626522083u, 2723660727447617723u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(6456428365552053201u, 1199403261032183111u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(3346567208089938575u, 2383119761029013459u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(11371634586699820652u, 1314783250642191861u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(17865943339510318926u, 6852058968402585010u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(6184068614737379672u, 3615164034002231440u) == 8u);
+ MOZ_ASSERT(mozilla::GCD(4188759555626894588u, 756597961380253895u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(9834711092513827417u, 3337572906055372223u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(15971004526745900665u, 8185256010881285296u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(15018742812984668959u, 529070670894924960u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(1067863751656464299u, 905318428655384382u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(13862829046112265837u, 6101005940549725663u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(5042641015440071021u, 3851032995323622058u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(12302889786666538640u, 10776548976024201292u) == 76u);
+ MOZ_ASSERT(mozilla::GCD(13722399417473040071u, 9411461429949802122u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(7320504128957551347u, 54052915134765261u) == 3u);
+ MOZ_ASSERT(mozilla::GCD(15757615267691124901u, 6960991167654285257u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(5748033181727727936u, 303811493931685833u) == 3u);
+ MOZ_ASSERT(mozilla::GCD(13393585076101458038u, 11704741982068090192u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(1305962146520003941u, 900947650687182151u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(10210329619324275486u, 9165444096209531122u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(12287397750298100333u, 4589303685754232593u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(13074046732385479094u, 9410427502131685240u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(10769225306727183116u, 3766083633148275570u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(16097129444752648454u, 1689032025737433449u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(12569058547490329992u, 3311470626838389990u) == 18u);
+ MOZ_ASSERT(mozilla::GCD(6800922789750937338u, 1401809431753492506u) == 6u);
+ MOZ_ASSERT(mozilla::GCD(7640775166765881526u, 330467034911649653u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(7713745971481011689u, 2881741428874316968u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(3447718804232188171u, 2048968371582835027u) == 17u);
+ MOZ_ASSERT(mozilla::GCD(5048117340512952935u, 2723523492436699844u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(10307361968692211723u, 428905266774914488u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(1319115090575683914u, 1262779939989801116u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(12690110976610715926u, 1527151730024909348u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(12963032302522784237u, 8894543024067386192u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(4719664701853305298u, 328290838903591497u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(6046363361224867225u, 2463351775539510194u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(467063656725960574u, 62796777888499328u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(16390445286228133923u, 3793827091023779027u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(3118497337756941652u, 2860811741849353064u) == 4u);
+ MOZ_ASSERT(mozilla::GCD(17480668716240157222u, 6736393718990377613u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(3008091962262081749u, 2764474578829797968u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(10443605258088065132u, 1118236736154633837u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(8681282777233478597u, 2520450074320754822u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2428054799146631800u, 2304419668216461210u) == 10u);
+ MOZ_ASSERT(mozilla::GCD(11986346113373252908u, 5868466983065345812u) == 4u);
+ MOZ_ASSERT(mozilla::GCD(566070446598076689u, 226910043938150340u) == 3u);
+ MOZ_ASSERT(mozilla::GCD(20286446051392853u, 2253005103754547u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(9478145873341733534u, 1361277916695374175u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2194077616952029858u, 1880982148321238243u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(5067528875217388843u, 1007391120419508106u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(14964775244731205772u, 8476706085421248933u) == 3u);
+ MOZ_ASSERT(mozilla::GCD(15864657026011160414u, 11542748143033682677u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(6819186727513097073u, 3374817819083626717u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(14864653919493481829u, 1475678482546800916u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(674964986925038761u, 500070581922501698u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(5286379749864372936u, 1077542296477907313u) == 3u);
+ MOZ_ASSERT(mozilla::GCD(506827427986892036u, 177356571976309469u) == 1u);
+
+ // signed 32-bits integers
+ MOZ_ASSERT(mozilla::GCD(2082847559, 1075502059) == 1);
+ MOZ_ASSERT(mozilla::GCD(1516817880, 1427978452) == 4);
+ MOZ_ASSERT(mozilla::GCD(1912103032, 865754441) == 1);
+ MOZ_ASSERT(mozilla::GCD(1907998028, 1578360455) == 1);
+ MOZ_ASSERT(mozilla::GCD(2082786344, 1864664012) == 4);
+ MOZ_ASSERT(mozilla::GCD(2060961011, 1928455778) == 1);
+ MOZ_ASSERT(mozilla::GCD(970664659, 63074065) == 1);
+ MOZ_ASSERT(mozilla::GCD(55960901, 36955491) == 1);
+ MOZ_ASSERT(mozilla::GCD(1136602528, 339758054) == 2);
+ MOZ_ASSERT(mozilla::GCD(2040420582, 1355439044) == 2);
+ MOZ_ASSERT(mozilla::GCD(1295522905, 736231412) == 1);
+ MOZ_ASSERT(mozilla::GCD(778941225, 674482877) == 1);
+ MOZ_ASSERT(mozilla::GCD(291862772, 262751987) == 1);
+ MOZ_ASSERT(mozilla::GCD(233275018, 60278627) == 1);
+ MOZ_ASSERT(mozilla::GCD(701740307, 432255046) == 1);
+ MOZ_ASSERT(mozilla::GCD(582766531, 457298210) == 1);
+ MOZ_ASSERT(mozilla::GCD(196369046, 15577226) == 2);
+ MOZ_ASSERT(mozilla::GCD(1342156837, 2790339) == 1);
+ MOZ_ASSERT(mozilla::GCD(502348102, 151073265) == 1);
+ MOZ_ASSERT(mozilla::GCD(836867611, 797891653) == 1);
+ MOZ_ASSERT(mozilla::GCD(859055751, 525520896) == 3);
+ MOZ_ASSERT(mozilla::GCD(701234220, 683730404) == 4);
+ MOZ_ASSERT(mozilla::GCD(2102253469, 1046820362) == 1);
+ MOZ_ASSERT(mozilla::GCD(1712691453, 34616585) == 1);
+ MOZ_ASSERT(mozilla::GCD(1074235876, 683609889) == 1);
+ MOZ_ASSERT(mozilla::GCD(535965177, 182306069) == 11);
+ MOZ_ASSERT(mozilla::GCD(1437763442, 180698008) == 2);
+ MOZ_ASSERT(mozilla::GCD(2005641602, 175306737) == 1);
+ MOZ_ASSERT(mozilla::GCD(803294953, 565920364) == 1);
+ MOZ_ASSERT(mozilla::GCD(2135931435, 220153322) == 1);
+ MOZ_ASSERT(mozilla::GCD(1002010726, 619364124) == 2);
+ MOZ_ASSERT(mozilla::GCD(1841159587, 577256747) == 1);
+ MOZ_ASSERT(mozilla::GCD(2117547620, 896973794) == 2);
+ MOZ_ASSERT(mozilla::GCD(2004836234, 157238204) == 2);
+ MOZ_ASSERT(mozilla::GCD(952368407, 625062194) == 1);
+ MOZ_ASSERT(mozilla::GCD(671144794, 357719289) == 1);
+ MOZ_ASSERT(mozilla::GCD(1369585680, 279330845) == 5);
+ MOZ_ASSERT(mozilla::GCD(389855496, 230820785) == 1);
+ MOZ_ASSERT(mozilla::GCD(2101505071, 572728762) == 1);
+ MOZ_ASSERT(mozilla::GCD(1657802296, 667524476) == 4);
+ MOZ_ASSERT(mozilla::GCD(1007298072, 598682608) == 8);
+ MOZ_ASSERT(mozilla::GCD(1499193816, 44129206) == 2);
+ MOZ_ASSERT(mozilla::GCD(1355799723, 1163556923) == 1);
+ MOZ_ASSERT(mozilla::GCD(346410469, 294136125) == 1);
+ MOZ_ASSERT(mozilla::GCD(240297386, 239749630) == 2);
+ MOZ_ASSERT(mozilla::GCD(1595986655, 706220030) == 5);
+ MOZ_ASSERT(mozilla::GCD(265850446, 117414954) == 2);
+ MOZ_ASSERT(mozilla::GCD(1594478812, 559606261) == 1);
+ MOZ_ASSERT(mozilla::GCD(1098933117, 145267674) == 3);
+ MOZ_ASSERT(mozilla::GCD(37749195, 34174284) == 3);
+ MOZ_ASSERT(mozilla::GCD(173141528, 158277345) == 1);
+ MOZ_ASSERT(mozilla::GCD(1523316779, 1507242666) == 1);
+ MOZ_ASSERT(mozilla::GCD(1574321272, 213222586) == 2);
+ MOZ_ASSERT(mozilla::GCD(186241582, 58675779) == 1);
+ MOZ_ASSERT(mozilla::GCD(1351024876, 1256961567) == 1);
+ MOZ_ASSERT(mozilla::GCD(2060871503, 1626844669) == 1);
+ MOZ_ASSERT(mozilla::GCD(794617235, 606782933) == 1);
+ MOZ_ASSERT(mozilla::GCD(620853401, 550785717) == 1);
+ MOZ_ASSERT(mozilla::GCD(978990617, 684228903) == 1);
+ MOZ_ASSERT(mozilla::GCD(185414372, 160958435) == 11);
+ MOZ_ASSERT(mozilla::GCD(13886275, 10781501) == 1);
+ MOZ_ASSERT(mozilla::GCD(316445410, 72994145) == 5);
+ MOZ_ASSERT(mozilla::GCD(260685833, 66561321) == 1);
+ MOZ_ASSERT(mozilla::GCD(656788852, 619471100) == 4);
+ MOZ_ASSERT(mozilla::GCD(409924450, 323144710) == 10);
+ MOZ_ASSERT(mozilla::GCD(1696374689, 155122424) == 1);
+ MOZ_ASSERT(mozilla::GCD(1720449495, 1332196090) == 5);
+ MOZ_ASSERT(mozilla::GCD(102504868, 95625294) == 2);
+ MOZ_ASSERT(mozilla::GCD(959039064, 266180243) == 1);
+ MOZ_ASSERT(mozilla::GCD(771762738, 99126507) == 3);
+ MOZ_ASSERT(mozilla::GCD(1666721205, 164347293) == 3);
+ MOZ_ASSERT(mozilla::GCD(1145868726, 1013299840) == 2);
+ MOZ_ASSERT(mozilla::GCD(123667035, 6968726) == 1);
+ MOZ_ASSERT(mozilla::GCD(856285310, 669026117) == 1);
+ MOZ_ASSERT(mozilla::GCD(1748843942, 376021862) == 2);
+ MOZ_ASSERT(mozilla::GCD(1364381942, 1316920424) == 2);
+ MOZ_ASSERT(mozilla::GCD(376501104, 233350000) == 16);
+ MOZ_ASSERT(mozilla::GCD(1516376773, 554534905) == 1);
+ MOZ_ASSERT(mozilla::GCD(1355209533, 371401397) == 1);
+ MOZ_ASSERT(mozilla::GCD(488029245, 453641230) == 5);
+ MOZ_ASSERT(mozilla::GCD(2086782535, 1965901533) == 1);
+ MOZ_ASSERT(mozilla::GCD(1701843138, 197489892) == 6);
+ MOZ_ASSERT(mozilla::GCD(1857287302, 756127018) == 2);
+ MOZ_ASSERT(mozilla::GCD(1806613582, 963087217) == 1);
+ MOZ_ASSERT(mozilla::GCD(1350708388, 1013432485) == 1);
+ MOZ_ASSERT(mozilla::GCD(742201232, 486590366) == 2);
+ MOZ_ASSERT(mozilla::GCD(47378255, 18524009) == 1);
+ MOZ_ASSERT(mozilla::GCD(750926792, 282203477) == 1);
+ MOZ_ASSERT(mozilla::GCD(1242468272, 1225593358) == 2);
+ MOZ_ASSERT(mozilla::GCD(1937337947, 1233008310) == 1);
+ MOZ_ASSERT(mozilla::GCD(600511783, 563234297) == 1);
+ MOZ_ASSERT(mozilla::GCD(1583895113, 1400349394) == 1);
+ MOZ_ASSERT(mozilla::GCD(361950446, 20294144) == 26);
+ MOZ_ASSERT(mozilla::GCD(712527923, 351368901) == 1);
+ MOZ_ASSERT(mozilla::GCD(221252886, 13768150) == 2);
+ MOZ_ASSERT(mozilla::GCD(1217530242, 184772639) == 1);
+ MOZ_ASSERT(mozilla::GCD(1145522580, 92958612) == 12);
+ MOZ_ASSERT(mozilla::GCD(1765854048, 1073605551) == 3);
+ MOZ_ASSERT(mozilla::GCD(1179258112, 1148756377) == 1);
+ MOZ_ASSERT(mozilla::GCD(211982661, 145365362) == 1);
+
+ // unsigned 32-bits integers
+ MOZ_ASSERT(mozilla::GCD(2346624228u, 854636854u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(257647411u, 113262213u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(532130107u, 181815062u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(188329196u, 21767880u) == 4u);
+ MOZ_ASSERT(mozilla::GCD(965417460u, 433449910u) == 10u);
+ MOZ_ASSERT(mozilla::GCD(4285939108u, 782087256u) == 4u);
+ MOZ_ASSERT(mozilla::GCD(3176833937u, 905249796u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(1596497177u, 1259467765u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(296928708u, 137867254u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(810260571u, 278688539u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2319673546u, 6698908u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(335032855u, 304923748u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(1520046075u, 30861208u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(3370242674u, 2513781509u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2380615411u, 41999289u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2999947090u, 619047913u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(463491935u, 219826435u) == 5u);
+ MOZ_ASSERT(mozilla::GCD(256795166u, 3240595u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(3794760062u, 542176354u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(2347135107u, 532837578u) == 3u);
+ MOZ_ASSERT(mozilla::GCD(215263644u, 82185110u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(3242470340u, 1014909501u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(1935066897u, 1646318370u) == 3u);
+ MOZ_ASSERT(mozilla::GCD(2528019825u, 2199478105u) == 5u);
+ MOZ_ASSERT(mozilla::GCD(814340701u, 505422837u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2422005621u, 1270490106u) == 3u);
+ MOZ_ASSERT(mozilla::GCD(2196878780u, 2125974315u) == 5u);
+ MOZ_ASSERT(mozilla::GCD(3243580525u, 3222120645u) == 5u);
+ MOZ_ASSERT(mozilla::GCD(592838u, 333273u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(957856834u, 660922287u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2650657380u, 2507896759u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(35861051u, 25878355u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(1907977010u, 514369620u) == 10u);
+ MOZ_ASSERT(mozilla::GCD(1850153182u, 1133466079u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2404132308u, 942620249u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(4120768767u, 794728522u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(3115077311u, 437206010u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(3653354572u, 3501340268u) == 4u);
+ MOZ_ASSERT(mozilla::GCD(3700775106u, 1237309608u) == 6u);
+ MOZ_ASSERT(mozilla::GCD(3838425682u, 2767520531u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(812123689u, 691153768u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(3201500844u, 1530832674u) == 6u);
+ MOZ_ASSERT(mozilla::GCD(802121923u, 753535009u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(575392026u, 450096822u) == 6u);
+ MOZ_ASSERT(mozilla::GCD(1074039450u, 724299558u) == 6u);
+ MOZ_ASSERT(mozilla::GCD(3785968159u, 230568577u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(80611731u, 30537579u) == 3u);
+ MOZ_ASSERT(mozilla::GCD(3717744094u, 3192172824u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(3481208739u, 3389567399u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(1126134290u, 760589919u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2452072599u, 1235840929u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(4172574373u, 664346996u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(4280275945u, 1940565231u) == 11u);
+ MOZ_ASSERT(mozilla::GCD(1138803378u, 919205598u) == 6u);
+ MOZ_ASSERT(mozilla::GCD(3871971423u, 3071143517u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(1889403334u, 261936800u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(1233462464u, 462090021u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(267801361u, 177041892u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(1586528261u, 1146114428u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2209381020u, 1616518545u) == 15u);
+ MOZ_ASSERT(mozilla::GCD(2493819993u, 110364986u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(105420984u, 83814372u) == 12u);
+ MOZ_ASSERT(mozilla::GCD(3093899047u, 917349662u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(3716325890u, 1554865432u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(692565714u, 265467690u) == 18u);
+ MOZ_ASSERT(mozilla::GCD(659720171u, 250624014u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(1890623148u, 1632453222u) == 6u);
+ MOZ_ASSERT(mozilla::GCD(3557986303u, 752931252u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(237903157u, 177153319u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(4133928804u, 3898800943u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(1783300920u, 196251347u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2035190407u, 866039372u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(3893680107u, 3211053018u) == 3u);
+ MOZ_ASSERT(mozilla::GCD(4293646715u, 2698207329u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(1409442959u, 151043902u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(1823328305u, 375231671u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2574512647u, 1902834298u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2533783127u, 1232079823u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2622446878u, 193328426u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(4099571222u, 3439224331u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2355797345u, 430435034u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2654318392u, 2069135952u) == 8u);
+ MOZ_ASSERT(mozilla::GCD(1671976410u, 1100794671u) == 3u);
+ MOZ_ASSERT(mozilla::GCD(328877177u, 236038245u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2373247523u, 1198763899u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(3230550971u, 203517406u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2274958703u, 353643804u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(1048415366u, 740416576u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(2768590397u, 843179468u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2839858158u, 1019946790u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(4116867766u, 52672530u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(3433787325u, 2398189631u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(2636022376u, 2289412838u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(2904900253u, 2748915828u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(4041240379u, 605321815u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(1730010566u, 92436785u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(1362635513u, 757365378u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(1327133482u, 940350094u) == 2u);
+ MOZ_ASSERT(mozilla::GCD(3515019959u, 810874750u) == 1u);
+ MOZ_ASSERT(mozilla::GCD(82871503u, 43900000u) == 1u);
+
+ MOZ_ASSERT(mozilla::GCD(3u, 7u) == 1u);
+}
+
+int main() {
+ TestIsPowerOfTwo();
+ TestClamp();
+ TestGCD();
+
+ return 0;
+}
diff --git a/mfbt/tests/TestMaybe.cpp b/mfbt/tests/TestMaybe.cpp
new file mode 100644
index 0000000000..2c56b85b6d
--- /dev/null
+++ b/mfbt/tests/TestMaybe.cpp
@@ -0,0 +1,1473 @@
+/* -*- 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 <type_traits>
+#include <utility>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+
+using mozilla::Maybe;
+using mozilla::Nothing;
+using mozilla::Some;
+using mozilla::SomeRef;
+using mozilla::ToMaybe;
+using mozilla::ToMaybeRef;
+
+#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,
+ eWasConstMoveConstructed,
+ eWasAssigned,
+ eWasCopyAssigned,
+ eWasMoveAssigned,
+ eWasCopiedFrom,
+ eWasMovedFrom,
+ eWasConstMovedFrom,
+};
+
+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(const BasicValue&& aOther)
+ : mStatus(eWasConstMoveConstructed), mTag(aOther.mTag) {
+ ++sUndestroyedObjects;
+ aOther.mStatus = eWasConstMovedFrom;
+ }
+
+ ~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:
+ mutable 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; }
+
+ UnmovableValue(UnmovableValue&& aOther) = delete;
+ UnmovableValue& operator=(UnmovableValue&& aOther) = delete;
+
+ private:
+ Status mStatus;
+};
+
+struct UncopyableUnmovableValue {
+ UncopyableUnmovableValue() : mStatus(eWasDefaultConstructed) {
+ ++sUndestroyedObjects;
+ }
+
+ explicit UncopyableUnmovableValue(int) : mStatus(eWasConstructed) {
+ ++sUndestroyedObjects;
+ }
+
+ ~UncopyableUnmovableValue() { --sUndestroyedObjects; }
+
+ Status GetStatus() const { 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_assert(std::is_trivially_destructible_v<Maybe<int>>);
+static_assert(std::is_trivially_copy_constructible_v<Maybe<int>>);
+static_assert(std::is_trivially_copy_assignable_v<Maybe<int>>);
+
+static_assert(42 == Some(42).value());
+static_assert(42 == Some(42).valueOr(43));
+static_assert(42 == Maybe<int>{}.valueOr(42));
+static_assert(42 == Some(42).valueOrFrom([] { return 43; }));
+static_assert(42 == Maybe<int>{}.valueOrFrom([] { return 42; }));
+static_assert(Some(43) == [] {
+ auto val = Some(42);
+ val.apply([](int& val) { val += 1; });
+ return val;
+}());
+static_assert(Some(43) == Some(42).map([](int val) { return val + 1; }));
+static_assert(Maybe<int>(std::in_place, 43) ==
+ Maybe<int>(std::in_place, 42).map([](int val) {
+ return val + 1;
+ }));
+
+struct TriviallyDestructible {
+ TriviallyDestructible() { // not trivially constructible
+ }
+};
+
+static_assert(std::is_trivially_destructible_v<Maybe<TriviallyDestructible>>);
+
+struct UncopyableValueLiteralType {
+ explicit constexpr UncopyableValueLiteralType(int aValue) : mValue{aValue} {}
+
+ UncopyableValueLiteralType(UncopyableValueLiteralType&&) = default;
+ UncopyableValueLiteralType& operator=(UncopyableValueLiteralType&&) = default;
+
+ int mValue;
+};
+
+static_assert(
+ std::is_trivially_destructible_v<Maybe<UncopyableValueLiteralType>>);
+static_assert(!std::is_copy_constructible_v<Maybe<UncopyableValueLiteralType>>);
+static_assert(!std::is_copy_assignable_v<Maybe<UncopyableValueLiteralType>>);
+static_assert(std::is_move_constructible_v<Maybe<UncopyableValueLiteralType>>);
+static_assert(std::is_move_assignable_v<Maybe<UncopyableValueLiteralType>>);
+
+constexpr Maybe<UncopyableValueLiteralType> someUncopyable =
+ Some(UncopyableValueLiteralType{42});
+static_assert(someUncopyable.isSome());
+static_assert(42 == someUncopyable->mValue);
+
+constexpr Maybe<UncopyableValueLiteralType> someUncopyableAssigned = [] {
+ auto res = Maybe<UncopyableValueLiteralType>{};
+ res = Some(UncopyableValueLiteralType{42});
+ return res;
+}();
+static_assert(someUncopyableAssigned.isSome());
+static_assert(42 == someUncopyableAssigned->mValue);
+
+static bool TestBasicFeatures() {
+ // Check that a Maybe<T> is initialized to Nothing.
+ Maybe<BasicValue> mayValue;
+ static_assert(std::is_same_v<BasicValue, decltype(mayValue)::ValueType>,
+ "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());
+ static_assert(std::is_same_v<BasicValue&, decltype(*mayValue)>,
+ "operator*() should return a BasicValue&");
+ MOZ_RELEASE_ASSERT(mayValue.value() == BasicValue());
+ static_assert(std::is_same_v<BasicValue, decltype(mayValue.value())>,
+ "value() should return a BasicValue");
+ MOZ_RELEASE_ASSERT(mayValue.ref() == BasicValue());
+ static_assert(std::is_same_v<BasicValue&, decltype(mayValue.ref())>,
+ "ref() should return a BasicValue&");
+ MOZ_RELEASE_ASSERT(mayValue.ptr() != nullptr);
+ static_assert(std::is_same_v<BasicValue*, decltype(mayValue.ptr())>,
+ "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 Maybe(std::in_place, T1) calls the correct constructor.
+ const auto mayValueConstructed = Maybe<BasicValue>(std::in_place, 1);
+ MOZ_RELEASE_ASSERT(mayValueConstructed);
+ MOZ_RELEASE_ASSERT(mayValueConstructed->GetStatus() == eWasConstructed);
+ MOZ_RELEASE_ASSERT(mayValueConstructed->GetTag() == 1);
+ }
+
+ // 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());
+ static_assert(std::is_same_v<const BasicValue&, decltype(*mayValueCRef)>,
+ "operator*() should return a BasicValue");
+ MOZ_RELEASE_ASSERT(mayValueCRef.value() == BasicValue());
+ static_assert(std::is_same_v<BasicValue, decltype(mayValueCRef.value())>,
+ "value() should return a BasicValue");
+ MOZ_RELEASE_ASSERT(mayValueCRef.ref() == BasicValue());
+ static_assert(std::is_same_v<const BasicValue&, decltype(mayValueCRef.ref())>,
+ "ref() should return a const BasicValue&");
+ MOZ_RELEASE_ASSERT(mayValueCRef.ptr() != nullptr);
+ static_assert(std::is_same_v<const BasicValue*, decltype(mayValueCRef.ptr())>,
+ "ptr() should return a const BasicValue*");
+ MOZ_RELEASE_ASSERT(mayValueCRef->GetStatus() == eWasDefaultConstructed);
+ mayValue.reset();
+
+ // Check that we can create and reference Maybe<const Type>.
+ Maybe<const BasicValue> mayCValue1 = Some(BasicValue(5));
+ MOZ_RELEASE_ASSERT(mayCValue1);
+ MOZ_RELEASE_ASSERT(mayCValue1.isSome());
+ MOZ_RELEASE_ASSERT(*mayCValue1 == BasicValue(5));
+ const Maybe<const BasicValue>& mayCValue1Ref = mayCValue1;
+ MOZ_RELEASE_ASSERT(mayCValue1Ref == mayCValue1);
+ MOZ_RELEASE_ASSERT(*mayCValue1Ref == BasicValue(5));
+ Maybe<const BasicValue> mayCValue2;
+ mayCValue2.emplace(6);
+ MOZ_RELEASE_ASSERT(mayCValue2);
+ MOZ_RELEASE_ASSERT(mayCValue2.isSome());
+ MOZ_RELEASE_ASSERT(*mayCValue2 == BasicValue(6));
+
+ // Check that accessors work through rvalue-references.
+ MOZ_RELEASE_ASSERT(Some(BasicValue()));
+ MOZ_RELEASE_ASSERT(Some(BasicValue()).isSome());
+ MOZ_RELEASE_ASSERT(!Some(BasicValue()).isNothing());
+ MOZ_RELEASE_ASSERT(*Some(BasicValue()) == BasicValue());
+ static_assert(std::is_same_v<BasicValue&&, decltype(*Some(BasicValue()))>,
+ "operator*() should return a BasicValue&&");
+ MOZ_RELEASE_ASSERT(Some(BasicValue()).value() == BasicValue());
+ static_assert(
+ std::is_same_v<BasicValue, decltype(Some(BasicValue()).value())>,
+ "value() should return a BasicValue");
+ MOZ_RELEASE_ASSERT(Some(BasicValue()).ref() == BasicValue());
+ static_assert(
+ std::is_same_v<BasicValue&&, decltype(Some(BasicValue()).ref())>,
+ "ref() should return a BasicValue&&");
+ MOZ_RELEASE_ASSERT(Some(BasicValue()).ptr() != nullptr);
+ static_assert(std::is_same_v<BasicValue*, decltype(Some(BasicValue()).ptr())>,
+ "ptr() should return a BasicValue*");
+ MOZ_RELEASE_ASSERT(Some(BasicValue())->GetStatus() == eWasMoveConstructed);
+
+ // Check that accessors work through const-rvalue-references.
+ auto MakeConstMaybe = []() -> const Maybe<BasicValue> {
+ return Some(BasicValue());
+ };
+ MOZ_RELEASE_ASSERT(MakeConstMaybe());
+ MOZ_RELEASE_ASSERT(MakeConstMaybe().isSome());
+ MOZ_RELEASE_ASSERT(!MakeConstMaybe().isNothing());
+ MOZ_RELEASE_ASSERT(*MakeConstMaybe() == BasicValue());
+ static_assert(std::is_same_v<const BasicValue&&, decltype(*MakeConstMaybe())>,
+ "operator*() should return a const BasicValue&&");
+ MOZ_RELEASE_ASSERT(MakeConstMaybe().value() == BasicValue());
+ static_assert(std::is_same_v<BasicValue, decltype(MakeConstMaybe().value())>,
+ "value() should return a BasicValue");
+ MOZ_RELEASE_ASSERT(MakeConstMaybe().ref() == BasicValue());
+ static_assert(
+ std::is_same_v<const BasicValue&&, decltype(MakeConstMaybe().ref())>,
+ "ref() should return a const BasicValue&&");
+ MOZ_RELEASE_ASSERT(MakeConstMaybe().ptr() != nullptr);
+ static_assert(
+ std::is_same_v<const BasicValue*, decltype(MakeConstMaybe().ptr())>,
+ "ptr() should return a const BasicValue*");
+ MOZ_RELEASE_ASSERT(MakeConstMaybe()->GetStatus() == eWasMoveConstructed);
+ MOZ_RELEASE_ASSERT(BasicValue(*MakeConstMaybe()).GetStatus() ==
+ eWasConstMoveConstructed);
+
+ // Check that take works
+ mayValue = Some(BasicValue(6));
+ Maybe taken = mayValue.take();
+ MOZ_RELEASE_ASSERT(taken->GetStatus() == eWasMoveConstructed);
+ MOZ_RELEASE_ASSERT(taken == Some(BasicValue(6)));
+ MOZ_RELEASE_ASSERT(!mayValue.isSome());
+ MOZ_RELEASE_ASSERT(mayValue.take() == Nothing());
+
+ // Check that extract works
+ mayValue = Some(BasicValue(7));
+ BasicValue extracted = mayValue.extract();
+ MOZ_RELEASE_ASSERT(extracted.GetStatus() == eWasMoveConstructed);
+ MOZ_RELEASE_ASSERT(extracted == BasicValue(7));
+ MOZ_RELEASE_ASSERT(!mayValue.isSome());
+
+ return true;
+}
+
+template <typename T>
+static void TestCopyMaybe() {
+ {
+ MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
+
+ Maybe<T> src = Some(T());
+ Maybe<T> dstCopyConstructed = src;
+
+ MOZ_RELEASE_ASSERT(2 == sUndestroyedObjects);
+ MOZ_RELEASE_ASSERT(dstCopyConstructed->GetStatus() == eWasCopyConstructed);
+ }
+
+ {
+ MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
+
+ Maybe<T> src = Some(T());
+ Maybe<T> dstCopyAssigned;
+ dstCopyAssigned = src;
+
+ MOZ_RELEASE_ASSERT(2 == sUndestroyedObjects);
+ MOZ_RELEASE_ASSERT(dstCopyAssigned->GetStatus() == eWasCopyConstructed);
+ }
+}
+
+template <typename T>
+static void TestMoveMaybe() {
+ {
+ MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
+
+ Maybe<T> src = Some(T());
+ Maybe<T> dstMoveConstructed = std::move(src);
+
+ MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects);
+ MOZ_RELEASE_ASSERT(dstMoveConstructed->GetStatus() == eWasMoveConstructed);
+ }
+
+ {
+ MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
+
+ Maybe<T> src = Some(T());
+ Maybe<T> dstMoveAssigned;
+ dstMoveAssigned = std::move(src);
+
+ MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects);
+ MOZ_RELEASE_ASSERT(dstMoveAssigned->GetStatus() == eWasMoveConstructed);
+ }
+
+ {
+ MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
+
+ Maybe<T> src = Some(T());
+ Maybe<T> dstMoveConstructed = src.take();
+
+ MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects);
+ MOZ_RELEASE_ASSERT(dstMoveConstructed->GetStatus() == eWasMoveConstructed);
+ }
+
+ {
+ MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
+
+ Maybe<T> src = Some(T());
+ T dstMoveConstructed = src.extract();
+
+ MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects);
+ MOZ_RELEASE_ASSERT(dstMoveConstructed.GetStatus() == eWasMoveConstructed);
+ }
+}
+
+static bool TestCopyAndMove() {
+ MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
+
+ {
+ // 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(1 == sUndestroyedObjects);
+ MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMoveConstructed);
+ MOZ_RELEASE_ASSERT(mayBasicValue->GetTag() == 1);
+ mayBasicValue = Some(BasicValue(2));
+ MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects);
+ MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMoveAssigned);
+ MOZ_RELEASE_ASSERT(mayBasicValue->GetTag() == 2);
+ mayBasicValue.reset();
+ MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
+ mayBasicValue.emplace(BasicValue(3));
+ MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects);
+ 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 std::move() works. (Another sanity check for move support.)
+ Maybe<BasicValue> mayBasicValue3 = Some(std::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(std::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(std::move(*mayBasicValue3));
+ MOZ_RELEASE_ASSERT(mayBasicValue4->GetStatus() == eWasMoveConstructed);
+ MOZ_RELEASE_ASSERT(mayBasicValue4->GetTag() == 6);
+ MOZ_RELEASE_ASSERT(mayBasicValue3->GetStatus() == eWasMovedFrom);
+ }
+
+ TestCopyMaybe<BasicValue>();
+ TestMoveMaybe<BasicValue>();
+ }
+
+ MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
+
+ {
+ // Check that we always get copies for types that don't support moves.
+ {
+ 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);
+ }
+
+ TestCopyMaybe<UnmovableValue>();
+
+ static_assert(std::is_copy_constructible_v<Maybe<UnmovableValue>>);
+ static_assert(std::is_copy_assignable_v<Maybe<UnmovableValue>>);
+ // XXX Why do these static_asserts not hold?
+ // static_assert(!std::is_move_constructible_v<Maybe<UnmovableValue>>);
+ // static_assert(!std::is_move_assignable_v<Maybe<UnmovableValue>>);
+ }
+
+ MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
+
+ {
+ // 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);
+ mayUncopyableValue = Nothing();
+ }
+
+ TestMoveMaybe<BasicValue>();
+
+ static_assert(!std::is_copy_constructible_v<Maybe<UncopyableValue>>);
+ static_assert(!std::is_copy_assignable_v<Maybe<UncopyableValue>>);
+ static_assert(std::is_move_constructible_v<Maybe<UncopyableValue>>);
+ static_assert(std::is_move_assignable_v<Maybe<UncopyableValue>>);
+ }
+
+ MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
+
+ { // Check that types that support neither moves or copies work.
+ {
+ const auto mayUncopyableUnmovableValueConstructed =
+ Maybe<UncopyableUnmovableValue>{std::in_place};
+ MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValueConstructed->GetStatus() ==
+ eWasDefaultConstructed);
+ }
+
+ Maybe<UncopyableUnmovableValue> mayUncopyableUnmovableValue;
+ mayUncopyableUnmovableValue.emplace();
+ MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValue->GetStatus() ==
+ eWasDefaultConstructed);
+ mayUncopyableUnmovableValue.reset();
+ mayUncopyableUnmovableValue.emplace(0);
+ MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValue->GetStatus() ==
+ eWasConstructed);
+ mayUncopyableUnmovableValue = Nothing();
+
+ static_assert(
+ !std::is_copy_constructible_v<Maybe<UncopyableUnmovableValue>>);
+ static_assert(!std::is_copy_assignable_v<Maybe<UncopyableUnmovableValue>>);
+ static_assert(
+ !std::is_move_constructible_v<Maybe<UncopyableUnmovableValue>>);
+ static_assert(!std::is_move_assignable_v<Maybe<UncopyableUnmovableValue>>);
+ }
+
+ {
+ // Test copy and move with a trivially copyable and trivially destructible
+ // type.
+ {
+ constexpr Maybe<int> src = Some(42);
+ constexpr Maybe<int> dstCopyConstructed = src;
+
+ static_assert(src.isSome());
+ static_assert(dstCopyConstructed.isSome());
+ static_assert(42 == *src);
+ static_assert(42 == *dstCopyConstructed);
+ static_assert(42 == dstCopyConstructed.value());
+ }
+
+ {
+ const Maybe<int> src = Some(42);
+ Maybe<int> dstCopyAssigned;
+ dstCopyAssigned = src;
+
+ MOZ_RELEASE_ASSERT(src.isSome());
+ MOZ_RELEASE_ASSERT(dstCopyAssigned.isSome());
+ MOZ_RELEASE_ASSERT(42 == *src);
+ MOZ_RELEASE_ASSERT(42 == *dstCopyAssigned);
+ }
+
+ {
+ Maybe<int> src = Some(42);
+ const Maybe<int> dstMoveConstructed = std::move(src);
+
+ MOZ_RELEASE_ASSERT(!src.isSome());
+ MOZ_RELEASE_ASSERT(dstMoveConstructed.isSome());
+ MOZ_RELEASE_ASSERT(42 == *dstMoveConstructed);
+ }
+
+ {
+ Maybe<int> src = Some(42);
+ Maybe<int> dstMoveAssigned;
+ dstMoveAssigned = std::move(src);
+
+ MOZ_RELEASE_ASSERT(!src.isSome());
+ MOZ_RELEASE_ASSERT(dstMoveAssigned.isSome());
+ MOZ_RELEASE_ASSERT(42 == *dstMoveAssigned);
+ }
+ }
+
+ 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(std::is_same_v<BasicValue, decltype(someValue.valueOr(value))>,
+ "valueOr should return a BasicValue");
+ MOZ_RELEASE_ASSERT(someValue.valueOrFrom(&MakeBasicValue) == BasicValue(3));
+ static_assert(
+ std::is_same_v<BasicValue,
+ decltype(someValue.valueOrFrom(&MakeBasicValue))>,
+ "valueOrFrom should return a BasicValue");
+ MOZ_RELEASE_ASSERT(someValue.ptrOr(&value) != &value);
+ static_assert(std::is_same_v<BasicValue*, decltype(someValue.ptrOr(&value))>,
+ "ptrOr should return a BasicValue*");
+ MOZ_RELEASE_ASSERT(*someValue.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(3));
+ static_assert(
+ std::is_same_v<BasicValue*,
+ decltype(someValue.ptrOrFrom(&MakeBasicValuePtr))>,
+ "ptrOrFrom should return a BasicValue*");
+ MOZ_RELEASE_ASSERT(someValue.refOr(value) == BasicValue(3));
+ static_assert(std::is_same_v<BasicValue&, decltype(someValue.refOr(value))>,
+ "refOr should return a BasicValue&");
+ MOZ_RELEASE_ASSERT(someValue.refOrFrom(&MakeBasicValueRef) == BasicValue(3));
+ static_assert(
+ std::is_same_v<BasicValue&,
+ decltype(someValue.refOrFrom(&MakeBasicValueRef))>,
+ "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(
+ std::is_same_v<BasicValue, decltype(someValueCRef.valueOr(value))>,
+ "valueOr should return a BasicValue");
+ MOZ_RELEASE_ASSERT(someValueCRef.valueOrFrom(&MakeBasicValue) ==
+ BasicValue(3));
+ static_assert(
+ std::is_same_v<BasicValue,
+ decltype(someValueCRef.valueOrFrom(&MakeBasicValue))>,
+ "valueOrFrom should return a BasicValue");
+ MOZ_RELEASE_ASSERT(someValueCRef.ptrOr(&value) != &value);
+ static_assert(
+ std::is_same_v<const BasicValue*, decltype(someValueCRef.ptrOr(&value))>,
+ "ptrOr should return a const BasicValue*");
+ MOZ_RELEASE_ASSERT(*someValueCRef.ptrOrFrom(&MakeBasicValuePtr) ==
+ BasicValue(3));
+ static_assert(
+ std::is_same_v<const BasicValue*,
+ decltype(someValueCRef.ptrOrFrom(&MakeBasicValuePtr))>,
+ "ptrOrFrom should return a const BasicValue*");
+ MOZ_RELEASE_ASSERT(someValueCRef.refOr(value) == BasicValue(3));
+ static_assert(
+ std::is_same_v<const BasicValue&, decltype(someValueCRef.refOr(value))>,
+ "refOr should return a const BasicValue&");
+ MOZ_RELEASE_ASSERT(someValueCRef.refOrFrom(&MakeBasicValueRef) ==
+ BasicValue(3));
+ static_assert(
+ std::is_same_v<const BasicValue&,
+ decltype(someValueCRef.refOrFrom(&MakeBasicValueRef))>,
+ "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(std::is_same_v<BasicValue, decltype(noneValue.valueOr(value))>,
+ "valueOr should return a BasicValue");
+ MOZ_RELEASE_ASSERT(noneValue.valueOrFrom(&MakeBasicValue) == BasicValue(9));
+ static_assert(
+ std::is_same_v<BasicValue,
+ decltype(noneValue.valueOrFrom(&MakeBasicValue))>,
+ "valueOrFrom should return a BasicValue");
+ MOZ_RELEASE_ASSERT(noneValue.ptrOr(&value) == &value);
+ static_assert(std::is_same_v<BasicValue*, decltype(noneValue.ptrOr(&value))>,
+ "ptrOr should return a BasicValue*");
+ MOZ_RELEASE_ASSERT(*noneValue.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(9));
+ static_assert(
+ std::is_same_v<BasicValue*,
+ decltype(noneValue.ptrOrFrom(&MakeBasicValuePtr))>,
+ "ptrOrFrom should return a BasicValue*");
+ MOZ_RELEASE_ASSERT(noneValue.refOr(value) == BasicValue(9));
+ static_assert(std::is_same_v<BasicValue&, decltype(noneValue.refOr(value))>,
+ "refOr should return a BasicValue&");
+ MOZ_RELEASE_ASSERT(noneValue.refOrFrom(&MakeBasicValueRef) == BasicValue(9));
+ static_assert(
+ std::is_same_v<BasicValue&,
+ decltype(noneValue.refOrFrom(&MakeBasicValueRef))>,
+ "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(
+ std::is_same_v<BasicValue, decltype(noneValueCRef.valueOr(value))>,
+ "valueOr should return a BasicValue");
+ MOZ_RELEASE_ASSERT(noneValueCRef.valueOrFrom(&MakeBasicValue) ==
+ BasicValue(9));
+ static_assert(
+ std::is_same_v<BasicValue,
+ decltype(noneValueCRef.valueOrFrom(&MakeBasicValue))>,
+ "valueOrFrom should return a BasicValue");
+ MOZ_RELEASE_ASSERT(noneValueCRef.ptrOr(&value) == &value);
+ static_assert(
+ std::is_same_v<const BasicValue*, decltype(noneValueCRef.ptrOr(&value))>,
+ "ptrOr should return a const BasicValue*");
+ MOZ_RELEASE_ASSERT(*noneValueCRef.ptrOrFrom(&MakeBasicValuePtr) ==
+ BasicValue(9));
+ static_assert(
+ std::is_same_v<const BasicValue*,
+ decltype(noneValueCRef.ptrOrFrom(&MakeBasicValuePtr))>,
+ "ptrOrFrom should return a const BasicValue*");
+ MOZ_RELEASE_ASSERT(noneValueCRef.refOr(value) == BasicValue(9));
+ static_assert(
+ std::is_same_v<const BasicValue&, decltype(noneValueCRef.refOr(value))>,
+ "refOr should return a const BasicValue&");
+ MOZ_RELEASE_ASSERT(noneValueCRef.refOrFrom(&MakeBasicValueRef) ==
+ BasicValue(9));
+ static_assert(
+ std::is_same_v<const BasicValue&,
+ decltype(noneValueCRef.refOrFrom(&MakeBasicValueRef))>,
+ "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(std::is_same_v<Maybe<int>, decltype(mayValue.map(&TimesTwo))>,
+ "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(
+ std::is_same_v<Maybe<int>, decltype(mayValueCRef.map(&TimesTwo))>,
+ "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));
+
+ // Check that function object qualifiers are preserved when invoked.
+ struct F {
+ std::integral_constant<int, 1> operator()(int) & { return {}; }
+ std::integral_constant<int, 2> operator()(int) const& { return {}; }
+ std::integral_constant<int, 3> operator()(int) && { return {}; }
+ std::integral_constant<int, 4> operator()(int) const&& { return {}; }
+ };
+ Maybe<int> mi = Some(0);
+ const Maybe<int> cmi = Some(0);
+ F f;
+ static_assert(std::is_same<decltype(mi.map(f)),
+ Maybe<std::integral_constant<int, 1>>>::value,
+ "Maybe.map(&)");
+ MOZ_RELEASE_ASSERT(mi.map(f).value()() == 1);
+ static_assert(std::is_same<decltype(cmi.map(f)),
+ Maybe<std::integral_constant<int, 1>>>::value,
+ "const Maybe.map(&)");
+ MOZ_RELEASE_ASSERT(cmi.map(f).value()() == 1);
+ const F cf;
+ static_assert(std::is_same<decltype(mi.map(cf)),
+ Maybe<std::integral_constant<int, 2>>>::value,
+ "Maybe.map(const &)");
+ MOZ_RELEASE_ASSERT(mi.map(cf).value() == 2);
+ static_assert(std::is_same<decltype(cmi.map(cf)),
+ Maybe<std::integral_constant<int, 2>>>::value,
+ "const Maybe.map(const &)");
+ MOZ_RELEASE_ASSERT(cmi.map(cf).value() == 2);
+ static_assert(std::is_same<decltype(mi.map(F{})),
+ Maybe<std::integral_constant<int, 3>>>::value,
+ "Maybe.map(&&)");
+ MOZ_RELEASE_ASSERT(mi.map(F{}).value() == 3);
+ static_assert(std::is_same<decltype(cmi.map(F{})),
+ Maybe<std::integral_constant<int, 3>>>::value,
+ "const Maybe.map(&&)");
+ MOZ_RELEASE_ASSERT(cmi.map(F{}).value() == 3);
+ using CF = const F;
+ static_assert(std::is_same<decltype(mi.map(CF{})),
+ Maybe<std::integral_constant<int, 4>>>::value,
+ "Maybe.map(const &&)");
+ MOZ_RELEASE_ASSERT(mi.map(CF{}).value() == 4);
+ static_assert(std::is_same<decltype(cmi.map(CF{})),
+ Maybe<std::integral_constant<int, 4>>>::value,
+ "const Maybe.map(const &&)");
+ MOZ_RELEASE_ASSERT(cmi.map(CF{}).value() == 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(std::is_same_v<Maybe<BasicValue>, decltype(ToMaybe(&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(
+ std::is_same_v<Maybe<BasicValue>, decltype(ToMaybe(nullPointer))>,
+ "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(const 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(const 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;
+}
+
+struct SourceType1 {
+ int mTag;
+
+ operator int() const { return mTag; }
+};
+struct DestType {
+ int mTag;
+ Status mStatus;
+
+ DestType() : mTag(0), mStatus(eWasDefaultConstructed) {}
+
+ MOZ_IMPLICIT DestType(int aTag) : mTag(aTag), mStatus(eWasConstructed) {}
+
+ MOZ_IMPLICIT DestType(SourceType1&& aSrc)
+ : mTag(aSrc.mTag), mStatus(eWasMoveConstructed) {}
+
+ MOZ_IMPLICIT DestType(const SourceType1& aSrc)
+ : mTag(aSrc.mTag), mStatus(eWasCopyConstructed) {}
+
+ DestType& operator=(int aTag) {
+ mTag = aTag;
+ mStatus = eWasAssigned;
+ return *this;
+ }
+
+ DestType& operator=(SourceType1&& aSrc) {
+ mTag = aSrc.mTag;
+ mStatus = eWasMoveAssigned;
+ return *this;
+ }
+
+ DestType& operator=(const SourceType1& aSrc) {
+ mTag = aSrc.mTag;
+ mStatus = eWasCopyAssigned;
+ return *this;
+ }
+};
+struct SourceType2 {
+ int mTag;
+
+ operator DestType() const& {
+ DestType result;
+ result.mTag = mTag;
+ result.mStatus = eWasCopiedFrom;
+ return result;
+ }
+
+ operator DestType() && {
+ DestType result;
+ result.mTag = mTag;
+ result.mStatus = eWasMovedFrom;
+ return result;
+ }
+};
+
+static bool TestTypeConversion() {
+ {
+ Maybe<SourceType1> src = Some(SourceType1{1});
+ Maybe<DestType> dest = src;
+ MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 1);
+ MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
+ MOZ_RELEASE_ASSERT(dest->mStatus == eWasCopyConstructed);
+
+ src = Some(SourceType1{2});
+ dest = src;
+ MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 2);
+ MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
+ MOZ_RELEASE_ASSERT(dest->mStatus == eWasCopyAssigned);
+ }
+
+ {
+ Maybe<SourceType1> src = Some(SourceType1{1});
+ Maybe<DestType> dest = std::move(src);
+ MOZ_RELEASE_ASSERT(src.isNothing());
+ MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
+ MOZ_RELEASE_ASSERT(dest->mStatus == eWasMoveConstructed);
+
+ src = Some(SourceType1{2});
+ dest = std::move(src);
+ MOZ_RELEASE_ASSERT(src.isNothing());
+ MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
+ MOZ_RELEASE_ASSERT(dest->mStatus == eWasMoveAssigned);
+ }
+
+ {
+ Maybe<SourceType2> src = Some(SourceType2{1});
+ Maybe<DestType> dest = src;
+ MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 1);
+ MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
+ MOZ_RELEASE_ASSERT(dest->mStatus == eWasCopiedFrom);
+
+ src = Some(SourceType2{2});
+ dest = src;
+ MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 2);
+ MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
+ MOZ_RELEASE_ASSERT(dest->mStatus == eWasCopiedFrom);
+ }
+
+ {
+ Maybe<SourceType2> src = Some(SourceType2{1});
+ Maybe<DestType> dest = std::move(src);
+ MOZ_RELEASE_ASSERT(src.isNothing());
+ MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
+ MOZ_RELEASE_ASSERT(dest->mStatus == eWasMovedFrom);
+
+ src = Some(SourceType2{2});
+ dest = std::move(src);
+ MOZ_RELEASE_ASSERT(src.isNothing());
+ MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
+ MOZ_RELEASE_ASSERT(dest->mStatus == eWasMovedFrom);
+ }
+
+ {
+ Maybe<int> src = Some(1);
+ Maybe<DestType> dest = src;
+ MOZ_RELEASE_ASSERT(src.isSome() && *src == 1);
+ MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
+ MOZ_RELEASE_ASSERT(dest->mStatus == eWasConstructed);
+
+ src = Some(2);
+ dest = src;
+ MOZ_RELEASE_ASSERT(src.isSome() && *src == 2);
+ MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
+ MOZ_RELEASE_ASSERT(dest->mStatus == eWasAssigned);
+ }
+
+ {
+ Maybe<int> src = Some(1);
+ Maybe<DestType> dest = std::move(src);
+ MOZ_RELEASE_ASSERT(src.isNothing());
+ MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
+ MOZ_RELEASE_ASSERT(dest->mStatus == eWasConstructed);
+
+ src = Some(2);
+ dest = std::move(src);
+ MOZ_RELEASE_ASSERT(src.isNothing());
+ MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
+ MOZ_RELEASE_ASSERT(dest->mStatus == eWasAssigned);
+ }
+
+ {
+ Maybe<SourceType1> src = Some(SourceType1{1});
+ Maybe<int> dest = src;
+ MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 1);
+ MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 1);
+
+ src = Some(SourceType1{2});
+ dest = src;
+ MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 2);
+ MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 2);
+ }
+
+ {
+ Maybe<SourceType1> src = Some(SourceType1{1});
+ Maybe<int> dest = std::move(src);
+ MOZ_RELEASE_ASSERT(src.isNothing());
+ MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 1);
+
+ src = Some(SourceType1{2});
+ dest = std::move(src);
+ MOZ_RELEASE_ASSERT(src.isNothing());
+ MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 2);
+ }
+
+ {
+ Maybe<size_t> src = Some(1);
+ Maybe<char16_t> dest = src;
+ MOZ_RELEASE_ASSERT(src.isSome() && *src == 1);
+ MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 1);
+
+ src = Some(2);
+ dest = src;
+ MOZ_RELEASE_ASSERT(src.isSome() && *src == 2);
+ MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 2);
+ }
+
+ {
+ Maybe<size_t> src = Some(1);
+ Maybe<char16_t> dest = std::move(src);
+ MOZ_RELEASE_ASSERT(src.isNothing());
+ MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 1);
+
+ src = Some(2);
+ dest = std::move(src);
+ MOZ_RELEASE_ASSERT(src.isNothing());
+ MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 2);
+ }
+
+ return true;
+}
+
+static bool TestReference() {
+ static_assert(std::is_trivially_destructible_v<Maybe<int&>>);
+ static_assert(std::is_trivially_copy_constructible_v<Maybe<int&>>);
+ static_assert(std::is_trivially_copy_assignable_v<Maybe<int&>>);
+
+ static_assert(Maybe<int&>{}.isNothing());
+ static_assert(Maybe<int&>{Nothing{}}.isNothing());
+
+ {
+ Maybe<int&> defaultConstructed;
+
+ MOZ_RELEASE_ASSERT(defaultConstructed.isNothing());
+ MOZ_RELEASE_ASSERT(!defaultConstructed.isSome());
+ MOZ_RELEASE_ASSERT(!defaultConstructed);
+ }
+
+ {
+ Maybe<int&> nothing = Nothing();
+
+ MOZ_RELEASE_ASSERT(nothing.isNothing());
+ MOZ_RELEASE_ASSERT(!nothing.isSome());
+ MOZ_RELEASE_ASSERT(!nothing);
+ }
+
+ {
+ int foo = 42, bar = 42;
+ Maybe<int&> some = SomeRef(foo);
+
+ MOZ_RELEASE_ASSERT(!some.isNothing());
+ MOZ_RELEASE_ASSERT(some.isSome());
+ MOZ_RELEASE_ASSERT(some);
+ MOZ_RELEASE_ASSERT(&some.ref() == &foo);
+
+ MOZ_RELEASE_ASSERT(some.refEquals(foo));
+ MOZ_RELEASE_ASSERT(some.refEquals(SomeRef(foo)));
+ MOZ_RELEASE_ASSERT(!some.refEquals(Nothing()));
+ MOZ_RELEASE_ASSERT(!some.refEquals(bar));
+ MOZ_RELEASE_ASSERT(!some.refEquals(SomeRef(bar)));
+
+ some.ref()++;
+ MOZ_RELEASE_ASSERT(43 == foo);
+
+ (*some)++;
+ MOZ_RELEASE_ASSERT(44 == foo);
+ }
+
+ {
+ int foo = 42, bar = 42;
+ Maybe<int&> some;
+ some.emplace(foo);
+
+ MOZ_RELEASE_ASSERT(!some.isNothing());
+ MOZ_RELEASE_ASSERT(some.isSome());
+ MOZ_RELEASE_ASSERT(some);
+ MOZ_RELEASE_ASSERT(&some.ref() == &foo);
+
+ MOZ_RELEASE_ASSERT(some.refEquals(foo));
+ MOZ_RELEASE_ASSERT(some.refEquals(SomeRef(foo)));
+ MOZ_RELEASE_ASSERT(!some.refEquals(Nothing()));
+ MOZ_RELEASE_ASSERT(!some.refEquals(bar));
+ MOZ_RELEASE_ASSERT(!some.refEquals(SomeRef(bar)));
+
+ some.ref()++;
+ MOZ_RELEASE_ASSERT(43 == foo);
+ }
+
+ {
+ Maybe<int&> defaultConstructed;
+ defaultConstructed.reset();
+
+ MOZ_RELEASE_ASSERT(defaultConstructed.isNothing());
+ MOZ_RELEASE_ASSERT(!defaultConstructed.isSome());
+ MOZ_RELEASE_ASSERT(!defaultConstructed);
+ }
+
+ {
+ int foo = 42;
+ Maybe<int&> some = SomeRef(foo);
+ some.reset();
+
+ MOZ_RELEASE_ASSERT(some.isNothing());
+ MOZ_RELEASE_ASSERT(!some.isSome());
+ MOZ_RELEASE_ASSERT(!some);
+ }
+
+ {
+ int foo = 42;
+ Maybe<int&> some = SomeRef(foo);
+
+ auto& applied = some.apply([](int& ref) { ref++; });
+
+ MOZ_RELEASE_ASSERT(&some == &applied);
+ MOZ_RELEASE_ASSERT(43 == foo);
+ }
+
+ {
+ Maybe<int&> nothing;
+
+ auto& applied = nothing.apply([](int& ref) { ref++; });
+
+ MOZ_RELEASE_ASSERT(&nothing == &applied);
+ }
+
+ {
+ int foo = 42;
+ Maybe<int&> some = SomeRef(foo);
+
+ auto mapped = some.map([](int& ref) { return &ref; });
+ static_assert(std::is_same_v<decltype(mapped), Maybe<int*>>);
+
+ MOZ_RELEASE_ASSERT(&foo == *mapped);
+ }
+
+ {
+ Maybe<int&> nothing;
+
+ auto mapped = nothing.map([](int& ref) { return &ref; });
+
+ MOZ_RELEASE_ASSERT(mapped.isNothing());
+ MOZ_RELEASE_ASSERT(!mapped.isSome());
+ MOZ_RELEASE_ASSERT(!mapped);
+ }
+
+ {
+ int foo = 42;
+ auto someRef = ToMaybeRef(&foo);
+
+ static_assert(std::is_same_v<decltype(someRef), Maybe<int&>>);
+
+ MOZ_RELEASE_ASSERT(someRef.isSome());
+ MOZ_RELEASE_ASSERT(&foo == &someRef.ref());
+ }
+
+ {
+ int* fooPtr = nullptr;
+ auto someRef = ToMaybeRef(fooPtr);
+
+ static_assert(std::is_same_v<decltype(someRef), Maybe<int&>>);
+
+ MOZ_RELEASE_ASSERT(someRef.isNothing());
+ }
+
+ return true;
+}
+
+// These are quasi-implementation details, but we assert them here to prevent
+// backsliding to earlier times when Maybe<T> for smaller T took up more space
+// than T's alignment required.
+
+static_assert(sizeof(Maybe<char>) == 2 * sizeof(char),
+ "Maybe<char> shouldn't bloat at all ");
+static_assert(sizeof(Maybe<bool>) <= 2 * sizeof(bool),
+ "Maybe<bool> shouldn't bloat");
+static_assert(sizeof(Maybe<int>) <= 2 * sizeof(int),
+ "Maybe<int> shouldn't bloat");
+static_assert(sizeof(Maybe<long>) <= 2 * sizeof(long),
+ "Maybe<long> shouldn't bloat");
+static_assert(sizeof(Maybe<double>) <= 2 * sizeof(double),
+ "Maybe<double> shouldn't bloat");
+static_assert(sizeof(Maybe<int&>) == sizeof(int*));
+
+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);
+ RUN_TEST(TestTypeConversion);
+ RUN_TEST(TestReference);
+
+ return 0;
+}
diff --git a/mfbt/tests/TestNonDereferenceable.cpp b/mfbt/tests/TestNonDereferenceable.cpp
new file mode 100644
index 0000000000..2f8f7c1dd1
--- /dev/null
+++ b/mfbt/tests/TestNonDereferenceable.cpp
@@ -0,0 +1,171 @@
+/* -*- 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 <utility>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/NonDereferenceable.h"
+
+using mozilla::NonDereferenceable;
+
+#define CHECK MOZ_RELEASE_ASSERT
+
+void TestNonDereferenceableSimple() {
+ // Default construction.
+ NonDereferenceable<int> nd0;
+ CHECK(!nd0);
+ CHECK(!nd0.value());
+
+ int i = 1;
+ int i2 = 2;
+
+ // Construction with pointer.
+ NonDereferenceable<int> nd1(&i);
+ CHECK(!!nd1);
+ CHECK(nd1.value() == reinterpret_cast<uintptr_t>(&i));
+
+ // Assignment with pointer.
+ nd1 = &i2;
+ CHECK(nd1.value() == reinterpret_cast<uintptr_t>(&i2));
+
+ // Copy-construction.
+ NonDereferenceable<int> nd2(nd1);
+ CHECK(nd2.value() == reinterpret_cast<uintptr_t>(&i2));
+
+ // Copy-assignment.
+ nd2 = nd0;
+ CHECK(!nd2.value());
+
+ // Move-construction.
+ NonDereferenceable<int> nd3{NonDereferenceable<int>(&i)};
+ CHECK(nd3.value() == reinterpret_cast<uintptr_t>(&i));
+
+ // Move-assignment.
+ nd3 = std::move(nd1);
+ CHECK(nd3.value() == reinterpret_cast<uintptr_t>(&i2));
+ // Note: Not testing nd1's value because we don't want to assume what state
+ // it is left in after move. But at least it should be reusable:
+ nd1 = &i;
+ CHECK(nd1.value() == reinterpret_cast<uintptr_t>(&i));
+}
+
+void TestNonDereferenceableHierarchy() {
+ struct Base1 {
+ // Member variable, to make sure Base1 is not empty.
+ int x1;
+ };
+ struct Base2 {
+ int x2;
+ };
+ struct Derived : Base1, Base2 {};
+
+ Derived d;
+
+ // Construct NonDereferenceable from raw pointer.
+ NonDereferenceable<Derived> ndd = NonDereferenceable<Derived>(&d);
+ CHECK(ndd);
+ CHECK(ndd.value() == reinterpret_cast<uintptr_t>(&d));
+
+ // Cast Derived to Base1.
+ NonDereferenceable<Base1> ndb1 = ndd;
+ CHECK(ndb1);
+ CHECK(ndb1.value() == reinterpret_cast<uintptr_t>(static_cast<Base1*>(&d)));
+
+ // Cast Base1 back to Derived.
+ NonDereferenceable<Derived> nddb1 = ndb1;
+ CHECK(nddb1.value() == reinterpret_cast<uintptr_t>(&d));
+
+ // Cast Derived to Base2.
+ NonDereferenceable<Base2> ndb2 = ndd;
+ CHECK(ndb2);
+ CHECK(ndb2.value() == reinterpret_cast<uintptr_t>(static_cast<Base2*>(&d)));
+ // Sanity check that Base2 should be offset from the start of Derived.
+ CHECK(ndb2.value() != ndd.value());
+
+ // Cast Base2 back to Derived.
+ NonDereferenceable<Derived> nddb2 = ndb2;
+ CHECK(nddb2.value() == reinterpret_cast<uintptr_t>(&d));
+
+ // Note that it's not possible to jump between bases, as they're not obviously
+ // related, i.e.: `NonDereferenceable<Base2> ndb22 = ndb1;` doesn't compile.
+ // However it's possible to explicitly navigate through the derived object:
+ NonDereferenceable<Base2> ndb22 = NonDereferenceable<Derived>(ndb1);
+ CHECK(ndb22.value() == reinterpret_cast<uintptr_t>(static_cast<Base2*>(&d)));
+
+ // Handling nullptr; should stay nullptr even for offset bases.
+ ndd = nullptr;
+ CHECK(!ndd);
+ CHECK(!ndd.value());
+ ndb1 = ndd;
+ CHECK(!ndb1);
+ CHECK(!ndb1.value());
+ ndb2 = ndd;
+ CHECK(!ndb2);
+ CHECK(!ndb2.value());
+ nddb2 = ndb2;
+ CHECK(!nddb2);
+ CHECK(!nddb2.value());
+}
+
+template <typename T, size_t Index>
+struct CRTPBase {
+ // Convert `this` from `CRTPBase*` to `T*` while construction is still in
+ // progress; normally UBSan -fsanitize=vptr would catch this, but using
+ // NonDereferenceable should keep UBSan happy.
+ CRTPBase() : mDerived(this) {}
+ NonDereferenceable<T> mDerived;
+};
+
+void TestNonDereferenceableCRTP() {
+ struct Derived : CRTPBase<Derived, 1>, CRTPBase<Derived, 2> {};
+ using Base1 = Derived::CRTPBase<Derived, 1>;
+ using Base2 = Derived::CRTPBase<Derived, 2>;
+
+ Derived d;
+ // Verify that base constructors have correctly captured the address of the
+ // (at the time still incomplete) derived object.
+ CHECK(d.Base1::mDerived.value() == reinterpret_cast<uintptr_t>(&d));
+ CHECK(d.Base2::mDerived.value() == reinterpret_cast<uintptr_t>(&d));
+
+ // Construct NonDereferenceable from raw pointer.
+ NonDereferenceable<Derived> ndd = NonDereferenceable<Derived>(&d);
+ CHECK(ndd);
+ CHECK(ndd.value() == reinterpret_cast<uintptr_t>(&d));
+
+ // Cast Derived to Base1.
+ NonDereferenceable<Base1> ndb1 = ndd;
+ CHECK(ndb1);
+ CHECK(ndb1.value() == reinterpret_cast<uintptr_t>(static_cast<Base1*>(&d)));
+
+ // Cast Base1 back to Derived.
+ NonDereferenceable<Derived> nddb1 = ndb1;
+ CHECK(nddb1.value() == reinterpret_cast<uintptr_t>(&d));
+
+ // Cast Derived to Base2.
+ NonDereferenceable<Base2> ndb2 = ndd;
+ CHECK(ndb2);
+ CHECK(ndb2.value() == reinterpret_cast<uintptr_t>(static_cast<Base2*>(&d)));
+ // Sanity check that Base2 should be offset from the start of Derived.
+ CHECK(ndb2.value() != ndd.value());
+
+ // Cast Base2 back to Derived.
+ NonDereferenceable<Derived> nddb2 = ndb2;
+ CHECK(nddb2.value() == reinterpret_cast<uintptr_t>(&d));
+
+ // Note that it's not possible to jump between bases, as they're not obviously
+ // related, i.e.: `NonDereferenceable<Base2> ndb22 = ndb1;` doesn't compile.
+ // However it's possible to explicitly navigate through the derived object:
+ NonDereferenceable<Base2> ndb22 = NonDereferenceable<Derived>(ndb1);
+ CHECK(ndb22.value() == reinterpret_cast<uintptr_t>(static_cast<Base2*>(&d)));
+}
+
+int main() {
+ TestNonDereferenceableSimple();
+ TestNonDereferenceableHierarchy();
+ TestNonDereferenceableCRTP();
+
+ return 0;
+}
diff --git a/mfbt/tests/TestNotNull.cpp b/mfbt/tests/TestNotNull.cpp
new file mode 100644
index 0000000000..b9a62ea4d9
--- /dev/null
+++ b/mfbt/tests/TestNotNull.cpp
@@ -0,0 +1,386 @@
+/* -*- 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 <type_traits>
+
+#include "mozilla/NotNull.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+
+using mozilla::MakeNotNull;
+using mozilla::NotNull;
+using mozilla::UniquePtr;
+using mozilla::WrapNotNull;
+
+#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.
+
+ NotNull<RefPtr<MyRefType>> r6 = std::move(r2);
+ mozilla::Unused << r6;
+
+ CHECK(r2.get());
+ CHECK(r6.get());
+
+ // At this point the refcount is 5 again, since NotNull is not movable.
+
+ // At function's end all RefPtrs are destroyed and the refcount drops to 0
+ // and the MyRefType is destroyed.
+}
+
+// Create a derived object and store its base pointer.
+struct Base {
+ virtual ~Base() = default;
+ virtual bool IsDerived() const { return false; }
+};
+struct Derived : Base {
+ bool IsDerived() const override { return true; }
+};
+
+void TestMakeNotNull() {
+ // Raw pointer.
+ auto nni = MakeNotNull<int*>(11);
+ static_assert(std::is_same_v<NotNull<int*>, decltype(nni)>,
+ "MakeNotNull<int*> should return NotNull<int*>");
+ CHECK(*nni == 11);
+ delete nni;
+
+ // Raw pointer to const.
+ auto nnci = MakeNotNull<const int*>(12);
+ static_assert(std::is_same_v<NotNull<const int*>, decltype(nnci)>,
+ "MakeNotNull<const int*> should return NotNull<const int*>");
+ CHECK(*nnci == 12);
+ delete nnci;
+
+ auto nnd = MakeNotNull<Derived*>();
+ static_assert(std::is_same_v<NotNull<Derived*>, decltype(nnd)>,
+ "MakeNotNull<Derived*> should return NotNull<Derived*>");
+ CHECK(nnd->IsDerived());
+ delete nnd;
+ NotNull<Base*> nnb = MakeNotNull<Derived*>();
+ static_assert(std::is_same_v<NotNull<Base*>, decltype(nnb)>,
+ "MakeNotNull<Derived*> should be assignable to NotNull<Base*>");
+ // Check that we have really built a Derived object.
+ CHECK(nnb->IsDerived());
+ delete nnb;
+
+ // Allow smart pointers.
+ auto nnmi = MakeNotNull<MyPtr<int>>(23);
+ static_assert(std::is_same_v<NotNull<MyPtr<int>>, decltype(nnmi)>,
+ "MakeNotNull<MyPtr<int>> should return NotNull<MyPtr<int>>");
+ CHECK(*nnmi == 23);
+ delete nnmi.get().get();
+
+ auto nnui = MakeNotNull<UniquePtr<int>>(24);
+ static_assert(
+ std::is_same_v<NotNull<UniquePtr<int>>, decltype(nnui)>,
+ "MakeNotNull<UniquePtr<int>> should return NotNull<UniquePtr<int>>");
+ CHECK(*nnui == 24);
+
+ // Expect only 1 RefCnt (from construction).
+ auto nnr = MakeNotNull<RefPtr<MyRefType>>(1);
+ static_assert(std::is_same_v<NotNull<RefPtr<MyRefType>>, decltype(nnr)>,
+ "MakeNotNull<RefPtr<MyRefType>> should return "
+ "NotNull<RefPtr<MyRefType>>");
+ mozilla::Unused << nnr;
+}
+
+mozilla::MovingNotNull<UniquePtr<int>> CreateNotNullUniquePtr() {
+ return mozilla::WrapMovingNotNull(mozilla::MakeUnique<int>(42));
+}
+
+void TestMovingNotNull() {
+ UniquePtr<int> x1 = CreateNotNullUniquePtr();
+ CHECK(x1);
+ CHECK(42 == *x1);
+
+ NotNull<UniquePtr<int>> x2 = CreateNotNullUniquePtr();
+ CHECK(42 == *x2);
+
+ NotNull<UniquePtr<Base>> x3 =
+ mozilla::WrapMovingNotNull(mozilla::MakeUnique<Derived>());
+
+ // Must not compile:
+ // auto y = CreateNotNullUniquePtr();
+}
+
+int main() {
+ TestNotNullWithMyPtr();
+ TestNotNullWithRefPtr();
+ TestMakeNotNull();
+ TestMovingNotNull();
+
+ return 0;
+}
diff --git a/mfbt/tests/TestPoisonArea.cpp b/mfbt/tests/TestPoisonArea.cpp
new file mode 100644
index 0000000000..9df3929834
--- /dev/null
+++ b/mfbt/tests/TestPoisonArea.cpp
@@ -0,0 +1,530 @@
+/* -*- 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.
+ */
+
+// MAP_ANON(YMOUS) is not in any standard. Add defines as necessary.
+#define _GNU_SOURCE 1
+#define _DARWIN_C_SOURCE 1
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _WIN32
+# include <windows.h>
+#else
+# include <sys/types.h>
+# include <unistd.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 __m68k__
+# define RETURN_INSTR 0x4E754E75 /* rts; rts */
+
+#elif defined __riscv
+# define RETURN_INSTR 0x80828082 /* ret; ret */
+
+#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 __sh__
+# define RETURN_INSTR 0x0b000b00 /* rts; rts */
+
+#elif defined __aarch64__ || defined _M_ARM64
+# define RETURN_INSTR 0xd65f03c0 /* ret */
+
+#elif defined __loongarch64
+# define RETURN_INSTR 0x4c000020 /* jirl zero, ra, 0 */
+
+#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) {
+# ifdef XP_SOLARIS
+ return !!posix_madvise(reinterpret_cast<void*>(aPage), PageSize(),
+ POSIX_MADV_NORMAL);
+# else
+ return !!madvise(reinterpret_cast<void*>(aPage), PageSize(), MADV_NORMAL);
+# endif
+}
+
+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;
+}
+
+#ifndef _WIN32
+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
+}
+#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 = true;
+ MEMORY_BASIC_INFORMATION mbi = {};
+
+ if (VirtualQuery((LPCVOID)opaddr, &mbi, sizeof(mbi)) &&
+ mbi.State == MEM_COMMIT) {
+ switch (test) {
+ case 0: // read
+ badptr = !(mbi.Protect & (PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE |
+ PAGE_READONLY | PAGE_READWRITE));
+ break;
+ case 1: // execute
+ badptr =
+ !(mbi.Protect & (PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE));
+ break;
+ case 2: // write
+ badptr = !(mbi.Protect & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE));
+ 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/TestRandomNum.cpp b/mfbt/tests/TestRandomNum.cpp
new file mode 100644
index 0000000000..f53c42fc83
--- /dev/null
+++ b/mfbt/tests/TestRandomNum.cpp
@@ -0,0 +1,61 @@
+/* -*- 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/RandomNum.h"
+#include <vector>
+
+/*
+
+ * We're going to check that random number generation is sane on a basic
+ * level - That is, we want to check that the function returns success
+ * and doesn't just keep returning the same number.
+ *
+ * Note that there are many more tests that could be done, but to really test
+ * a PRNG we'd probably need to generate a large set of randoms and
+ * perform statistical analysis on them. Maybe that's worth doing eventually?
+ *
+ * For now we should be fine just performing a dumb test of generating 5
+ * numbers and making sure they're all unique. In theory, it is possible for
+ * this test to report a false negative, but with 5 numbers the probability
+ * is less than one-in-a-trillion.
+ *
+ */
+
+#define NUM_RANDOMS_TO_GENERATE 5
+
+using mozilla::Maybe;
+using mozilla::RandomUint64;
+
+static uint64_t getRandomUint64OrDie() {
+ Maybe<uint64_t> maybeRandomNum = RandomUint64();
+
+ MOZ_RELEASE_ASSERT(maybeRandomNum.isSome());
+
+ return maybeRandomNum.value();
+}
+
+static void TestRandomUint64() {
+ // The allocator uses RandomNum.h too, but its initialization path allocates
+ // memory. While the allocator itself handles the situation, we can't, so
+ // we make sure to use an allocation before getting a Random number ourselves.
+ std::vector<uint64_t> randomsList;
+ randomsList.reserve(NUM_RANDOMS_TO_GENERATE);
+
+ for (uint8_t i = 0; i < NUM_RANDOMS_TO_GENERATE; ++i) {
+ uint64_t randomNum = getRandomUint64OrDie();
+
+ for (uint64_t num : randomsList) {
+ MOZ_RELEASE_ASSERT(randomNum != num);
+ }
+
+ randomsList.push_back(randomNum);
+ }
+}
+
+int main() {
+ TestRandomUint64();
+ return 0;
+}
diff --git a/mfbt/tests/TestRange.cpp b/mfbt/tests/TestRange.cpp
new file mode 100644
index 0000000000..a3bc134896
--- /dev/null
+++ b/mfbt/tests/TestRange.cpp
@@ -0,0 +1,29 @@
+/* -*- 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 <type_traits>
+
+using mozilla::Range;
+
+static_assert(std::is_convertible_v<Range<int>, Range<const int>>,
+ "Range should convert into const");
+static_assert(!std::is_convertible_v<Range<const int>, Range<int>>,
+ "Range should not drop const in conversion");
+
+void test_RangeToBoolConversionShouldCompile() {
+ auto dummy = bool{Range<int>{}};
+ (void)dummy;
+}
+
+void test_RangeT_To_RangeConstT_ShouldCompile() {
+ auto dummy = Range<const int>{Range<int>{}};
+ (void)dummy;
+}
+
+// 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 0000000000..972e284c44
--- /dev/null
+++ b/mfbt/tests/TestRefPtr.cpp
@@ -0,0 +1,131 @@
+/* -*- 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"
+
+#include <type_traits>
+
+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 f1 = NewFoo();
+ static_assert(std::is_same_v<decltype(f1), RefPtr<Foo>>);
+ RefPtr f2(NewFoo());
+ static_assert(std::is_same_v<decltype(f2), RefPtr<Foo>>);
+ 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 f = GetNullFoo();
+ static_assert(std::is_same_v<decltype(f), RefPtr<Foo>>);
+ MOZ_RELEASE_ASSERT(11 == Foo::sNumDestroyed);
+ }
+ MOZ_RELEASE_ASSERT(11 == Foo::sNumDestroyed);
+
+ {
+ bool condition = true;
+ const auto f =
+ condition ? mozilla::MakeRefPtr<Bar>() : mozilla::MakeRefPtr<Foo>();
+
+ MOZ_RELEASE_ASSERT(f);
+ }
+
+ return 0;
+}
diff --git a/mfbt/tests/TestResult.cpp b/mfbt/tests/TestResult.cpp
new file mode 100644
index 0000000000..a2e10640c5
--- /dev/null
+++ b/mfbt/tests/TestResult.cpp
@@ -0,0 +1,870 @@
+/* -*- 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 <stdint.h>
+#include <string.h>
+#include "mozilla/ResultVariant.h"
+#include "mozilla/Try.h"
+#include "mozilla/UniquePtr.h"
+
+using mozilla::Err;
+using mozilla::GenericErrorResult;
+using mozilla::Ok;
+using mozilla::Result;
+using mozilla::UniquePtr;
+
+#define MOZ_STATIC_AND_RELEASE_ASSERT(expr) \
+ static_assert(expr); \
+ MOZ_RELEASE_ASSERT(expr)
+
+enum struct TestUnusedZeroEnum : int16_t { Ok = 0, NotOk = 1 };
+
+namespace mozilla::detail {
+template <>
+struct UnusedZero<TestUnusedZeroEnum> : UnusedZeroEnum<TestUnusedZeroEnum> {};
+} // namespace mozilla::detail
+
+struct Failed {};
+
+namespace mozilla::detail {
+template <>
+struct UnusedZero<Failed> {
+ using StorageType = uintptr_t;
+
+ static constexpr bool value = true;
+ static constexpr StorageType nullValue = 0;
+ static constexpr StorageType GetDefaultValue() { return 2; }
+
+ static constexpr void AssertValid(StorageType aValue) {}
+ static constexpr Failed Inspect(const StorageType& aValue) {
+ return Failed{};
+ }
+ static constexpr Failed Unwrap(StorageType aValue) { return Failed{}; }
+ static constexpr StorageType Store(Failed aValue) {
+ return GetDefaultValue();
+ }
+};
+
+} // namespace mozilla::detail
+
+// V is trivially default-constructible, and E has UnusedZero<E>::value == true,
+// for a reference type and for a non-reference type
+static_assert(mozilla::detail::SelectResultImpl<uintptr_t, Failed>::value ==
+ mozilla::detail::PackingStrategy::NullIsOk);
+static_assert(
+ mozilla::detail::SelectResultImpl<Ok, TestUnusedZeroEnum>::value ==
+ mozilla::detail::PackingStrategy::NullIsOk);
+static_assert(mozilla::detail::SelectResultImpl<Ok, Failed>::value ==
+ mozilla::detail::PackingStrategy::LowBitTagIsError);
+
+static_assert(std::is_trivially_destructible_v<Result<uintptr_t, Failed>>);
+static_assert(std::is_trivially_destructible_v<Result<Ok, TestUnusedZeroEnum>>);
+static_assert(std::is_trivially_destructible_v<Result<Ok, Failed>>);
+
+static_assert(
+ sizeof(Result<bool, TestUnusedZeroEnum>) <= sizeof(uintptr_t),
+ "Result with bool value type should not be larger than pointer-sized");
+static_assert(sizeof(Result<Ok, Failed>) == sizeof(uint8_t),
+ "Result with empty value type should be size 1");
+static_assert(sizeof(Result<int*, Failed>) == sizeof(uintptr_t),
+ "Result with two aligned pointer types should be pointer-sized");
+static_assert(
+ sizeof(Result<char*, Failed*>) > sizeof(char*),
+ "Result with unaligned success type `char*` must not be pointer-sized");
+static_assert(
+ sizeof(Result<int*, char*>) > sizeof(char*),
+ "Result with unaligned error type `char*` must not be pointer-sized");
+
+enum Foo8 : uint8_t {};
+enum Foo16 : uint16_t {};
+enum Foo32 : uint32_t {};
+static_assert(sizeof(Result<Ok, Foo8>) <= sizeof(uintptr_t),
+ "Result with small types should be pointer-sized");
+static_assert(sizeof(Result<Ok, Foo16>) <= sizeof(uintptr_t),
+ "Result with small types should be pointer-sized");
+static_assert(sizeof(Foo32) >= sizeof(uintptr_t) ||
+ sizeof(Result<Ok, Foo32>) <= sizeof(uintptr_t),
+ "Result with small types should be pointer-sized");
+
+static_assert(sizeof(Result<Foo16, Foo8>) <= sizeof(uintptr_t),
+ "Result with small types should be pointer-sized");
+static_assert(sizeof(Result<Foo8, Foo16>) <= sizeof(uintptr_t),
+ "Result with small types should be pointer-sized");
+static_assert(sizeof(Foo32) >= sizeof(uintptr_t) ||
+ sizeof(Result<Foo32, Foo16>) <= sizeof(uintptr_t),
+ "Result with small types should be pointer-sized");
+static_assert(sizeof(Foo32) >= sizeof(uintptr_t) ||
+ sizeof(Result<Foo16, Foo32>) <= sizeof(uintptr_t),
+ "Result with small types should be pointer-sized");
+
+#if __cplusplus < 202002L
+static_assert(std::is_literal_type_v<Result<int*, Failed>>);
+static_assert(std::is_literal_type_v<Result<Ok, Failed>>);
+static_assert(std::is_literal_type_v<Result<Ok, Foo8>>);
+static_assert(std::is_literal_type_v<Result<Foo8, Foo16>>);
+static_assert(!std::is_literal_type_v<Result<Ok, UniquePtr<int>>>);
+#endif
+
+static constexpr GenericErrorResult<Failed> Fail() { return Err(Failed{}); }
+
+static constexpr GenericErrorResult<TestUnusedZeroEnum>
+FailTestUnusedZeroEnum() {
+ return Err(TestUnusedZeroEnum::NotOk);
+}
+
+static constexpr Result<Ok, Failed> Task1(bool pass) {
+ if (!pass) {
+ return Fail(); // implicit conversion from GenericErrorResult to Result
+ }
+ return Ok();
+}
+
+static constexpr Result<Ok, TestUnusedZeroEnum> Task1UnusedZeroEnumErr(
+ bool pass) {
+ if (!pass) {
+ return FailTestUnusedZeroEnum(); // implicit conversion from
+ // GenericErrorResult to Result
+ }
+ return Ok();
+}
+
+static constexpr Result<int, Failed> Task2(bool pass, int value) {
+ MOZ_TRY(
+ Task1(pass)); // converts one type of result to another in the error case
+ return value; // implicit conversion from T to Result<T, E>
+}
+
+static constexpr Result<int, TestUnusedZeroEnum> Task2UnusedZeroEnumErr(
+ bool pass, int value) {
+ MOZ_TRY(Task1UnusedZeroEnumErr(
+ pass)); // converts one type of result to another in the error case
+ return value; // implicit conversion from T to Result<T, E>
+}
+
+static Result<int, Failed> Task3(bool pass1, bool pass2, int value) {
+ int x, y;
+ MOZ_TRY_VAR(x, Task2(pass1, value));
+ MOZ_TRY_VAR(y, Task2(pass2, value));
+ return x + y;
+}
+
+static void BasicTests() {
+ MOZ_STATIC_AND_RELEASE_ASSERT(Task1(true).isOk());
+ MOZ_STATIC_AND_RELEASE_ASSERT(!Task1(true).isErr());
+ MOZ_STATIC_AND_RELEASE_ASSERT(!Task1(false).isOk());
+ MOZ_STATIC_AND_RELEASE_ASSERT(Task1(false).isErr());
+
+ MOZ_STATIC_AND_RELEASE_ASSERT(Task1UnusedZeroEnumErr(true).isOk());
+ MOZ_STATIC_AND_RELEASE_ASSERT(!Task1UnusedZeroEnumErr(true).isErr());
+ MOZ_STATIC_AND_RELEASE_ASSERT(!Task1UnusedZeroEnumErr(false).isOk());
+ MOZ_STATIC_AND_RELEASE_ASSERT(Task1UnusedZeroEnumErr(false).isErr());
+ MOZ_STATIC_AND_RELEASE_ASSERT(TestUnusedZeroEnum::NotOk ==
+ Task1UnusedZeroEnumErr(false).inspectErr());
+ MOZ_STATIC_AND_RELEASE_ASSERT(TestUnusedZeroEnum::NotOk ==
+ Task1UnusedZeroEnumErr(false).unwrapErr());
+
+ // MOZ_TRY works.
+ MOZ_STATIC_AND_RELEASE_ASSERT(Task2(true, 3).isOk());
+ MOZ_STATIC_AND_RELEASE_ASSERT(Task2(true, 3).unwrap() == 3);
+ MOZ_STATIC_AND_RELEASE_ASSERT(Task2(true, 3).unwrapOr(6) == 3);
+ MOZ_RELEASE_ASSERT(Task2(false, 3).isErr());
+ MOZ_RELEASE_ASSERT(Task2(false, 3).unwrapOr(6) == 6);
+
+ MOZ_STATIC_AND_RELEASE_ASSERT(Task2UnusedZeroEnumErr(true, 3).isOk());
+ MOZ_STATIC_AND_RELEASE_ASSERT(Task2UnusedZeroEnumErr(true, 3).unwrap() == 3);
+ MOZ_STATIC_AND_RELEASE_ASSERT(Task2UnusedZeroEnumErr(true, 3).unwrapOr(6) ==
+ 3);
+ MOZ_STATIC_AND_RELEASE_ASSERT(Task2UnusedZeroEnumErr(false, 3).isErr());
+ MOZ_STATIC_AND_RELEASE_ASSERT(Task2UnusedZeroEnumErr(false, 3).unwrapOr(6) ==
+ 6);
+
+ // MOZ_TRY_VAR works.
+ MOZ_RELEASE_ASSERT(Task3(true, true, 3).isOk());
+ MOZ_RELEASE_ASSERT(Task3(true, true, 3).unwrap() == 6);
+ MOZ_RELEASE_ASSERT(Task3(true, false, 3).isErr());
+ MOZ_RELEASE_ASSERT(Task3(false, true, 3).isErr());
+ MOZ_RELEASE_ASSERT(Task3(false, true, 3).unwrapOr(6) == 6);
+
+ // Lvalues should work too.
+ {
+ constexpr Result<Ok, Failed> res1 = Task1(true);
+ MOZ_STATIC_AND_RELEASE_ASSERT(res1.isOk());
+ MOZ_STATIC_AND_RELEASE_ASSERT(!res1.isErr());
+
+ constexpr Result<Ok, Failed> res2 = Task1(false);
+ MOZ_STATIC_AND_RELEASE_ASSERT(!res2.isOk());
+ MOZ_STATIC_AND_RELEASE_ASSERT(res2.isErr());
+ }
+
+ {
+ Result<int, Failed> res = Task2(true, 3);
+ MOZ_RELEASE_ASSERT(res.isOk());
+ MOZ_RELEASE_ASSERT(res.unwrap() == 3);
+
+ res = Task2(false, 4);
+ MOZ_RELEASE_ASSERT(res.isErr());
+ }
+
+ // Some tests for pointer tagging.
+ {
+ int i = 123;
+
+ Result<int*, Failed> res = &i;
+ static_assert(sizeof(res) == sizeof(uintptr_t),
+ "should use pointer tagging to fit in a word");
+
+ MOZ_RELEASE_ASSERT(res.isOk());
+ MOZ_RELEASE_ASSERT(*res.unwrap() == 123);
+
+ res = Err(Failed());
+ MOZ_RELEASE_ASSERT(res.isErr());
+ }
+}
+
+struct NonCopyableNonMovable {
+ explicit constexpr NonCopyableNonMovable(uint32_t aValue) : mValue(aValue) {}
+
+ NonCopyableNonMovable(const NonCopyableNonMovable&) = delete;
+ NonCopyableNonMovable(NonCopyableNonMovable&&) = delete;
+ NonCopyableNonMovable& operator=(const NonCopyableNonMovable&) = delete;
+ NonCopyableNonMovable& operator=(NonCopyableNonMovable&&) = delete;
+
+ uint32_t mValue;
+};
+
+static void InPlaceConstructionTests() {
+ {
+ // PackingStrategy == NullIsOk
+ static_assert(mozilla::detail::SelectResultImpl<NonCopyableNonMovable,
+ Failed>::value ==
+ mozilla::detail::PackingStrategy::NullIsOk);
+ constexpr Result<NonCopyableNonMovable, Failed> result{std::in_place, 42u};
+ MOZ_STATIC_AND_RELEASE_ASSERT(42 == result.inspect().mValue);
+ }
+
+ {
+ // PackingStrategy == Variant
+ static_assert(
+ mozilla::detail::SelectResultImpl<NonCopyableNonMovable, int>::value ==
+ mozilla::detail::PackingStrategy::Variant);
+ const Result<NonCopyableNonMovable, int> result{std::in_place, 42};
+ MOZ_RELEASE_ASSERT(42 == result.inspect().mValue);
+ }
+}
+
+/* * */
+
+struct Snafu : Failed {};
+
+static Result<Ok, Snafu*> Explode() {
+ static Snafu snafu;
+ return Err(&snafu);
+}
+
+static Result<Ok, Failed*> ErrorGeneralization() {
+ MOZ_TRY(Explode()); // change error type from Snafu* to more general Failed*
+ return Ok();
+}
+
+static void TypeConversionTests() {
+ MOZ_RELEASE_ASSERT(ErrorGeneralization().isErr());
+
+ {
+ const Result<Ok, Failed*> res = Explode();
+ MOZ_RELEASE_ASSERT(res.isErr());
+ }
+
+ {
+ const Result<Ok, Failed*> res = Result<Ok, Snafu*>{Ok{}};
+ MOZ_RELEASE_ASSERT(res.isOk());
+ }
+}
+
+static void EmptyValueTest() {
+ struct Fine {};
+ mozilla::Result<Fine, Failed> res((Fine()));
+ res.unwrap();
+ MOZ_RELEASE_ASSERT(res.isOk());
+ static_assert(sizeof(res) == sizeof(uint8_t),
+ "Result with empty value and error types should be size 1");
+}
+
+static void MapTest() {
+ struct MyError {
+ int x;
+
+ explicit MyError(int y) : x(y) {}
+ };
+
+ // Mapping over success values, to the same success type.
+ {
+ Result<int, MyError> res(5);
+ bool invoked = false;
+ auto res2 = res.map([&invoked](int x) {
+ MOZ_RELEASE_ASSERT(x == 5);
+ invoked = true;
+ return 6;
+ });
+ MOZ_RELEASE_ASSERT(res2.isOk());
+ MOZ_RELEASE_ASSERT(invoked);
+ MOZ_RELEASE_ASSERT(res2.unwrap() == 6);
+ }
+
+ // Mapping over success values, to a different success type.
+ {
+ Result<int, MyError> res(5);
+ bool invoked = false;
+ auto res2 = res.map([&invoked](int x) {
+ MOZ_RELEASE_ASSERT(x == 5);
+ invoked = true;
+ return "hello";
+ });
+ MOZ_RELEASE_ASSERT(res2.isOk());
+ MOZ_RELEASE_ASSERT(invoked);
+ MOZ_RELEASE_ASSERT(strcmp(res2.unwrap(), "hello") == 0);
+ }
+
+ // Mapping over success values (constexpr).
+ {
+ constexpr uint64_t kValue = 42u;
+ constexpr auto res2a = Result<int32_t, Failed>{5}.map([](int32_t x) {
+ MOZ_RELEASE_ASSERT(x == 5);
+ return kValue;
+ });
+ MOZ_STATIC_AND_RELEASE_ASSERT(res2a.isOk());
+ MOZ_STATIC_AND_RELEASE_ASSERT(kValue == res2a.inspect());
+ }
+
+ // Mapping over error values.
+ {
+ MyError err(1);
+ Result<char, MyError> res(err);
+ MOZ_RELEASE_ASSERT(res.isErr());
+ Result<char, MyError> res2 = res.map([](int x) {
+ MOZ_RELEASE_ASSERT(false);
+ return 'a';
+ });
+ MOZ_RELEASE_ASSERT(res2.isErr());
+ MOZ_RELEASE_ASSERT(res2.unwrapErr().x == err.x);
+ }
+
+ // Function pointers instead of lambdas as the mapping function.
+ {
+ Result<const char*, MyError> res("hello");
+ auto res2 = res.map(strlen);
+ MOZ_RELEASE_ASSERT(res2.isOk());
+ MOZ_RELEASE_ASSERT(res2.unwrap() == 5);
+ }
+}
+
+static void MapErrTest() {
+ struct MyError {
+ int x;
+
+ explicit MyError(int y) : x(y) {}
+ };
+
+ struct MyError2 {
+ int a;
+
+ explicit MyError2(int b) : a(b) {}
+ };
+
+ // Mapping over error values, to the same error type.
+ {
+ MyError err(1);
+ Result<char, MyError> res(err);
+ MOZ_RELEASE_ASSERT(res.isErr());
+ bool invoked = false;
+ auto res2 = res.mapErr([&invoked](const auto err) {
+ MOZ_RELEASE_ASSERT(err.x == 1);
+ invoked = true;
+ return MyError(2);
+ });
+ MOZ_RELEASE_ASSERT(res2.isErr());
+ MOZ_RELEASE_ASSERT(invoked);
+ MOZ_RELEASE_ASSERT(res2.unwrapErr().x == 2);
+ }
+
+ // Mapping over error values, to a different error type.
+ {
+ MyError err(1);
+ Result<char, MyError> res(err);
+ MOZ_RELEASE_ASSERT(res.isErr());
+ bool invoked = false;
+ auto res2 = res.mapErr([&invoked](const auto err) {
+ MOZ_RELEASE_ASSERT(err.x == 1);
+ invoked = true;
+ return MyError2(2);
+ });
+ MOZ_RELEASE_ASSERT(res2.isErr());
+ MOZ_RELEASE_ASSERT(invoked);
+ MOZ_RELEASE_ASSERT(res2.unwrapErr().a == 2);
+ }
+
+ // Mapping over success values.
+ {
+ Result<int, MyError> res(5);
+ auto res2 = res.mapErr([](const auto err) {
+ MOZ_RELEASE_ASSERT(false);
+ return MyError(1);
+ });
+ MOZ_RELEASE_ASSERT(res2.isOk());
+ MOZ_RELEASE_ASSERT(res2.unwrap() == 5);
+ }
+
+ // Function pointers instead of lambdas as the mapping function.
+ {
+ Result<Ok, const char*> res("hello");
+ auto res2 = res.mapErr(strlen);
+ MOZ_RELEASE_ASSERT(res2.isErr());
+ MOZ_RELEASE_ASSERT(res2.unwrapErr() == 5);
+ }
+}
+
+static Result<Ok, size_t> strlen_ResultWrapper(const char* aValue) {
+ return Err(strlen(aValue));
+}
+
+static void OrElseTest() {
+ struct MyError {
+ int x;
+
+ explicit constexpr MyError(int y) : x(y) {}
+ };
+
+ struct MyError2 {
+ int a;
+
+ explicit constexpr MyError2(int b) : a(b) {}
+ };
+
+ // `orElse`ing over error values, to Result<V, E> (the same error type) error
+ // variant.
+ {
+ MyError err(1);
+ Result<char, MyError> res(err);
+ MOZ_RELEASE_ASSERT(res.isErr());
+ bool invoked = false;
+ auto res2 = res.orElse([&invoked](const auto err) -> Result<char, MyError> {
+ MOZ_RELEASE_ASSERT(err.x == 1);
+ invoked = true;
+ if (err.x != 42) {
+ return Err(MyError(2));
+ }
+ return 'a';
+ });
+ MOZ_RELEASE_ASSERT(res2.isErr());
+ MOZ_RELEASE_ASSERT(invoked);
+ MOZ_RELEASE_ASSERT(res2.unwrapErr().x == 2);
+ }
+
+ // `orElse`ing over error values, to Result<V, E> (the same error type)
+ // success variant.
+ {
+ MyError err(42);
+ Result<char, MyError> res(err);
+ MOZ_RELEASE_ASSERT(res.isErr());
+ bool invoked = false;
+ auto res2 = res.orElse([&invoked](const auto err) -> Result<char, MyError> {
+ MOZ_RELEASE_ASSERT(err.x == 42);
+ invoked = true;
+ if (err.x != 42) {
+ return Err(MyError(2));
+ }
+ return 'a';
+ });
+ MOZ_RELEASE_ASSERT(res2.isOk());
+ MOZ_RELEASE_ASSERT(invoked);
+ MOZ_RELEASE_ASSERT(res2.unwrap() == 'a');
+ }
+
+ // `orElse`ing over error values, to Result<V, E2> (a different error type)
+ // error variant.
+ {
+ MyError err(1);
+ Result<char, MyError> res(err);
+ MOZ_RELEASE_ASSERT(res.isErr());
+ bool invoked = false;
+ auto res2 =
+ res.orElse([&invoked](const auto err) -> Result<char, MyError2> {
+ MOZ_RELEASE_ASSERT(err.x == 1);
+ invoked = true;
+ if (err.x != 42) {
+ return Err(MyError2(2));
+ }
+ return 'a';
+ });
+ MOZ_RELEASE_ASSERT(res2.isErr());
+ MOZ_RELEASE_ASSERT(invoked);
+ MOZ_RELEASE_ASSERT(res2.unwrapErr().a == 2);
+ }
+
+ // `orElse`ing over error values, to Result<V, E2> (a different error type)
+ // success variant.
+ {
+ MyError err(42);
+ Result<char, MyError> res(err);
+ MOZ_RELEASE_ASSERT(res.isErr());
+ bool invoked = false;
+ auto res2 =
+ res.orElse([&invoked](const auto err) -> Result<char, MyError2> {
+ MOZ_RELEASE_ASSERT(err.x == 42);
+ invoked = true;
+ if (err.x != 42) {
+ return Err(MyError2(2));
+ }
+ return 'a';
+ });
+ MOZ_RELEASE_ASSERT(res2.isOk());
+ MOZ_RELEASE_ASSERT(invoked);
+ MOZ_RELEASE_ASSERT(res2.unwrap() == 'a');
+ }
+
+ // `orElse`ing over success values.
+ {
+ Result<int, MyError> res(5);
+ auto res2 = res.orElse([](const auto err) -> Result<int, MyError> {
+ MOZ_RELEASE_ASSERT(false);
+ return Err(MyError(1));
+ });
+ MOZ_RELEASE_ASSERT(res2.isOk());
+ MOZ_RELEASE_ASSERT(res2.unwrap() == 5);
+ }
+
+ // Function pointers instead of lambdas as the `orElse`ing function.
+ {
+ Result<Ok, const char*> res("hello");
+ auto res2 = res.orElse(strlen_ResultWrapper);
+ MOZ_RELEASE_ASSERT(res2.isErr());
+ MOZ_RELEASE_ASSERT(res2.unwrapErr() == 5);
+ }
+}
+
+static void AndThenTest() {
+ // `andThen`ing over success results.
+ {
+ Result<int, const char*> r1(10);
+ Result<int, const char*> r2 =
+ r1.andThen([](int x) { return Result<int, const char*>(x + 1); });
+ MOZ_RELEASE_ASSERT(r2.isOk());
+ MOZ_RELEASE_ASSERT(r2.unwrap() == 11);
+ }
+
+ // `andThen`ing over success results (constexpr).
+ {
+ constexpr Result<int, Failed> r2a = Result<int, Failed>{10}.andThen(
+ [](int x) { return Result<int, Failed>(x + 1); });
+ MOZ_STATIC_AND_RELEASE_ASSERT(r2a.isOk());
+ MOZ_STATIC_AND_RELEASE_ASSERT(r2a.inspect() == 11);
+ }
+
+ // `andThen`ing over error results.
+ {
+ Result<int, const char*> r3("error");
+ Result<int, const char*> r4 = r3.andThen([](int x) {
+ MOZ_RELEASE_ASSERT(false);
+ return Result<int, const char*>(1);
+ });
+ MOZ_RELEASE_ASSERT(r4.isErr());
+ MOZ_RELEASE_ASSERT(r3.unwrapErr() == r4.unwrapErr());
+ }
+
+ // andThen with a function accepting an rvalue
+ {
+ Result<int, const char*> r1(10);
+ Result<int, const char*> r2 =
+ r1.andThen([](int&& x) { return Result<int, const char*>(x + 1); });
+ MOZ_RELEASE_ASSERT(r2.isOk());
+ MOZ_RELEASE_ASSERT(r2.unwrap() == 11);
+ }
+
+ // `andThen`ing over error results (constexpr).
+ {
+ constexpr Result<int, Failed> r4a =
+ Result<int, Failed>{Failed{}}.andThen([](int x) {
+ MOZ_RELEASE_ASSERT(false);
+ return Result<int, Failed>(1);
+ });
+ MOZ_STATIC_AND_RELEASE_ASSERT(r4a.isErr());
+ }
+}
+
+using UniqueResult = Result<UniquePtr<int>, const char*>;
+
+static UniqueResult UniqueTask() { return mozilla::MakeUnique<int>(3); }
+static UniqueResult UniqueTaskError() { return Err("bad"); }
+
+using UniqueErrorResult = Result<int, UniquePtr<int>>;
+static UniqueErrorResult UniqueError() {
+ return Err(mozilla::MakeUnique<int>(4));
+}
+
+static Result<Ok, UniquePtr<int>> TryUniqueErrorResult() {
+ MOZ_TRY(UniqueError());
+ return Ok();
+}
+
+static void UniquePtrTest() {
+ {
+ auto result = UniqueTask();
+ MOZ_RELEASE_ASSERT(result.isOk());
+ auto ptr = result.unwrap();
+ MOZ_RELEASE_ASSERT(ptr);
+ MOZ_RELEASE_ASSERT(*ptr == 3);
+ auto moved = result.unwrap();
+ MOZ_RELEASE_ASSERT(!moved);
+ }
+
+ {
+ auto err = UniqueTaskError();
+ MOZ_RELEASE_ASSERT(err.isErr());
+ auto ptr = err.unwrapOr(mozilla::MakeUnique<int>(4));
+ MOZ_RELEASE_ASSERT(ptr);
+ MOZ_RELEASE_ASSERT(*ptr == 4);
+ }
+
+ {
+ auto result = UniqueTaskError();
+ result = UniqueResult(mozilla::MakeUnique<int>(6));
+ MOZ_RELEASE_ASSERT(result.isOk());
+ MOZ_RELEASE_ASSERT(result.inspect() && *result.inspect() == 6);
+ }
+
+ {
+ auto result = UniqueError();
+ MOZ_RELEASE_ASSERT(result.isErr());
+ MOZ_RELEASE_ASSERT(result.inspectErr());
+ MOZ_RELEASE_ASSERT(*result.inspectErr() == 4);
+ auto err = result.unwrapErr();
+ MOZ_RELEASE_ASSERT(!result.inspectErr());
+ MOZ_RELEASE_ASSERT(err);
+ MOZ_RELEASE_ASSERT(*err == 4);
+
+ result = UniqueErrorResult(0);
+ MOZ_RELEASE_ASSERT(result.isOk() && result.unwrap() == 0);
+ }
+
+ {
+ auto result = TryUniqueErrorResult();
+ MOZ_RELEASE_ASSERT(result.isErr());
+ auto err = result.unwrapErr();
+ MOZ_RELEASE_ASSERT(err && *err == 4);
+ MOZ_RELEASE_ASSERT(!result.inspectErr());
+ }
+}
+
+struct ZeroIsUnusedStructForPointer {
+ int x = 1;
+};
+enum class ZeroIsUnusedEnum1 : uint8_t {
+ V1 = 1,
+ V2 = 2,
+};
+enum class ZeroIsUnusedEnum2 : uint16_t {
+ V1 = 1,
+ V2 = 2,
+};
+enum class ZeroIsUnusedEnum4 : uint32_t {
+ V1 = 1,
+ V2 = 2,
+};
+enum class ZeroIsUnusedEnum8 : uint64_t {
+ V1 = 1,
+ V2 = 2,
+};
+struct EmptyErrorStruct {};
+
+template <>
+struct mozilla::detail::UnusedZero<ZeroIsUnusedStructForPointer*> {
+ static const bool value = true;
+};
+template <>
+struct mozilla::detail::UnusedZero<ZeroIsUnusedEnum1> {
+ static const bool value = true;
+};
+template <>
+struct mozilla::detail::UnusedZero<ZeroIsUnusedEnum2> {
+ static const bool value = true;
+};
+template <>
+struct mozilla::detail::UnusedZero<ZeroIsUnusedEnum4> {
+ static const bool value = true;
+};
+template <>
+struct mozilla::detail::UnusedZero<ZeroIsUnusedEnum8> {
+ static const bool value = true;
+};
+
+static void ZeroIsEmptyErrorTest() {
+ {
+ ZeroIsUnusedStructForPointer s;
+
+ using V = ZeroIsUnusedStructForPointer*;
+
+ mozilla::Result<V, EmptyErrorStruct> result(&s);
+ MOZ_RELEASE_ASSERT(sizeof(result) == sizeof(V));
+
+ MOZ_RELEASE_ASSERT(result.isOk());
+ MOZ_RELEASE_ASSERT(result.inspect() == &s);
+ }
+
+ {
+ using V = ZeroIsUnusedStructForPointer*;
+
+ mozilla::Result<V, EmptyErrorStruct> result(Err(EmptyErrorStruct{}));
+
+ MOZ_RELEASE_ASSERT(result.isErr());
+ MOZ_RELEASE_ASSERT(*reinterpret_cast<V*>(&result) == nullptr);
+ }
+
+ {
+ ZeroIsUnusedEnum1 e = ZeroIsUnusedEnum1::V1;
+
+ using V = ZeroIsUnusedEnum1;
+
+ mozilla::Result<V, EmptyErrorStruct> result(e);
+ MOZ_RELEASE_ASSERT(sizeof(result) == sizeof(V));
+
+ MOZ_RELEASE_ASSERT(result.isOk());
+ MOZ_RELEASE_ASSERT(result.inspect() == e);
+ }
+
+ {
+ using V = ZeroIsUnusedEnum1;
+
+ mozilla::Result<V, EmptyErrorStruct> result(Err(EmptyErrorStruct()));
+
+ MOZ_RELEASE_ASSERT(result.isErr());
+ MOZ_RELEASE_ASSERT(*reinterpret_cast<uint8_t*>(&result) == 0);
+ }
+
+ {
+ ZeroIsUnusedEnum2 e = ZeroIsUnusedEnum2::V1;
+
+ using V = ZeroIsUnusedEnum2;
+
+ mozilla::Result<V, EmptyErrorStruct> result(e);
+ MOZ_RELEASE_ASSERT(sizeof(result) == sizeof(V));
+
+ MOZ_RELEASE_ASSERT(result.isOk());
+ MOZ_RELEASE_ASSERT(result.inspect() == e);
+ }
+
+ {
+ using V = ZeroIsUnusedEnum2;
+
+ mozilla::Result<V, EmptyErrorStruct> result(Err(EmptyErrorStruct()));
+
+ MOZ_RELEASE_ASSERT(result.isErr());
+ MOZ_RELEASE_ASSERT(*reinterpret_cast<uint16_t*>(&result) == 0);
+ }
+
+ {
+ ZeroIsUnusedEnum4 e = ZeroIsUnusedEnum4::V1;
+
+ using V = ZeroIsUnusedEnum4;
+
+ mozilla::Result<V, EmptyErrorStruct> result(e);
+ MOZ_RELEASE_ASSERT(sizeof(result) == sizeof(V));
+
+ MOZ_RELEASE_ASSERT(result.isOk());
+ MOZ_RELEASE_ASSERT(result.inspect() == e);
+ }
+
+ {
+ using V = ZeroIsUnusedEnum4;
+
+ mozilla::Result<V, EmptyErrorStruct> result(Err(EmptyErrorStruct()));
+
+ MOZ_RELEASE_ASSERT(result.isErr());
+ MOZ_RELEASE_ASSERT(*reinterpret_cast<uint32_t*>(&result) == 0);
+ }
+
+ {
+ ZeroIsUnusedEnum8 e = ZeroIsUnusedEnum8::V1;
+
+ using V = ZeroIsUnusedEnum8;
+
+ mozilla::Result<V, EmptyErrorStruct> result(e);
+ MOZ_RELEASE_ASSERT(sizeof(result) == sizeof(V));
+
+ MOZ_RELEASE_ASSERT(result.isOk());
+ MOZ_RELEASE_ASSERT(result.inspect() == e);
+ }
+
+ {
+ using V = ZeroIsUnusedEnum8;
+
+ mozilla::Result<V, EmptyErrorStruct> result(Err(EmptyErrorStruct()));
+
+ MOZ_RELEASE_ASSERT(result.isErr());
+ MOZ_RELEASE_ASSERT(*reinterpret_cast<uint64_t*>(&result) == 0);
+ }
+}
+
+class Foo {};
+
+class C1 {};
+class C2 : public C1 {};
+
+class E1 {};
+class E2 : public E1 {};
+
+void UpcastTest() {
+ {
+ C2 c2;
+
+ mozilla::Result<C2*, Failed> result(&c2);
+ mozilla::Result<C1*, Failed> copied(std::move(result));
+
+ MOZ_RELEASE_ASSERT(copied.inspect() == &c2);
+ }
+
+ {
+ E2 e2;
+
+ mozilla::Result<Foo, E2*> result(Err(&e2));
+ mozilla::Result<Foo, E1*> copied(std::move(result));
+
+ MOZ_RELEASE_ASSERT(copied.inspectErr() == &e2);
+ }
+
+ {
+ C2 c2;
+
+ mozilla::Result<C2*, E2*> result(&c2);
+ mozilla::Result<C1*, E1*> copied(std::move(result));
+
+ MOZ_RELEASE_ASSERT(copied.inspect() == &c2);
+ }
+
+ {
+ E2 e2;
+
+ mozilla::Result<C2*, E2*> result(Err(&e2));
+ mozilla::Result<C1*, E1*> copied(std::move(result));
+
+ MOZ_RELEASE_ASSERT(copied.inspectErr() == &e2);
+ }
+}
+
+/* * */
+
+int main() {
+ BasicTests();
+ InPlaceConstructionTests();
+ TypeConversionTests();
+ EmptyValueTest();
+ MapTest();
+ MapErrTest();
+ OrElseTest();
+ AndThenTest();
+ UniquePtrTest();
+ ZeroIsEmptyErrorTest();
+ UpcastTest();
+ return 0;
+}
diff --git a/mfbt/tests/TestRollingMean.cpp b/mfbt/tests/TestRollingMean.cpp
new file mode 100644
index 0000000000..001d827c4f
--- /dev/null
+++ b/mfbt/tests/TestRollingMean.cpp
@@ -0,0 +1,114 @@
+/* -*- 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() = default;
+
+ 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 0000000000..9bc9d2a0b7
--- /dev/null
+++ b/mfbt/tests/TestSHA1.cpp
@@ -0,0 +1,204 @@
+/* -*- 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/TestSIMD.cpp b/mfbt/tests/TestSIMD.cpp
new file mode 100644
index 0000000000..23dc8b0117
--- /dev/null
+++ b/mfbt/tests/TestSIMD.cpp
@@ -0,0 +1,631 @@
+/* -*- 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/Assertions.h"
+#include "mozilla/SIMD.h"
+
+using mozilla::SIMD;
+
+void TestTinyString() {
+ const char* test = "012\n";
+
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '0', 3) == test + 0x0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '0', 3) == test + 0x0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '1', 3) == test + 0x1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '1', 3) == test + 0x1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '2', 3) == test + 0x2);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '2', 3) == test + 0x2);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '\n', 3) == nullptr);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '\n', 3) == nullptr);
+}
+
+void TestShortString() {
+ const char* test = "0123456789\n";
+
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '0', 10) == test + 0x0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '0', 10) == test + 0x0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '1', 10) == test + 0x1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '1', 10) == test + 0x1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '2', 10) == test + 0x2);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '2', 10) == test + 0x2);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '3', 10) == test + 0x3);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '3', 10) == test + 0x3);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '4', 10) == test + 0x4);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '4', 10) == test + 0x4);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '5', 10) == test + 0x5);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '5', 10) == test + 0x5);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '6', 10) == test + 0x6);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '6', 10) == test + 0x6);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '7', 10) == test + 0x7);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '7', 10) == test + 0x7);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '8', 10) == test + 0x8);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '8', 10) == test + 0x8);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '9', 10) == test + 0x9);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '9', 10) == test + 0x9);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '\n', 10) == nullptr);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '\n', 10) == nullptr);
+}
+
+void TestMediumString() {
+ const char* test = "0123456789abcdef\n";
+
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '0', 16) == test + 0x0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '0', 16) == test + 0x0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '1', 16) == test + 0x1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '1', 16) == test + 0x1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '2', 16) == test + 0x2);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '2', 16) == test + 0x2);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '3', 16) == test + 0x3);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '3', 16) == test + 0x3);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '4', 16) == test + 0x4);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '4', 16) == test + 0x4);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '5', 16) == test + 0x5);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '5', 16) == test + 0x5);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '6', 16) == test + 0x6);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '6', 16) == test + 0x6);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '7', 16) == test + 0x7);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '7', 16) == test + 0x7);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '8', 16) == test + 0x8);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '8', 16) == test + 0x8);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '9', 16) == test + 0x9);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '9', 16) == test + 0x9);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, 'a', 16) == test + 0xa);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, 'a', 16) == test + 0xa);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, 'b', 16) == test + 0xb);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, 'b', 16) == test + 0xb);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, 'c', 16) == test + 0xc);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, 'c', 16) == test + 0xc);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, 'd', 16) == test + 0xd);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, 'd', 16) == test + 0xd);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, 'e', 16) == test + 0xe);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, 'e', 16) == test + 0xe);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, 'f', 16) == test + 0xf);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, 'f', 16) == test + 0xf);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, '\n', 16) == nullptr);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test, '\n', 16) == nullptr);
+}
+
+void TestLongString() {
+ // NOTE: here we make sure we go all the way up to 256 to ensure we're
+ // handling negative-valued chars appropriately. We don't need to bother
+ // testing this side of things with char16_t's because they are very
+ // sensibly guaranteed to be unsigned.
+ const size_t count = 256;
+ char test[count];
+ for (size_t i = 0; i < count; ++i) {
+ test[i] = static_cast<char>(i);
+ }
+
+ for (size_t i = 0; i < count - 1; ++i) {
+ MOZ_RELEASE_ASSERT(SIMD::memchr8(test, static_cast<char>(i), count - 1) ==
+ test + i);
+ MOZ_RELEASE_ASSERT(
+ SIMD::memchr8SSE2(test, static_cast<char>(i), count - 1) == test + i);
+ }
+ MOZ_RELEASE_ASSERT(
+ SIMD::memchr8(test, static_cast<char>(count - 1), count - 1) == nullptr);
+}
+
+void TestGauntlet() {
+ const size_t count = 256;
+ char test[count];
+ for (size_t i = 0; i < count; ++i) {
+ test[i] = static_cast<char>(i);
+ }
+
+ for (size_t i = 0; i < count - 1; ++i) {
+ for (size_t j = 0; j < count - 1; ++j) {
+ for (size_t k = 0; k < count - 1; ++k) {
+ if (i >= k) {
+ const char* expected = nullptr;
+ if (j >= k && j < i) {
+ expected = test + j;
+ }
+ MOZ_RELEASE_ASSERT(
+ SIMD::memchr8(test + k, static_cast<char>(j), i - k) == expected);
+ MOZ_RELEASE_ASSERT(SIMD::memchr8SSE2(test + k, static_cast<char>(j),
+ i - k) == expected);
+ }
+ }
+ }
+ }
+}
+
+void TestTinyString16() {
+ const char16_t* test = u"012\n";
+
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'0', 3) == test + 0x0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'0', 3) == test + 0x0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'1', 3) == test + 0x1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'1', 3) == test + 0x1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'2', 3) == test + 0x2);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'2', 3) == test + 0x2);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'\n', 3) == nullptr);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'\n', 3) == nullptr);
+}
+
+void TestShortString16() {
+ const char16_t* test = u"0123456789\n";
+
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'0', 10) == test + 0x0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'0', 10) == test + 0x0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'1', 10) == test + 0x1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'1', 10) == test + 0x1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'2', 10) == test + 0x2);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'2', 10) == test + 0x2);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'3', 10) == test + 0x3);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'3', 10) == test + 0x3);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'4', 10) == test + 0x4);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'4', 10) == test + 0x4);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'5', 10) == test + 0x5);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'5', 10) == test + 0x5);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'6', 10) == test + 0x6);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'6', 10) == test + 0x6);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'7', 10) == test + 0x7);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'7', 10) == test + 0x7);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'8', 10) == test + 0x8);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'8', 10) == test + 0x8);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'9', 10) == test + 0x9);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'9', 10) == test + 0x9);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'\n', 10) == nullptr);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'\n', 10) == nullptr);
+}
+
+void TestMediumString16() {
+ const char16_t* test = u"0123456789abcdef\n";
+
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'0', 16) == test + 0x0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'0', 16) == test + 0x0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'1', 16) == test + 0x1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'1', 16) == test + 0x1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'2', 16) == test + 0x2);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'2', 16) == test + 0x2);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'3', 16) == test + 0x3);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'3', 16) == test + 0x3);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'4', 16) == test + 0x4);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'4', 16) == test + 0x4);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'5', 16) == test + 0x5);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'5', 16) == test + 0x5);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'6', 16) == test + 0x6);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'6', 16) == test + 0x6);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'7', 16) == test + 0x7);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'7', 16) == test + 0x7);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'8', 16) == test + 0x8);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'8', 16) == test + 0x8);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'9', 16) == test + 0x9);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'9', 16) == test + 0x9);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'a', 16) == test + 0xa);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'a', 16) == test + 0xa);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'b', 16) == test + 0xb);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'b', 16) == test + 0xb);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'c', 16) == test + 0xc);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'c', 16) == test + 0xc);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'd', 16) == test + 0xd);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'd', 16) == test + 0xd);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'e', 16) == test + 0xe);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'e', 16) == test + 0xe);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'f', 16) == test + 0xf);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'f', 16) == test + 0xf);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, u'\n', 16) == nullptr);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, u'\n', 16) == nullptr);
+}
+
+void TestLongString16() {
+ const size_t count = 256;
+ char16_t test[count];
+ for (size_t i = 0; i < count; ++i) {
+ test[i] = i;
+ }
+
+ for (size_t i = 0; i < count - 1; ++i) {
+ MOZ_RELEASE_ASSERT(
+ SIMD::memchr16(test, static_cast<char16_t>(i), count - 1) == test + i);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, static_cast<char16_t>(i),
+ count - 1) == test + i);
+ }
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test, count - 1, count - 1) == nullptr);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test, count - 1, count - 1) == nullptr);
+}
+
+void TestGauntlet16() {
+ const size_t count = 257;
+ char16_t test[count];
+ for (size_t i = 0; i < count; ++i) {
+ test[i] = i;
+ }
+
+ for (size_t i = 0; i < count - 1; ++i) {
+ for (size_t j = 0; j < count - 1; ++j) {
+ for (size_t k = 0; k < count - 1; ++k) {
+ if (i >= k) {
+ const char16_t* expected = nullptr;
+ if (j >= k && j < i) {
+ expected = test + j;
+ }
+ MOZ_RELEASE_ASSERT(SIMD::memchr16(test + k, static_cast<char16_t>(j),
+ i - k) == expected);
+ MOZ_RELEASE_ASSERT(SIMD::memchr16SSE2(test + k,
+ static_cast<char16_t>(j),
+ i - k) == expected);
+ }
+ }
+ }
+ }
+}
+
+void TestTinyString64() {
+ const uint64_t test[4] = {0, 1, 2, 3};
+
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 0, 3) == test + 0x0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 1, 3) == test + 0x1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 2, 3) == test + 0x2);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 3, 3) == nullptr);
+}
+
+void TestShortString64() {
+ const uint64_t test[16] = {0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15};
+
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 0, 15) == test + 0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 1, 15) == test + 1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 2, 15) == test + 2);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 3, 15) == test + 3);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 4, 15) == test + 4);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 5, 15) == test + 5);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 6, 15) == test + 6);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 7, 15) == test + 7);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 8, 15) == test + 8);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 9, 15) == test + 9);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 9, 15) == test + 9);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 10, 15) == test + 10);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 11, 15) == test + 11);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 12, 15) == test + 12);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 13, 15) == test + 13);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 14, 15) == test + 14);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 15, 15) == nullptr);
+}
+
+void TestMediumString64() {
+ const uint64_t test[32] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
+
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 0, 31) == test + 0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 1, 31) == test + 1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 2, 31) == test + 2);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 3, 31) == test + 3);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 4, 31) == test + 4);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 5, 31) == test + 5);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 6, 31) == test + 6);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 7, 31) == test + 7);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 8, 31) == test + 8);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 9, 31) == test + 9);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 9, 31) == test + 9);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 10, 31) == test + 10);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 11, 31) == test + 11);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 12, 31) == test + 12);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 13, 31) == test + 13);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 14, 31) == test + 14);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 15, 31) == test + 15);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 16, 31) == test + 16);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 17, 31) == test + 17);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 18, 31) == test + 18);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 19, 31) == test + 19);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 20, 31) == test + 20);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 21, 31) == test + 21);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 22, 31) == test + 22);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 23, 31) == test + 23);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 24, 31) == test + 24);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 25, 31) == test + 25);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 26, 31) == test + 26);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 27, 31) == test + 27);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 28, 31) == test + 28);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 29, 31) == test + 29);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 30, 31) == test + 30);
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, 31, 31) == nullptr);
+}
+
+void TestLongString64() {
+ const size_t count = 256;
+ uint64_t test[count];
+ for (size_t i = 0; i < count; ++i) {
+ test[i] = i;
+ }
+
+ for (uint64_t i = 0; i < count - 1; ++i) {
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, i, count - 1) == test + i);
+ }
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test, count - 1, count - 1) == nullptr);
+}
+
+void TestGauntlet64() {
+ const size_t count = 257;
+ uint64_t test[count];
+ for (size_t i = 0; i < count; ++i) {
+ test[i] = i;
+ }
+
+ for (uint64_t i = 0; i < count - 1; ++i) {
+ for (uint64_t j = 0; j < count - 1; ++j) {
+ for (uint64_t k = 0; k < count - 1; ++k) {
+ if (i >= k) {
+ const uint64_t* expected = nullptr;
+ if (j >= k && j < i) {
+ expected = test + j;
+ }
+ MOZ_RELEASE_ASSERT(SIMD::memchr64(test + k, j, i - k) == expected);
+ }
+ }
+ }
+ }
+}
+
+void TestTinyString2x8() {
+ const char* test = "012\n";
+
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '0', '1', 3) == test + 0x0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '1', '2', 3) == test + 0x1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '2', '\n', 3) == nullptr);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '0', '2', 3) == nullptr);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '1', '\n', 3) == nullptr);
+}
+
+void TestShortString2x8() {
+ const char* test = "0123456789\n";
+
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '0', '1', 10) == test + 0x0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '1', '2', 10) == test + 0x1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '2', '3', 10) == test + 0x2);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '3', '4', 10) == test + 0x3);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '4', '5', 10) == test + 0x4);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '5', '6', 10) == test + 0x5);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '6', '7', 10) == test + 0x6);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '7', '8', 10) == test + 0x7);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '8', '9', 10) == test + 0x8);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '9', '\n', 10) == nullptr);
+}
+
+void TestMediumString2x8() {
+ const char* test = "0123456789abcdef\n";
+
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '0', '1', 16) == test + 0x0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '1', '2', 16) == test + 0x1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '2', '3', 16) == test + 0x2);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '3', '4', 16) == test + 0x3);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '4', '5', 16) == test + 0x4);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '5', '6', 16) == test + 0x5);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '6', '7', 16) == test + 0x6);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '7', '8', 16) == test + 0x7);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '8', '9', 16) == test + 0x8);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, '9', 'a', 16) == test + 0x9);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, 'a', 'b', 16) == test + 0xa);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, 'b', 'c', 16) == test + 0xb);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, 'c', 'd', 16) == test + 0xc);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, 'd', 'e', 16) == test + 0xd);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, 'e', 'f', 16) == test + 0xe);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, 'f', '\n', 16) == nullptr);
+}
+
+void TestLongString2x8() {
+ const size_t count = 256;
+ char test[count];
+ for (size_t i = 0; i < count; ++i) {
+ test[i] = static_cast<char>(i);
+ }
+
+ for (size_t i = 0; i < count - 2; ++i) {
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, static_cast<char>(i),
+ static_cast<char>(i + 1),
+ count - 1) == test + i);
+ }
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test, static_cast<char>(count - 2),
+ static_cast<char>(count - 1),
+ count - 1) == nullptr);
+}
+
+void TestTinyString2x16() {
+ const char16_t* test = u"012\n";
+
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'0', u'1', 3) == test + 0x0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'1', u'2', 3) == test + 0x1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'2', u'\n', 3) == nullptr);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'0', u'2', 3) == nullptr);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'1', u'\n', 3) == nullptr);
+}
+
+void TestShortString2x16() {
+ const char16_t* test = u"0123456789\n";
+
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'0', u'1', 10) == test + 0x0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'1', u'2', 10) == test + 0x1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'2', u'3', 10) == test + 0x2);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'3', u'4', 10) == test + 0x3);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'4', u'5', 10) == test + 0x4);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'5', u'6', 10) == test + 0x5);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'6', u'7', 10) == test + 0x6);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'7', u'8', 10) == test + 0x7);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'8', u'9', 10) == test + 0x8);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'9', u'\n', 10) == nullptr);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'0', u'2', 10) == nullptr);
+}
+
+void TestMediumString2x16() {
+ const char16_t* test = u"0123456789abcdef\n";
+
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'0', u'1', 16) == test + 0x0);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'1', u'2', 16) == test + 0x1);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'2', u'3', 16) == test + 0x2);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'3', u'4', 16) == test + 0x3);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'4', u'5', 16) == test + 0x4);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'5', u'6', 16) == test + 0x5);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'6', u'7', 16) == test + 0x6);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'7', u'8', 16) == test + 0x7);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'8', u'9', 16) == test + 0x8);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'9', u'a', 16) == test + 0x9);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'a', u'b', 16) == test + 0xa);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'b', u'c', 16) == test + 0xb);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'c', u'd', 16) == test + 0xc);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'd', u'e', 16) == test + 0xd);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'e', u'f', 16) == test + 0xe);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'f', u'\n', 16) == nullptr);
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, u'0', u'2', 10) == nullptr);
+}
+
+void TestLongString2x16() {
+ const size_t count = 257;
+ char16_t test[count];
+ for (size_t i = 0; i < count; ++i) {
+ test[i] = static_cast<char16_t>(i);
+ }
+
+ for (size_t i = 0; i < count - 2; ++i) {
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, static_cast<char16_t>(i),
+ static_cast<char16_t>(i + 1),
+ count - 1) == test + i);
+ }
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test, static_cast<char16_t>(count - 2),
+ static_cast<char16_t>(count - 1),
+ count - 1) == nullptr);
+}
+
+void TestGauntlet2x8() {
+ const size_t count = 256;
+ char test[count * 2];
+ // load in the evens
+ for (size_t i = 0; i < count / 2; ++i) {
+ test[i] = static_cast<char>(2 * i);
+ }
+ // load in the odds
+ for (size_t i = 0; i < count / 2; ++i) {
+ test[count / 2 + i] = static_cast<char>(2 * i + 1);
+ }
+ // load in evens and odds sequentially
+ for (size_t i = 0; i < count; ++i) {
+ test[count + i] = static_cast<char>(i);
+ }
+
+ for (size_t i = 0; i < count - 1; ++i) {
+ for (size_t j = 0; j < count - 2; ++j) {
+ for (size_t k = 0; k < count - 1; ++k) {
+ if (i > k + 1) {
+ const char* expected1 = nullptr;
+ const char* expected2 = nullptr;
+ if (i > j + 1) {
+ expected1 = test + j + count; // Add count to skip over odds/evens
+ if (j >= k) {
+ expected2 = test + j + count;
+ }
+ }
+ char a = static_cast<char>(j);
+ char b = static_cast<char>(j + 1);
+ // Make sure it doesn't pick up any in the alternating odd/even
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test + k, a, b, i - k + count) ==
+ expected1);
+ // Make sure we cover smaller inputs
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test + k + count, a, b, i - k) ==
+ expected2);
+ }
+ }
+ }
+ }
+}
+
+void TestGauntlet2x16() {
+ const size_t count = 1024;
+ char16_t test[count * 2];
+ // load in the evens
+ for (size_t i = 0; i < count / 2; ++i) {
+ test[i] = static_cast<char16_t>(2 * i);
+ }
+ // load in the odds
+ for (size_t i = 0; i < count / 2; ++i) {
+ test[count / 2 + i] = static_cast<char16_t>(2 * i + 1);
+ }
+ // load in evens and odds sequentially
+ for (size_t i = 0; i < count; ++i) {
+ test[count + i] = static_cast<char16_t>(i);
+ }
+
+ for (size_t i = 0; i < count - 1; ++i) {
+ for (size_t j = 0; j < count - 2; ++j) {
+ for (size_t k = 0; k < count - 1; ++k) {
+ if (i > k + 1) {
+ const char16_t* expected1 = nullptr;
+ const char16_t* expected2 = nullptr;
+ if (i > j + 1) {
+ expected1 = test + j + count; // Add count to skip over odds/evens
+ if (j >= k) {
+ expected2 = test + j + count;
+ }
+ }
+ char16_t a = static_cast<char16_t>(j);
+ char16_t b = static_cast<char16_t>(j + 1);
+ // Make sure it doesn't pick up any in the alternating odd/even
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test + k, a, b, i - k + count) ==
+ expected1);
+ // Make sure we cover smaller inputs
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test + k + count, a, b, i - k) ==
+ expected2);
+ }
+ }
+ }
+ }
+}
+
+void TestSpecialCases() {
+ // The following 4 asserts test the case where we do two overlapping checks,
+ // where the first one ends with our first search character, and the second
+ // one begins with our search character. Since they are overlapping, we want
+ // to ensure that the search function doesn't carry the match from the
+ // first check over to the second check.
+ const char* test1 = "x123456789abcdey";
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test1, 'y', 'x', 16) == nullptr);
+ const char* test2 = "1000000000000000200000000000000030b000000000000a40";
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x8(test2, 'a', 'b', 50) == nullptr);
+ const char16_t* test1wide = u"x123456y";
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test1wide, 'y', 'x', 8) == nullptr);
+ const char16_t* test2wide = u"100000002000000030b0000a40";
+ MOZ_RELEASE_ASSERT(SIMD::memchr2x16(test2wide, 'a', 'b', 26) == nullptr);
+}
+
+int main(void) {
+ TestTinyString();
+ TestShortString();
+ TestMediumString();
+ TestLongString();
+ TestGauntlet();
+
+ TestTinyString16();
+ TestShortString16();
+ TestMediumString16();
+ TestLongString16();
+ TestGauntlet16();
+
+ TestTinyString64();
+ TestShortString64();
+ TestMediumString64();
+ TestLongString64();
+ TestGauntlet64();
+
+ TestTinyString2x8();
+ TestShortString2x8();
+ TestMediumString2x8();
+ TestLongString2x8();
+
+ TestTinyString2x16();
+ TestShortString2x16();
+ TestMediumString2x16();
+ TestLongString2x16();
+
+ TestSpecialCases();
+
+ // These are too slow to run all the time, but they should be run when making
+ // meaningful changes just to be sure.
+ // TestGauntlet2x8();
+ // TestGauntlet2x16();
+
+ return 0;
+}
diff --git a/mfbt/tests/TestSPSCQueue.cpp b/mfbt/tests/TestSPSCQueue.cpp
new file mode 100644
index 0000000000..e54d911b85
--- /dev/null
+++ b/mfbt/tests/TestSPSCQueue.cpp
@@ -0,0 +1,302 @@
+/* -*- 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/SPSCQueue.h"
+#include "mozilla/PodOperations.h"
+#include <vector>
+#include <iostream>
+#include <thread>
+#include <chrono>
+#include <memory>
+#include <string>
+
+#ifdef _WIN32
+# include <windows.h>
+#endif
+
+using namespace mozilla;
+
+/* Generate a monotonically increasing sequence of numbers. */
+template <typename T>
+class SequenceGenerator {
+ public:
+ SequenceGenerator() = default;
+ void Get(T* aElements, size_t aCount) {
+ for (size_t i = 0; i < aCount; i++) {
+ aElements[i] = static_cast<T>(mIndex);
+ mIndex++;
+ }
+ }
+ void Rewind(size_t aCount) { mIndex -= aCount; }
+
+ private:
+ size_t mIndex = 0;
+};
+
+/* Checks that a sequence is monotonically increasing. */
+template <typename T>
+class SequenceVerifier {
+ public:
+ SequenceVerifier() = default;
+ void Check(T* aElements, size_t aCount) {
+ for (size_t i = 0; i < aCount; i++) {
+ if (aElements[i] != static_cast<T>(mIndex)) {
+ std::cerr << "Element " << i << " is different. Expected "
+ << static_cast<T>(mIndex) << ", got " << aElements[i] << "."
+ << std::endl;
+ MOZ_RELEASE_ASSERT(false);
+ }
+ mIndex++;
+ }
+ }
+
+ private:
+ size_t mIndex = 0;
+};
+
+const int BLOCK_SIZE = 127;
+
+template <typename T>
+void TestRing(int capacity) {
+ SPSCQueue<T> buf(capacity);
+ std::unique_ptr<T[]> seq(new T[capacity]);
+ SequenceGenerator<T> gen;
+ SequenceVerifier<T> checker;
+
+ int iterations = 1002;
+
+ while (iterations--) {
+ gen.Get(seq.get(), BLOCK_SIZE);
+ int rv = buf.Enqueue(seq.get(), BLOCK_SIZE);
+ MOZ_RELEASE_ASSERT(rv == BLOCK_SIZE);
+ PodZero(seq.get(), BLOCK_SIZE);
+ rv = buf.Dequeue(seq.get(), BLOCK_SIZE);
+ MOZ_RELEASE_ASSERT(rv == BLOCK_SIZE);
+ checker.Check(seq.get(), BLOCK_SIZE);
+ }
+}
+
+void Delay() {
+ // On Windows and x86 Android, the timer resolution is so bad that, even if
+ // we used `timeBeginPeriod(1)`, any nonzero sleep from the test's inner loops
+ // would make this program take far too long.
+#ifdef _WIN32
+ Sleep(0);
+#elif defined(ANDROID)
+ std::this_thread::sleep_for(std::chrono::microseconds(0));
+#else
+ std::this_thread::sleep_for(std::chrono::microseconds(10));
+#endif
+}
+
+template <typename T>
+void TestRingMultiThread(int capacity) {
+ SPSCQueue<T> buf(capacity);
+ SequenceVerifier<T> checker;
+ std::unique_ptr<T[]> outBuffer(new T[capacity]);
+
+ std::thread t([&buf, capacity] {
+ int iterations = 1002;
+ std::unique_ptr<T[]> inBuffer(new T[capacity]);
+ SequenceGenerator<T> gen;
+
+ while (iterations--) {
+ Delay();
+ gen.Get(inBuffer.get(), BLOCK_SIZE);
+ int rv = buf.Enqueue(inBuffer.get(), BLOCK_SIZE);
+ MOZ_RELEASE_ASSERT(rv <= BLOCK_SIZE);
+ if (rv != BLOCK_SIZE) {
+ gen.Rewind(BLOCK_SIZE - rv);
+ }
+ }
+ });
+
+ int remaining = 1002;
+
+ while (remaining--) {
+ Delay();
+ int rv = buf.Dequeue(outBuffer.get(), BLOCK_SIZE);
+ MOZ_RELEASE_ASSERT(rv <= BLOCK_SIZE);
+ checker.Check(outBuffer.get(), rv);
+ }
+
+ t.join();
+}
+
+template <typename T>
+void BasicAPITest(T& ring) {
+ MOZ_RELEASE_ASSERT(ring.Capacity() == 128);
+
+ MOZ_RELEASE_ASSERT(ring.AvailableRead() == 0);
+ MOZ_RELEASE_ASSERT(ring.AvailableWrite() == 128);
+
+ int rv = ring.EnqueueDefault(63);
+
+ MOZ_RELEASE_ASSERT(rv == 63);
+ MOZ_RELEASE_ASSERT(ring.AvailableRead() == 63);
+ MOZ_RELEASE_ASSERT(ring.AvailableWrite() == 65);
+
+ rv = ring.EnqueueDefault(65);
+
+ MOZ_RELEASE_ASSERT(rv == 65);
+ MOZ_RELEASE_ASSERT(ring.AvailableRead() == 128);
+ MOZ_RELEASE_ASSERT(ring.AvailableWrite() == 0);
+
+ rv = ring.Dequeue(nullptr, 63);
+
+ MOZ_RELEASE_ASSERT(ring.AvailableRead() == 65);
+ MOZ_RELEASE_ASSERT(ring.AvailableWrite() == 63);
+
+ rv = ring.Dequeue(nullptr, 65);
+
+ MOZ_RELEASE_ASSERT(ring.AvailableRead() == 0);
+ MOZ_RELEASE_ASSERT(ring.AvailableWrite() == 128);
+}
+
+const size_t RING_BUFFER_SIZE = 128;
+const size_t ENQUEUE_SIZE = RING_BUFFER_SIZE / 2;
+
+void TestResetAPI() {
+ SPSCQueue<float> ring(RING_BUFFER_SIZE);
+ std::thread p([&ring] {
+ std::unique_ptr<float[]> inBuffer(new float[ENQUEUE_SIZE]);
+ int rv = ring.Enqueue(inBuffer.get(), ENQUEUE_SIZE);
+ MOZ_RELEASE_ASSERT(rv > 0);
+ });
+
+ p.join();
+
+ std::thread c([&ring] {
+ std::unique_ptr<float[]> outBuffer(new float[ENQUEUE_SIZE]);
+ int rv = ring.Dequeue(outBuffer.get(), ENQUEUE_SIZE);
+ MOZ_RELEASE_ASSERT(rv > 0);
+ });
+
+ c.join();
+
+ // Enqueue with a different thread. We reset the thread ID in the ring buffer,
+ // this should work.
+ std::thread p2([&ring] {
+ ring.ResetProducerThreadId();
+ std::unique_ptr<float[]> inBuffer(new float[ENQUEUE_SIZE]);
+ int rv = ring.Enqueue(inBuffer.get(), ENQUEUE_SIZE);
+ MOZ_RELEASE_ASSERT(rv > 0);
+ });
+
+ p2.join();
+
+ // Dequeue with a different thread. We reset the thread ID in the ring buffer,
+ // this should work.
+ std::thread c2([&ring] {
+ ring.ResetConsumerThreadId();
+ std::unique_ptr<float[]> outBuffer(new float[ENQUEUE_SIZE]);
+ int rv = ring.Dequeue(outBuffer.get(), ENQUEUE_SIZE);
+ MOZ_RELEASE_ASSERT(rv > 0);
+ });
+
+ c2.join();
+
+ // Similarly, but do the Enqueues without a Dequeue in between, since a
+ // Dequeue could affect memory ordering.
+ std::thread p4;
+ std::thread p3([&] {
+ ring.ResetProducerThreadId();
+ std::unique_ptr<float[]> inBuffer(new float[ENQUEUE_SIZE]);
+ int rv = ring.Enqueue(inBuffer.get(), ENQUEUE_SIZE);
+ MOZ_RELEASE_ASSERT(rv > 0);
+ p4 = std::thread([&ring] {
+ ring.ResetProducerThreadId();
+ std::unique_ptr<float[]> inBuffer(new float[ENQUEUE_SIZE]);
+ int rv = ring.Enqueue(inBuffer.get(), ENQUEUE_SIZE);
+ MOZ_RELEASE_ASSERT(rv > 0);
+ });
+ });
+
+ p3.join();
+ p4.join();
+
+ std::thread c4;
+ std::thread c3([&] {
+ ring.ResetConsumerThreadId();
+ std::unique_ptr<float[]> outBuffer(new float[ENQUEUE_SIZE]);
+ int rv = ring.Dequeue(outBuffer.get(), ENQUEUE_SIZE);
+ MOZ_RELEASE_ASSERT(rv > 0);
+ c4 = std::thread([&ring] {
+ ring.ResetConsumerThreadId();
+ std::unique_ptr<float[]> outBuffer(new float[ENQUEUE_SIZE]);
+ int rv = ring.Dequeue(outBuffer.get(), ENQUEUE_SIZE);
+ MOZ_RELEASE_ASSERT(rv > 0);
+ });
+ });
+
+ c3.join();
+ c4.join();
+}
+
+void TestMove() {
+ const size_t ELEMENT_COUNT = 16;
+ struct Thing {
+ Thing() : mStr("") {}
+ explicit Thing(const std::string& aStr) : mStr(aStr) {}
+ Thing(Thing&& aOtherThing) {
+ mStr = std::move(aOtherThing.mStr);
+ // aOtherThing.mStr.clear();
+ }
+ Thing& operator=(Thing&& aOtherThing) {
+ mStr = std::move(aOtherThing.mStr);
+ return *this;
+ }
+ std::string mStr;
+ };
+
+ std::vector<Thing> vec_in;
+ std::vector<Thing> vec_out;
+
+ for (uint32_t i = 0; i < ELEMENT_COUNT; i++) {
+ vec_in.push_back(Thing(std::to_string(i)));
+ vec_out.push_back(Thing());
+ }
+
+ SPSCQueue<Thing> queue(ELEMENT_COUNT);
+
+ int rv = queue.Enqueue(&vec_in[0], ELEMENT_COUNT);
+ MOZ_RELEASE_ASSERT(rv == ELEMENT_COUNT);
+
+ // Check that we've moved the std::string into the queue.
+ for (uint32_t i = 0; i < ELEMENT_COUNT; i++) {
+ MOZ_RELEASE_ASSERT(vec_in[i].mStr.empty());
+ }
+
+ rv = queue.Dequeue(&vec_out[0], ELEMENT_COUNT);
+ MOZ_RELEASE_ASSERT(rv == ELEMENT_COUNT);
+
+ for (uint32_t i = 0; i < ELEMENT_COUNT; i++) {
+ MOZ_RELEASE_ASSERT(std::stoul(vec_out[i].mStr) == i);
+ }
+}
+
+int main() {
+ const int minCapacity = 199;
+ const int maxCapacity = 1277;
+ const int capacityIncrement = 27;
+
+ SPSCQueue<float> q1(128);
+ BasicAPITest(q1);
+ SPSCQueue<char> q2(128);
+ BasicAPITest(q2);
+
+ for (uint32_t i = minCapacity; i < maxCapacity; i += capacityIncrement) {
+ TestRing<uint32_t>(i);
+ TestRingMultiThread<uint32_t>(i);
+ TestRing<float>(i);
+ TestRingMultiThread<float>(i);
+ }
+
+ TestResetAPI();
+ TestMove();
+
+ return 0;
+}
diff --git a/mfbt/tests/TestSaturate.cpp b/mfbt/tests/TestSaturate.cpp
new file mode 100644
index 0000000000..500c9eed7f
--- /dev/null
+++ b/mfbt/tests/TestSaturate.cpp
@@ -0,0 +1,181 @@
+/* -*- 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 0000000000..1c5eef68c2
--- /dev/null
+++ b/mfbt/tests/TestScopeExit.cpp
@@ -0,0 +1,55 @@
+/* -*- 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;
+ int c = 1;
+
+ {
+ a++;
+ auto guardA = MakeScopeExit([&] { a--; });
+
+ b++;
+ auto guardB = MakeScopeExit([&] { b--; });
+
+ guardB.release();
+
+ c++;
+ auto guardC = MakeScopeExit([&] { c--; });
+
+ { auto guardC_ = std::move(guardC); }
+
+ CHECK(c == 1);
+ }
+
+ CHECK(a == 1);
+ CHECK(b == 2);
+ CHECK(c == 1);
+
+ 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 0000000000..dd569ea7b6
--- /dev/null
+++ b/mfbt/tests/TestSegmentedVector.cpp
@@ -0,0 +1,388 @@
+/* -*- 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;
+ }
+
+ template <typename T>
+ void free_(T* aPtr, size_t aNumElems = 0) {
+ free(aPtr);
+ }
+};
+
+template <typename Vector>
+void CheckContents(Vector& vector, size_t expectedLength) {
+ MOZ_RELEASE_ASSERT(vector.Length() == expectedLength);
+ size_t n = 0;
+ for (auto iter = vector.Iter(); !iter.Done(); iter.Next()) {
+ MOZ_RELEASE_ASSERT(iter.Get() == int(n));
+ n++;
+ }
+ MOZ_RELEASE_ASSERT(n == expectedLength);
+}
+
+// We want to test Append(), which is fallible and marked with
+// [[nodiscard]]. 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;
+
+ MOZ_RELEASE_ASSERT(v.IsEmpty());
+
+ // Add 100 elements, then check various things.
+ i = 0;
+ for (; i < 100; i++) {
+ gDummy = v.Append(std::move(i));
+ }
+ MOZ_RELEASE_ASSERT(!v.IsEmpty());
+ CheckContents(v, 100);
+
+ // Add another 900 elements, then re-check.
+ for (; i < 1000; i++) {
+ v.InfallibleAppend(std::move(i));
+ }
+ MOZ_RELEASE_ASSERT(!v.IsEmpty());
+ CheckContents(v, 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(std::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(std::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.
+ CheckContents(v, 700);
+}
+
+void TestMoveAndSwap() {
+ typedef SegmentedVector<int, 32, InfallibleAllocPolicy> MyVector;
+ MyVector v;
+
+ for (int i = 0; i < 100; i++) {
+ (void)v.Append(i);
+ }
+ MOZ_RELEASE_ASSERT(!v.IsEmpty());
+ CheckContents(v, 100);
+
+ // Test move constructor.
+ MyVector w(std::move(v));
+ CheckContents(w, 100);
+ MOZ_RELEASE_ASSERT(v.IsEmpty());
+
+ // Test move assignment.
+ v = std::move(w);
+ CheckContents(v, 100);
+ MOZ_RELEASE_ASSERT(w.IsEmpty());
+
+ // Test swap.
+ std::swap(v, w);
+ CheckContents(w, 100);
+ MOZ_RELEASE_ASSERT(v.IsEmpty());
+}
+
+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(std::move(y)); // move constructor called
+ moveCtorCalls++;
+ NonPOD z(1); // explicit constructor called
+ explicitCtorCalls++;
+ v.InfallibleAppend(std::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);
+}
+
+void TestIterator() {
+ SegmentedVector<int, 4> v;
+
+ auto iter = v.Iter();
+ auto iterFromLast = v.IterFromLast();
+ MOZ_RELEASE_ASSERT(iter.Done());
+ MOZ_RELEASE_ASSERT(iterFromLast.Done());
+
+ gDummy = v.Append(1);
+ iter = v.Iter();
+ iterFromLast = v.IterFromLast();
+ MOZ_RELEASE_ASSERT(!iter.Done());
+ MOZ_RELEASE_ASSERT(!iterFromLast.Done());
+
+ iter.Next();
+ MOZ_RELEASE_ASSERT(iter.Done());
+ iterFromLast.Next();
+ MOZ_RELEASE_ASSERT(iterFromLast.Done());
+
+ iter = v.Iter();
+ iterFromLast = v.IterFromLast();
+ MOZ_RELEASE_ASSERT(!iter.Done());
+ MOZ_RELEASE_ASSERT(!iterFromLast.Done());
+
+ iter.Prev();
+ MOZ_RELEASE_ASSERT(iter.Done());
+ iterFromLast.Prev();
+ MOZ_RELEASE_ASSERT(iterFromLast.Done());
+
+ // Append enough entries to ensure we have at least two segments.
+ gDummy = v.Append(1);
+ gDummy = v.Append(1);
+ gDummy = v.Append(1);
+ gDummy = v.Append(1);
+
+ iter = v.Iter();
+ iterFromLast = v.IterFromLast();
+ MOZ_RELEASE_ASSERT(!iter.Done());
+ MOZ_RELEASE_ASSERT(!iterFromLast.Done());
+
+ iter.Prev();
+ MOZ_RELEASE_ASSERT(iter.Done());
+ iterFromLast.Next();
+ MOZ_RELEASE_ASSERT(iterFromLast.Done());
+
+ iter = v.Iter();
+ iterFromLast = v.IterFromLast();
+ MOZ_RELEASE_ASSERT(!iter.Done());
+ MOZ_RELEASE_ASSERT(!iterFromLast.Done());
+
+ iter.Next();
+ MOZ_RELEASE_ASSERT(!iter.Done());
+ iterFromLast.Prev();
+ MOZ_RELEASE_ASSERT(!iterFromLast.Done());
+
+ iter = v.Iter();
+ iterFromLast = v.IterFromLast();
+ int count = 0;
+ for (; !iter.Done() && !iterFromLast.Done();
+ iter.Next(), iterFromLast.Prev()) {
+ ++count;
+ }
+ MOZ_RELEASE_ASSERT(count == 5);
+
+ // Modify the vector while using the iterator.
+ iterFromLast = v.IterFromLast();
+ gDummy = v.Append(2);
+ gDummy = v.Append(3);
+ gDummy = v.Append(4);
+ iterFromLast.Next();
+ MOZ_RELEASE_ASSERT(!iterFromLast.Done());
+ MOZ_RELEASE_ASSERT(iterFromLast.Get() == 2);
+ iterFromLast.Next();
+ MOZ_RELEASE_ASSERT(iterFromLast.Get() == 3);
+ iterFromLast.Next();
+ MOZ_RELEASE_ASSERT(iterFromLast.Get() == 4);
+ iterFromLast.Next();
+ MOZ_RELEASE_ASSERT(iterFromLast.Done());
+}
+
+int main(void) {
+ TestBasics();
+ TestMoveAndSwap();
+ TestConstructorsAndDestructors();
+ TestSegmentCapacitiesAndAlignments();
+ TestIterator();
+
+ return 0;
+}
diff --git a/mfbt/tests/TestSmallPointerArray.cpp b/mfbt/tests/TestSmallPointerArray.cpp
new file mode 100644
index 0000000000..163b2b1df8
--- /dev/null
+++ b/mfbt/tests/TestSmallPointerArray.cpp
@@ -0,0 +1,237 @@
+/* -*- 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/SmallPointerArray.h"
+
+#define PTR1 (void*)0x4
+#define PTR2 (void*)0x5
+#define PTR3 (void*)0x6
+
+// We explicitly test sizes up to 3 here, as that is when SmallPointerArray<>
+// switches to the storage method used for larger arrays.
+void TestArrayManipulation() {
+ using namespace mozilla;
+ SmallPointerArray<void> testArray;
+
+ MOZ_RELEASE_ASSERT(testArray.Length() == 0);
+ MOZ_RELEASE_ASSERT(sizeof(testArray) == 2 * sizeof(void*));
+ MOZ_RELEASE_ASSERT(!testArray.Contains(PTR1));
+
+ testArray.AppendElement(PTR1);
+
+ MOZ_RELEASE_ASSERT(testArray.Length() == 1);
+ MOZ_RELEASE_ASSERT(testArray[0] == PTR1);
+ MOZ_RELEASE_ASSERT(testArray.ElementAt(0) == PTR1);
+ MOZ_RELEASE_ASSERT(testArray.Contains(PTR1));
+
+ testArray.AppendElement(PTR2);
+
+ MOZ_RELEASE_ASSERT(testArray.Length() == 2);
+ MOZ_RELEASE_ASSERT(testArray[0] == PTR1);
+ MOZ_RELEASE_ASSERT(testArray.ElementAt(0) == PTR1);
+ MOZ_RELEASE_ASSERT(testArray[1] == PTR2);
+ MOZ_RELEASE_ASSERT(testArray.ElementAt(1) == PTR2);
+ MOZ_RELEASE_ASSERT(testArray.Contains(PTR2));
+
+ MOZ_RELEASE_ASSERT(testArray.RemoveElement(PTR1));
+ MOZ_RELEASE_ASSERT(!testArray.RemoveElement(PTR1));
+
+ MOZ_RELEASE_ASSERT(testArray.Length() == 1);
+ MOZ_RELEASE_ASSERT(testArray[0] == PTR2);
+ MOZ_RELEASE_ASSERT(testArray.ElementAt(0) == PTR2);
+ MOZ_RELEASE_ASSERT(!testArray.Contains(PTR1));
+
+ testArray.AppendElement(PTR1);
+
+ MOZ_RELEASE_ASSERT(testArray.Length() == 2);
+ MOZ_RELEASE_ASSERT(testArray[0] == PTR2);
+ MOZ_RELEASE_ASSERT(testArray.ElementAt(0) == PTR2);
+ MOZ_RELEASE_ASSERT(testArray[1] == PTR1);
+ MOZ_RELEASE_ASSERT(testArray.ElementAt(1) == PTR1);
+ MOZ_RELEASE_ASSERT(testArray.Contains(PTR1));
+
+ testArray.AppendElement(PTR3);
+
+ MOZ_RELEASE_ASSERT(testArray.Length() == 3);
+ MOZ_RELEASE_ASSERT(testArray[0] == PTR2);
+ MOZ_RELEASE_ASSERT(testArray.ElementAt(0) == PTR2);
+ MOZ_RELEASE_ASSERT(testArray[1] == PTR1);
+ MOZ_RELEASE_ASSERT(testArray.ElementAt(1) == PTR1);
+ MOZ_RELEASE_ASSERT(testArray[2] == PTR3);
+ MOZ_RELEASE_ASSERT(testArray.ElementAt(2) == PTR3);
+ MOZ_RELEASE_ASSERT(testArray.Contains(PTR3));
+
+ MOZ_RELEASE_ASSERT(testArray.RemoveElement(PTR1));
+
+ MOZ_RELEASE_ASSERT(testArray.Length() == 2);
+ MOZ_RELEASE_ASSERT(testArray[0] == PTR2);
+ MOZ_RELEASE_ASSERT(testArray.ElementAt(0) == PTR2);
+ MOZ_RELEASE_ASSERT(testArray[1] == PTR3);
+ MOZ_RELEASE_ASSERT(testArray.ElementAt(1) == PTR3);
+
+ MOZ_RELEASE_ASSERT(testArray.RemoveElement(PTR2));
+
+ MOZ_RELEASE_ASSERT(testArray.Length() == 1);
+ MOZ_RELEASE_ASSERT(testArray[0] == PTR3);
+ MOZ_RELEASE_ASSERT(testArray.ElementAt(0) == PTR3);
+
+ MOZ_RELEASE_ASSERT(testArray.RemoveElement(PTR3));
+
+ MOZ_RELEASE_ASSERT(testArray.Length() == 0);
+
+ testArray.Clear();
+
+ MOZ_RELEASE_ASSERT(testArray.Length() == 0);
+
+ testArray.AppendElement(PTR1);
+
+ MOZ_RELEASE_ASSERT(testArray.Length() == 1);
+ MOZ_RELEASE_ASSERT(testArray[0] == PTR1);
+ MOZ_RELEASE_ASSERT(testArray.ElementAt(0) == PTR1);
+
+ testArray.AppendElement(PTR2);
+
+ MOZ_RELEASE_ASSERT(testArray.Length() == 2);
+ MOZ_RELEASE_ASSERT(testArray[0] == PTR1);
+ MOZ_RELEASE_ASSERT(testArray.ElementAt(0) == PTR1);
+ MOZ_RELEASE_ASSERT(testArray[1] == PTR2);
+ MOZ_RELEASE_ASSERT(testArray.ElementAt(1) == PTR2);
+
+ MOZ_RELEASE_ASSERT(testArray.RemoveElement(PTR2));
+
+ MOZ_RELEASE_ASSERT(testArray.Length() == 1);
+ MOZ_RELEASE_ASSERT(testArray[0] == PTR1);
+ MOZ_RELEASE_ASSERT(testArray.ElementAt(0) == PTR1);
+
+ MOZ_RELEASE_ASSERT(!testArray.RemoveElement(PTR3));
+
+ MOZ_RELEASE_ASSERT(testArray.Length() == 1);
+ MOZ_RELEASE_ASSERT(testArray[0] == PTR1);
+ MOZ_RELEASE_ASSERT(testArray.ElementAt(0) == PTR1);
+}
+
+void TestRangeBasedLoops() {
+ using namespace mozilla;
+ SmallPointerArray<void> testArray;
+ void* verification[3];
+ uint32_t entries = 0;
+
+ for (void* test : testArray) {
+ verification[entries++] = test;
+ }
+
+ MOZ_RELEASE_ASSERT(entries == 0);
+
+ testArray.AppendElement(PTR1);
+
+ for (void* test : testArray) {
+ verification[entries++] = test;
+ }
+
+ MOZ_RELEASE_ASSERT(entries == 1);
+ MOZ_RELEASE_ASSERT(verification[0] == PTR1);
+
+ entries = 0;
+
+ testArray.AppendElement(PTR2);
+
+ for (void* test : testArray) {
+ verification[entries++] = test;
+ }
+
+ MOZ_RELEASE_ASSERT(entries == 2);
+ MOZ_RELEASE_ASSERT(verification[0] == PTR1);
+ MOZ_RELEASE_ASSERT(verification[1] == PTR2);
+
+ entries = 0;
+
+ testArray.RemoveElement(PTR1);
+
+ for (void* test : testArray) {
+ verification[entries++] = test;
+ }
+
+ MOZ_RELEASE_ASSERT(entries == 1);
+ MOZ_RELEASE_ASSERT(verification[0] == PTR2);
+
+ entries = 0;
+
+ testArray.AppendElement(PTR1);
+ testArray.AppendElement(PTR3);
+
+ for (void* test : testArray) {
+ verification[entries++] = test;
+ }
+
+ MOZ_RELEASE_ASSERT(entries == 3);
+ MOZ_RELEASE_ASSERT(verification[0] == PTR2);
+ MOZ_RELEASE_ASSERT(verification[1] == PTR1);
+ MOZ_RELEASE_ASSERT(verification[2] == PTR3);
+
+ entries = 0;
+
+ testArray.RemoveElement(PTR1);
+ testArray.RemoveElement(PTR2);
+ testArray.RemoveElement(PTR3);
+
+ for (void* test : testArray) {
+ verification[entries++] = test;
+ }
+
+ MOZ_RELEASE_ASSERT(entries == 0);
+
+ testArray.Clear();
+
+ for (void* test : testArray) {
+ verification[entries++] = test;
+ }
+
+ MOZ_RELEASE_ASSERT(entries == 0);
+}
+
+void TestMove() {
+ using namespace mozilla;
+
+ SmallPointerArray<void> testArray;
+ testArray.AppendElement(PTR1);
+ testArray.AppendElement(PTR2);
+
+ SmallPointerArray<void> moved = std::move(testArray);
+
+ MOZ_RELEASE_ASSERT(testArray.IsEmpty());
+ MOZ_RELEASE_ASSERT(moved.Length() == 2);
+ MOZ_RELEASE_ASSERT(moved[0] == PTR1);
+ MOZ_RELEASE_ASSERT(moved[1] == PTR2);
+
+ // Heap case.
+ moved.AppendElement(PTR3);
+
+ SmallPointerArray<void> another = std::move(moved);
+
+ MOZ_RELEASE_ASSERT(testArray.IsEmpty());
+ MOZ_RELEASE_ASSERT(moved.IsEmpty());
+ MOZ_RELEASE_ASSERT(another.Length() == 3);
+ MOZ_RELEASE_ASSERT(another[0] == PTR1);
+ MOZ_RELEASE_ASSERT(another[1] == PTR2);
+ MOZ_RELEASE_ASSERT(another[2] == PTR3);
+
+ // Move assignment.
+ testArray = std::move(another);
+
+ MOZ_RELEASE_ASSERT(moved.IsEmpty());
+ MOZ_RELEASE_ASSERT(another.IsEmpty());
+ MOZ_RELEASE_ASSERT(testArray.Length() == 3);
+ MOZ_RELEASE_ASSERT(testArray[0] == PTR1);
+ MOZ_RELEASE_ASSERT(testArray[1] == PTR2);
+ MOZ_RELEASE_ASSERT(testArray[2] == PTR3);
+}
+
+int main() {
+ TestArrayManipulation();
+ TestRangeBasedLoops();
+ TestMove();
+ return 0;
+}
diff --git a/mfbt/tests/TestSplayTree.cpp b/mfbt/tests/TestSplayTree.cpp
new file mode 100644
index 0000000000..8269664ce9
--- /dev/null
+++ b/mfbt/tests/TestSplayTree.cpp
@@ -0,0 +1,208 @@
+/* -*- 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/TestTextUtils.cpp b/mfbt/tests/TestTextUtils.cpp
new file mode 100644
index 0000000000..93989019f7
--- /dev/null
+++ b/mfbt/tests/TestTextUtils.cpp
@@ -0,0 +1,1064 @@
+/* -*- 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/TextUtils.h"
+
+using mozilla::AsciiAlphanumericToNumber;
+using mozilla::IsAscii;
+using mozilla::IsAsciiAlpha;
+using mozilla::IsAsciiAlphanumeric;
+using mozilla::IsAsciiDigit;
+using mozilla::IsAsciiLowercaseAlpha;
+using mozilla::IsAsciiNullTerminated;
+using mozilla::IsAsciiUppercaseAlpha;
+
+static void TestIsAscii() {
+ // char
+
+ static_assert(!IsAscii(char(-1)), "char(-1) isn't ASCII");
+
+ static_assert(IsAscii('\0'), "nul is ASCII");
+
+ static_assert(IsAscii('A'), "'A' is ASCII");
+ static_assert(IsAscii('B'), "'B' is ASCII");
+ static_assert(IsAscii('M'), "'M' is ASCII");
+ static_assert(IsAscii('Y'), "'Y' is ASCII");
+ static_assert(IsAscii('Z'), "'Z' is ASCII");
+
+ static_assert(IsAscii('['), "'[' is ASCII");
+ static_assert(IsAscii('`'), "'`' is ASCII");
+
+ static_assert(IsAscii('a'), "'a' is ASCII");
+ static_assert(IsAscii('b'), "'b' is ASCII");
+ static_assert(IsAscii('m'), "'m' is ASCII");
+ static_assert(IsAscii('y'), "'y' is ASCII");
+ static_assert(IsAscii('z'), "'z' is ASCII");
+
+ static_assert(IsAscii('{'), "'{' is ASCII");
+
+ static_assert(IsAscii('5'), "'5' is ASCII");
+
+ static_assert(IsAscii('\x7F'), "'\\x7F' is ASCII");
+ static_assert(!IsAscii('\x80'), "'\\x80' isn't ASCII");
+
+ // char16_t
+
+ static_assert(!IsAscii(char16_t(-1)), "char16_t(-1) isn't ASCII");
+
+ static_assert(IsAscii(u'\0'), "nul is ASCII");
+
+ static_assert(IsAscii(u'A'), "u'A' is ASCII");
+ static_assert(IsAscii(u'B'), "u'B' is ASCII");
+ static_assert(IsAscii(u'M'), "u'M' is ASCII");
+ static_assert(IsAscii(u'Y'), "u'Y' is ASCII");
+ static_assert(IsAscii(u'Z'), "u'Z' is ASCII");
+
+ static_assert(IsAscii(u'['), "u'[' is ASCII");
+ static_assert(IsAscii(u'`'), "u'`' is ASCII");
+
+ static_assert(IsAscii(u'a'), "u'a' is ASCII");
+ static_assert(IsAscii(u'b'), "u'b' is ASCII");
+ static_assert(IsAscii(u'm'), "u'm' is ASCII");
+ static_assert(IsAscii(u'y'), "u'y' is ASCII");
+ static_assert(IsAscii(u'z'), "u'z' is ASCII");
+
+ static_assert(IsAscii(u'{'), "u'{' is ASCII");
+
+ static_assert(IsAscii(u'5'), "u'5' is ASCII");
+
+ static_assert(IsAscii(u'\x7F'), "u'\\x7F' is ASCII");
+ static_assert(!IsAscii(u'\x80'), "u'\\x80' isn't ASCII");
+
+ // char32_t
+
+ static_assert(!IsAscii(char32_t(-1)), "char32_t(-1) isn't ASCII");
+
+ static_assert(IsAscii(U'\0'), "nul is ASCII");
+
+ static_assert(IsAscii(U'A'), "U'A' is ASCII");
+ static_assert(IsAscii(U'B'), "U'B' is ASCII");
+ static_assert(IsAscii(U'M'), "U'M' is ASCII");
+ static_assert(IsAscii(U'Y'), "U'Y' is ASCII");
+ static_assert(IsAscii(U'Z'), "U'Z' is ASCII");
+
+ static_assert(IsAscii(U'['), "U'[' is ASCII");
+ static_assert(IsAscii(U'`'), "U'`' is ASCII");
+
+ static_assert(IsAscii(U'a'), "U'a' is ASCII");
+ static_assert(IsAscii(U'b'), "U'b' is ASCII");
+ static_assert(IsAscii(U'm'), "U'm' is ASCII");
+ static_assert(IsAscii(U'y'), "U'y' is ASCII");
+ static_assert(IsAscii(U'z'), "U'z' is ASCII");
+
+ static_assert(IsAscii(U'{'), "U'{' is ASCII");
+
+ static_assert(IsAscii(U'5'), "U'5' is ASCII");
+
+ static_assert(IsAscii(U'\x7F'), "U'\\x7F' is ASCII");
+ static_assert(!IsAscii(U'\x80'), "U'\\x80' isn't ASCII");
+}
+
+static void TestIsAsciiNullTerminated() {
+ // char
+
+ constexpr char allChar[] =
+ "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\0x0C\x0D\x0E\x0F"
+ "\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\0x1C\x1D\x1E\x1F"
+ "\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\0x2C\x2D\x2E\x2F"
+ "\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\0x3C\x3D\x3E\x3F"
+ "\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\0x4C\x4D\x4E\x4F"
+ "\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\0x5C\x5D\x5E\x5F"
+ "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\0x6C\x6D\x6E\x6F"
+ "\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\0x7C\x7D\x7E\x7F";
+
+ static_assert(IsAsciiNullTerminated(allChar), "allChar is ASCII");
+
+ constexpr char loBadChar[] = "\x80";
+
+ static_assert(!IsAsciiNullTerminated(loBadChar), "loBadChar isn't ASCII");
+
+ constexpr char hiBadChar[] = "\xFF";
+
+ static_assert(!IsAsciiNullTerminated(hiBadChar), "hiBadChar isn't ASCII");
+
+ // char16_t
+
+ constexpr char16_t allChar16[] =
+ u"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\0x0C\x0D\x0E\x0F"
+ "\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\0x1C\x1D\x1E\x1F"
+ "\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\0x2C\x2D\x2E\x2F"
+ "\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\0x3C\x3D\x3E\x3F"
+ "\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\0x4C\x4D\x4E\x4F"
+ "\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\0x5C\x5D\x5E\x5F"
+ "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\0x6C\x6D\x6E\x6F"
+ "\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\0x7C\x7D\x7E\x7F";
+
+ static_assert(IsAsciiNullTerminated(allChar16), "allChar16 is ASCII");
+
+ constexpr char16_t loBadChar16[] = u"\x80";
+
+ static_assert(!IsAsciiNullTerminated(loBadChar16), "loBadChar16 isn't ASCII");
+
+ constexpr char16_t hiBadChar16[] = u"\xFF";
+
+ static_assert(!IsAsciiNullTerminated(hiBadChar16), "hiBadChar16 isn't ASCII");
+
+ constexpr char16_t highestChar16[] = u"\uFFFF";
+
+ static_assert(!IsAsciiNullTerminated(highestChar16),
+ "highestChar16 isn't ASCII");
+
+ // char32_t
+
+ constexpr char32_t allChar32[] =
+ U"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\0x0C\x0D\x0E\x0F"
+ "\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\0x1C\x1D\x1E\x1F"
+ "\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\0x2C\x2D\x2E\x2F"
+ "\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\0x3C\x3D\x3E\x3F"
+ "\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\0x4C\x4D\x4E\x4F"
+ "\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\0x5C\x5D\x5E\x5F"
+ "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\0x6C\x6D\x6E\x6F"
+ "\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\0x7C\x7D\x7E\x7F";
+
+ static_assert(IsAsciiNullTerminated(allChar32), "allChar32 is ASCII");
+
+ constexpr char32_t loBadChar32[] = U"\x80";
+
+ static_assert(!IsAsciiNullTerminated(loBadChar32), "loBadChar32 isn't ASCII");
+
+ constexpr char32_t hiBadChar32[] = U"\xFF";
+
+ static_assert(!IsAsciiNullTerminated(hiBadChar32), "hiBadChar32 isn't ASCII");
+
+ constexpr char32_t highestChar32[] = {static_cast<char32_t>(-1), 0};
+
+ static_assert(!IsAsciiNullTerminated(highestChar32),
+ "highestChar32 isn't ASCII");
+}
+
+static void TestIsAsciiAlpha() {
+ // char
+
+ static_assert(!IsAsciiAlpha('@'), "'@' isn't ASCII alpha");
+ static_assert('@' == 0x40, "'@' has value 0x40");
+
+ static_assert('A' == 0x41, "'A' has value 0x41");
+ static_assert(IsAsciiAlpha('A'), "'A' is ASCII alpha");
+ static_assert(IsAsciiAlpha('B'), "'B' is ASCII alpha");
+ static_assert(IsAsciiAlpha('M'), "'M' is ASCII alpha");
+ static_assert(IsAsciiAlpha('Y'), "'Y' is ASCII alpha");
+ static_assert(IsAsciiAlpha('Z'), "'Z' is ASCII alpha");
+
+ static_assert('Z' == 0x5A, "'Z' has value 0x5A");
+ static_assert('[' == 0x5B, "'[' has value 0x5B");
+ static_assert(!IsAsciiAlpha('['), "'[' isn't ASCII alpha");
+
+ static_assert(!IsAsciiAlpha('`'), "'`' isn't ASCII alpha");
+ static_assert('`' == 0x60, "'`' has value 0x60");
+
+ static_assert('a' == 0x61, "'a' has value 0x61");
+ static_assert(IsAsciiAlpha('a'), "'a' is ASCII alpha");
+ static_assert(IsAsciiAlpha('b'), "'b' is ASCII alpha");
+ static_assert(IsAsciiAlpha('m'), "'m' is ASCII alpha");
+ static_assert(IsAsciiAlpha('y'), "'y' is ASCII alpha");
+ static_assert(IsAsciiAlpha('z'), "'z' is ASCII alpha");
+
+ static_assert('z' == 0x7A, "'z' has value 0x7A");
+ static_assert('{' == 0x7B, "'{' has value 0x7B");
+ static_assert(!IsAsciiAlpha('{'), "'{' isn't ASCII alpha");
+
+ static_assert(!IsAsciiAlpha('5'), "'5' isn't ASCII alpha");
+
+ // char16_t
+
+ static_assert(!IsAsciiAlpha(u'@'), "u'@' isn't ASCII alpha");
+ static_assert(u'@' == 0x40, "u'@' has value 0x40");
+
+ static_assert(u'A' == 0x41, "u'A' has value 0x41");
+ static_assert(IsAsciiAlpha(u'A'), "u'A' is ASCII alpha");
+ static_assert(IsAsciiAlpha(u'B'), "u'B' is ASCII alpha");
+ static_assert(IsAsciiAlpha(u'M'), "u'M' is ASCII alpha");
+ static_assert(IsAsciiAlpha(u'Y'), "u'Y' is ASCII alpha");
+ static_assert(IsAsciiAlpha(u'Z'), "u'Z' is ASCII alpha");
+
+ static_assert(u'Z' == 0x5A, "u'Z' has value 0x5A");
+ static_assert(u'[' == 0x5B, "u'[' has value 0x5B");
+ static_assert(!IsAsciiAlpha(u'['), "u'[' isn't ASCII alpha");
+
+ static_assert(!IsAsciiAlpha(u'`'), "u'`' isn't ASCII alpha");
+ static_assert(u'`' == 0x60, "u'`' has value 0x60");
+
+ static_assert(u'a' == 0x61, "u'a' has value 0x61");
+ static_assert(IsAsciiAlpha(u'a'), "u'a' is ASCII alpha");
+ static_assert(IsAsciiAlpha(u'b'), "u'b' is ASCII alpha");
+ static_assert(IsAsciiAlpha(u'm'), "u'm' is ASCII alpha");
+ static_assert(IsAsciiAlpha(u'y'), "u'y' is ASCII alpha");
+ static_assert(IsAsciiAlpha(u'z'), "u'z' is ASCII alpha");
+
+ static_assert(u'z' == 0x7A, "u'z' has value 0x7A");
+ static_assert(u'{' == 0x7B, "u'{' has value 0x7B");
+ static_assert(!IsAsciiAlpha(u'{'), "u'{' isn't ASCII alpha");
+
+ static_assert(!IsAsciiAlpha(u'5'), "u'5' isn't ASCII alpha");
+
+ // char32_t
+
+ static_assert(!IsAsciiAlpha(U'@'), "U'@' isn't ASCII alpha");
+ static_assert(U'@' == 0x40, "U'@' has value 0x40");
+
+ static_assert(U'A' == 0x41, "U'A' has value 0x41");
+ static_assert(IsAsciiAlpha(U'A'), "U'A' is ASCII alpha");
+ static_assert(IsAsciiAlpha(U'B'), "U'B' is ASCII alpha");
+ static_assert(IsAsciiAlpha(U'M'), "U'M' is ASCII alpha");
+ static_assert(IsAsciiAlpha(U'Y'), "U'Y' is ASCII alpha");
+ static_assert(IsAsciiAlpha(U'Z'), "U'Z' is ASCII alpha");
+
+ static_assert(U'Z' == 0x5A, "U'Z' has value 0x5A");
+ static_assert(U'[' == 0x5B, "U'[' has value 0x5B");
+ static_assert(!IsAsciiAlpha(U'['), "U'[' isn't ASCII alpha");
+
+ static_assert(!IsAsciiAlpha(U'`'), "U'`' isn't ASCII alpha");
+ static_assert(U'`' == 0x60, "U'`' has value 0x60");
+
+ static_assert(U'a' == 0x61, "U'a' has value 0x61");
+ static_assert(IsAsciiAlpha(U'a'), "U'a' is ASCII alpha");
+ static_assert(IsAsciiAlpha(U'b'), "U'b' is ASCII alpha");
+ static_assert(IsAsciiAlpha(U'm'), "U'm' is ASCII alpha");
+ static_assert(IsAsciiAlpha(U'y'), "U'y' is ASCII alpha");
+ static_assert(IsAsciiAlpha(U'z'), "U'z' is ASCII alpha");
+
+ static_assert(U'z' == 0x7A, "U'z' has value 0x7A");
+ static_assert(U'{' == 0x7B, "U'{' has value 0x7B");
+ static_assert(!IsAsciiAlpha(U'{'), "U'{' isn't ASCII alpha");
+
+ static_assert(!IsAsciiAlpha(U'5'), "U'5' isn't ASCII alpha");
+}
+
+static void TestIsAsciiUppercaseAlpha() {
+ // char
+
+ static_assert(!IsAsciiUppercaseAlpha('@'), "'@' isn't ASCII alpha uppercase");
+ static_assert('@' == 0x40, "'@' has value 0x40");
+
+ static_assert('A' == 0x41, "'A' has value 0x41");
+ static_assert(IsAsciiUppercaseAlpha('A'), "'A' is ASCII alpha uppercase");
+ static_assert(IsAsciiUppercaseAlpha('B'), "'B' is ASCII alpha uppercase");
+ static_assert(IsAsciiUppercaseAlpha('M'), "'M' is ASCII alpha uppercase");
+ static_assert(IsAsciiUppercaseAlpha('Y'), "'Y' is ASCII alpha uppercase");
+ static_assert(IsAsciiUppercaseAlpha('Z'), "'Z' is ASCII alpha uppercase");
+
+ static_assert('Z' == 0x5A, "'Z' has value 0x5A");
+ static_assert('[' == 0x5B, "'[' has value 0x5B");
+ static_assert(!IsAsciiUppercaseAlpha('['), "'[' isn't ASCII alpha uppercase");
+
+ static_assert(!IsAsciiUppercaseAlpha('`'), "'`' isn't ASCII alpha uppercase");
+ static_assert(!IsAsciiUppercaseAlpha('a'), "'a' is ASCII alpha uppercase");
+ static_assert(!IsAsciiUppercaseAlpha('b'), "'b' is ASCII alpha uppercase");
+ static_assert(!IsAsciiUppercaseAlpha('m'), "'m' is ASCII alpha uppercase");
+ static_assert(!IsAsciiUppercaseAlpha('y'), "'y' is ASCII alpha uppercase");
+ static_assert(!IsAsciiUppercaseAlpha('z'), "'z' is ASCII alpha uppercase");
+ static_assert(!IsAsciiUppercaseAlpha('{'), "'{' isn't ASCII alpha uppercase");
+
+ // char16_t
+
+ static_assert(!IsAsciiUppercaseAlpha(u'@'),
+ "u'@' isn't ASCII alpha uppercase");
+ static_assert(u'@' == 0x40, "u'@' has value 0x40");
+
+ static_assert(u'A' == 0x41, "u'A' has value 0x41");
+ static_assert(IsAsciiUppercaseAlpha(u'A'), "u'A' is ASCII alpha uppercase");
+ static_assert(IsAsciiUppercaseAlpha(u'B'), "u'B' is ASCII alpha uppercase");
+ static_assert(IsAsciiUppercaseAlpha(u'M'), "u'M' is ASCII alpha uppercase");
+ static_assert(IsAsciiUppercaseAlpha(u'Y'), "u'Y' is ASCII alpha uppercase");
+ static_assert(IsAsciiUppercaseAlpha(u'Z'), "u'Z' is ASCII alpha uppercase");
+
+ static_assert(u'Z' == 0x5A, "u'Z' has value 0x5A");
+ static_assert(u'[' == 0x5B, "u'[' has value 0x5B");
+ static_assert(!IsAsciiUppercaseAlpha(u'['),
+ "u'[' isn't ASCII alpha uppercase");
+
+ static_assert(!IsAsciiUppercaseAlpha(u'`'),
+ "u'`' isn't ASCII alpha uppercase");
+ static_assert(!IsAsciiUppercaseAlpha(u'a'), "u'a' is ASCII alpha uppercase");
+ static_assert(!IsAsciiUppercaseAlpha(u'b'), "u'b' is ASCII alpha uppercase");
+ static_assert(!IsAsciiUppercaseAlpha(u'm'), "u'm' is ASCII alpha uppercase");
+ static_assert(!IsAsciiUppercaseAlpha(u'y'), "u'y' is ASCII alpha uppercase");
+ static_assert(!IsAsciiUppercaseAlpha(u'z'), "u'z' is ASCII alpha uppercase");
+ static_assert(!IsAsciiUppercaseAlpha(u'{'),
+ "u'{' isn't ASCII alpha uppercase");
+
+ // char32_t
+
+ static_assert(!IsAsciiUppercaseAlpha(U'@'),
+ "U'@' isn't ASCII alpha uppercase");
+ static_assert(U'@' == 0x40, "U'@' has value 0x40");
+
+ static_assert(U'A' == 0x41, "U'A' has value 0x41");
+ static_assert(IsAsciiUppercaseAlpha(U'A'), "U'A' is ASCII alpha uppercase");
+ static_assert(IsAsciiUppercaseAlpha(U'B'), "U'B' is ASCII alpha uppercase");
+ static_assert(IsAsciiUppercaseAlpha(U'M'), "U'M' is ASCII alpha uppercase");
+ static_assert(IsAsciiUppercaseAlpha(U'Y'), "U'Y' is ASCII alpha uppercase");
+ static_assert(IsAsciiUppercaseAlpha(U'Z'), "U'Z' is ASCII alpha uppercase");
+
+ static_assert(U'Z' == 0x5A, "U'Z' has value 0x5A");
+ static_assert(U'[' == 0x5B, "U'[' has value 0x5B");
+ static_assert(!IsAsciiUppercaseAlpha(U'['),
+ "U'[' isn't ASCII alpha uppercase");
+
+ static_assert(!IsAsciiUppercaseAlpha(U'`'),
+ "U'`' isn't ASCII alpha uppercase");
+ static_assert(!IsAsciiUppercaseAlpha(U'a'), "U'a' is ASCII alpha uppercase");
+ static_assert(!IsAsciiUppercaseAlpha(U'b'), "U'b' is ASCII alpha uppercase");
+ static_assert(!IsAsciiUppercaseAlpha(U'm'), "U'm' is ASCII alpha uppercase");
+ static_assert(!IsAsciiUppercaseAlpha(U'y'), "U'y' is ASCII alpha uppercase");
+ static_assert(!IsAsciiUppercaseAlpha(U'z'), "U'z' is ASCII alpha uppercase");
+ static_assert(!IsAsciiUppercaseAlpha(U'{'),
+ "U'{' isn't ASCII alpha uppercase");
+}
+
+static void TestIsAsciiLowercaseAlpha() {
+ // char
+
+ static_assert(!IsAsciiLowercaseAlpha('`'), "'`' isn't ASCII alpha lowercase");
+ static_assert('`' == 0x60, "'`' has value 0x60");
+
+ static_assert('a' == 0x61, "'a' has value 0x61");
+ static_assert(IsAsciiLowercaseAlpha('a'), "'a' is ASCII alpha lowercase");
+ static_assert(IsAsciiLowercaseAlpha('b'), "'b' is ASCII alpha lowercase");
+ static_assert(IsAsciiLowercaseAlpha('m'), "'m' is ASCII alpha lowercase");
+ static_assert(IsAsciiLowercaseAlpha('y'), "'y' is ASCII alpha lowercase");
+ static_assert(IsAsciiLowercaseAlpha('z'), "'z' is ASCII alpha lowercase");
+
+ static_assert('z' == 0x7A, "'z' has value 0x7A");
+ static_assert('{' == 0x7B, "'{' has value 0x7B");
+ static_assert(!IsAsciiLowercaseAlpha('{'), "'{' isn't ASCII alpha lowercase");
+
+ static_assert(!IsAsciiLowercaseAlpha('@'), "'@' isn't ASCII alpha lowercase");
+ static_assert(!IsAsciiLowercaseAlpha('A'), "'A' is ASCII alpha lowercase");
+ static_assert(!IsAsciiLowercaseAlpha('B'), "'B' is ASCII alpha lowercase");
+ static_assert(!IsAsciiLowercaseAlpha('M'), "'M' is ASCII alpha lowercase");
+ static_assert(!IsAsciiLowercaseAlpha('Y'), "'Y' is ASCII alpha lowercase");
+ static_assert(!IsAsciiLowercaseAlpha('Z'), "'Z' is ASCII alpha lowercase");
+ static_assert(!IsAsciiLowercaseAlpha('['), "'[' isn't ASCII alpha lowercase");
+
+ // char16_t
+
+ static_assert(!IsAsciiLowercaseAlpha(u'`'),
+ "u'`' isn't ASCII alpha lowercase");
+ static_assert(u'`' == 0x60, "u'`' has value 0x60");
+
+ static_assert(u'a' == 0x61, "u'a' has value 0x61");
+ static_assert(IsAsciiLowercaseAlpha(u'a'), "u'a' is ASCII alpha lowercase");
+ static_assert(IsAsciiLowercaseAlpha(u'b'), "u'b' is ASCII alpha lowercase");
+ static_assert(IsAsciiLowercaseAlpha(u'm'), "u'm' is ASCII alpha lowercase");
+ static_assert(IsAsciiLowercaseAlpha(u'y'), "u'y' is ASCII alpha lowercase");
+ static_assert(IsAsciiLowercaseAlpha(u'z'), "u'z' is ASCII alpha lowercase");
+
+ static_assert(u'z' == 0x7A, "u'z' has value 0x7A");
+ static_assert(u'{' == 0x7B, "u'{' has value 0x7B");
+ static_assert(!IsAsciiLowercaseAlpha(u'{'),
+ "u'{' isn't ASCII alpha lowercase");
+
+ static_assert(!IsAsciiLowercaseAlpha(u'@'),
+ "u'@' isn't ASCII alpha lowercase");
+ static_assert(!IsAsciiLowercaseAlpha(u'A'), "u'A' is ASCII alpha lowercase");
+ static_assert(!IsAsciiLowercaseAlpha(u'B'), "u'B' is ASCII alpha lowercase");
+ static_assert(!IsAsciiLowercaseAlpha(u'M'), "u'M' is ASCII alpha lowercase");
+ static_assert(!IsAsciiLowercaseAlpha(u'Y'), "u'Y' is ASCII alpha lowercase");
+ static_assert(!IsAsciiLowercaseAlpha(u'Z'), "u'Z' is ASCII alpha lowercase");
+ static_assert(!IsAsciiLowercaseAlpha(u'['),
+ "u'[' isn't ASCII alpha lowercase");
+
+ // char32_t
+
+ static_assert(!IsAsciiLowercaseAlpha(U'`'),
+ "U'`' isn't ASCII alpha lowercase");
+ static_assert(U'`' == 0x60, "U'`' has value 0x60");
+
+ static_assert(U'a' == 0x61, "U'a' has value 0x61");
+ static_assert(IsAsciiLowercaseAlpha(U'a'), "U'a' is ASCII alpha lowercase");
+ static_assert(IsAsciiLowercaseAlpha(U'b'), "U'b' is ASCII alpha lowercase");
+ static_assert(IsAsciiLowercaseAlpha(U'm'), "U'm' is ASCII alpha lowercase");
+ static_assert(IsAsciiLowercaseAlpha(U'y'), "U'y' is ASCII alpha lowercase");
+ static_assert(IsAsciiLowercaseAlpha(U'z'), "U'z' is ASCII alpha lowercase");
+
+ static_assert(U'z' == 0x7A, "U'z' has value 0x7A");
+ static_assert(U'{' == 0x7B, "U'{' has value 0x7B");
+ static_assert(!IsAsciiLowercaseAlpha(U'{'),
+ "U'{' isn't ASCII alpha lowercase");
+
+ static_assert(!IsAsciiLowercaseAlpha(U'@'),
+ "U'@' isn't ASCII alpha lowercase");
+ static_assert(!IsAsciiLowercaseAlpha(U'A'), "U'A' is ASCII alpha lowercase");
+ static_assert(!IsAsciiLowercaseAlpha(U'B'), "U'B' is ASCII alpha lowercase");
+ static_assert(!IsAsciiLowercaseAlpha(U'M'), "U'M' is ASCII alpha lowercase");
+ static_assert(!IsAsciiLowercaseAlpha(U'Y'), "U'Y' is ASCII alpha lowercase");
+ static_assert(!IsAsciiLowercaseAlpha(U'Z'), "U'Z' is ASCII alpha lowercase");
+ static_assert(!IsAsciiLowercaseAlpha(U'['),
+ "U'[' isn't ASCII alpha lowercase");
+}
+
+static void TestIsAsciiAlphanumeric() {
+ // char
+
+ static_assert(!IsAsciiAlphanumeric('/'), "'/' isn't ASCII alphanumeric");
+ static_assert('/' == 0x2F, "'/' has value 0x2F");
+
+ static_assert('0' == 0x30, "'0' has value 0x30");
+ static_assert(IsAsciiAlphanumeric('0'), "'0' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric('1'), "'1' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric('5'), "'5' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric('8'), "'8' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric('9'), "'9' is ASCII alphanumeric");
+
+ static_assert('9' == 0x39, "'9' has value 0x39");
+ static_assert(':' == 0x3A, "':' has value 0x3A");
+ static_assert(!IsAsciiAlphanumeric(':'), "':' isn't ASCII alphanumeric");
+
+ static_assert(!IsAsciiAlphanumeric('@'), "'@' isn't ASCII alphanumeric");
+ static_assert('@' == 0x40, "'@' has value 0x40");
+
+ static_assert('A' == 0x41, "'A' has value 0x41");
+ static_assert(IsAsciiAlphanumeric('A'), "'A' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric('B'), "'B' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric('M'), "'M' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric('Y'), "'Y' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric('Z'), "'Z' is ASCII alphanumeric");
+
+ static_assert('Z' == 0x5A, "'Z' has value 0x5A");
+ static_assert('[' == 0x5B, "'[' has value 0x5B");
+ static_assert(!IsAsciiAlphanumeric('['), "'[' isn't ASCII alphanumeric");
+
+ static_assert(!IsAsciiAlphanumeric('`'), "'`' isn't ASCII alphanumeric");
+ static_assert('`' == 0x60, "'`' has value 0x60");
+
+ static_assert('a' == 0x61, "'a' has value 0x61");
+ static_assert(IsAsciiAlphanumeric('a'), "'a' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric('b'), "'b' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric('m'), "'m' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric('y'), "'y' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric('z'), "'z' is ASCII alphanumeric");
+
+ static_assert('z' == 0x7A, "'z' has value 0x7A");
+ static_assert('{' == 0x7B, "'{' has value 0x7B");
+ static_assert(!IsAsciiAlphanumeric('{'), "'{' isn't ASCII alphanumeric");
+
+ // char16_t
+
+ static_assert(!IsAsciiAlphanumeric(u'/'), "u'/' isn't ASCII alphanumeric");
+ static_assert(u'/' == 0x2F, "u'/' has value 0x2F");
+
+ static_assert(u'0' == 0x30, "u'0' has value 0x30");
+ static_assert(IsAsciiAlphanumeric(u'0'), "u'0' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(u'1'), "u'1' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(u'5'), "u'5' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(u'8'), "u'8' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(u'9'), "u'9' is ASCII alphanumeric");
+
+ static_assert(u'9' == 0x39, "u'9' has value 0x39");
+ static_assert(u':' == 0x3A, "u':' has value 0x3A");
+ static_assert(!IsAsciiAlphanumeric(u':'), "u':' isn't ASCII alphanumeric");
+
+ static_assert(!IsAsciiAlphanumeric(u'@'), "u'@' isn't ASCII alphanumeric");
+ static_assert(u'@' == 0x40, "u'@' has value 0x40");
+
+ static_assert(u'A' == 0x41, "u'A' has value 0x41");
+ static_assert(IsAsciiAlphanumeric(u'A'), "u'A' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(u'B'), "u'B' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(u'M'), "u'M' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(u'Y'), "u'Y' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(u'Z'), "u'Z' is ASCII alphanumeric");
+
+ static_assert(u'Z' == 0x5A, "u'Z' has value 0x5A");
+ static_assert(u'[' == 0x5B, "u'[' has value 0x5B");
+ static_assert(!IsAsciiAlphanumeric(u'['), "u'[' isn't ASCII alphanumeric");
+
+ static_assert(!IsAsciiAlphanumeric(u'`'), "u'`' isn't ASCII alphanumeric");
+ static_assert(u'`' == 0x60, "u'`' has value 0x60");
+
+ static_assert(u'a' == 0x61, "u'a' has value 0x61");
+ static_assert(IsAsciiAlphanumeric(u'a'), "u'a' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(u'b'), "u'b' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(u'm'), "u'm' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(u'y'), "u'y' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(u'z'), "u'z' is ASCII alphanumeric");
+
+ static_assert(u'z' == 0x7A, "u'z' has value 0x7A");
+ static_assert(u'{' == 0x7B, "u'{' has value 0x7B");
+ static_assert(!IsAsciiAlphanumeric(u'{'), "u'{' isn't ASCII alphanumeric");
+
+ // char32_t
+
+ static_assert(!IsAsciiAlphanumeric(U'/'), "U'/' isn't ASCII alphanumeric");
+ static_assert(U'/' == 0x2F, "U'/' has value 0x2F");
+
+ static_assert(U'0' == 0x30, "U'0' has value 0x30");
+ static_assert(IsAsciiAlphanumeric(U'0'), "U'0' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(U'1'), "U'1' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(U'5'), "U'5' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(U'8'), "U'8' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(U'9'), "U'9' is ASCII alphanumeric");
+
+ static_assert(U'9' == 0x39, "U'9' has value 0x39");
+ static_assert(U':' == 0x3A, "U':' has value 0x3A");
+ static_assert(!IsAsciiAlphanumeric(U':'), "U':' isn't ASCII alphanumeric");
+
+ static_assert(!IsAsciiAlphanumeric(U'@'), "U'@' isn't ASCII alphanumeric");
+ static_assert(U'@' == 0x40, "U'@' has value 0x40");
+
+ static_assert(U'A' == 0x41, "U'A' has value 0x41");
+ static_assert(IsAsciiAlphanumeric(U'A'), "U'A' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(U'B'), "U'B' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(U'M'), "U'M' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(U'Y'), "U'Y' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(U'Z'), "U'Z' is ASCII alphanumeric");
+
+ static_assert(U'Z' == 0x5A, "U'Z' has value 0x5A");
+ static_assert(U'[' == 0x5B, "U'[' has value 0x5B");
+ static_assert(!IsAsciiAlphanumeric(U'['), "U'[' isn't ASCII alphanumeric");
+
+ static_assert(!IsAsciiAlphanumeric(U'`'), "U'`' isn't ASCII alphanumeric");
+ static_assert(U'`' == 0x60, "U'`' has value 0x60");
+
+ static_assert(U'a' == 0x61, "U'a' has value 0x61");
+ static_assert(IsAsciiAlphanumeric(U'a'), "U'a' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(U'b'), "U'b' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(U'm'), "U'm' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(U'y'), "U'y' is ASCII alphanumeric");
+ static_assert(IsAsciiAlphanumeric(U'z'), "U'z' is ASCII alphanumeric");
+
+ static_assert(U'z' == 0x7A, "U'z' has value 0x7A");
+ static_assert(U'{' == 0x7B, "U'{' has value 0x7B");
+ static_assert(!IsAsciiAlphanumeric(U'{'), "U'{' isn't ASCII alphanumeric");
+}
+
+static void TestAsciiAlphanumericToNumber() {
+ // When AsciiAlphanumericToNumber becomes constexpr, make sure to convert all
+ // these to just static_assert.
+
+ // char
+
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('0') == 0, "'0' converts to 0");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('1') == 1, "'1' converts to 1");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('2') == 2, "'2' converts to 2");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('3') == 3, "'3' converts to 3");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('4') == 4, "'4' converts to 4");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('5') == 5, "'5' converts to 5");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('6') == 6, "'6' converts to 6");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('7') == 7, "'7' converts to 7");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('8') == 8, "'8' converts to 8");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('9') == 9, "'9' converts to 9");
+
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('A') == 10,
+ "'A' converts to 10");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('B') == 11,
+ "'B' converts to 11");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('C') == 12,
+ "'C' converts to 12");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('D') == 13,
+ "'D' converts to 13");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('E') == 14,
+ "'E' converts to 14");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('F') == 15,
+ "'F' converts to 15");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('G') == 16,
+ "'G' converts to 16");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('H') == 17,
+ "'H' converts to 17");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('I') == 18,
+ "'I' converts to 18");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('J') == 19,
+ "'J' converts to 19");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('K') == 20,
+ "'K' converts to 20");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('L') == 21,
+ "'L' converts to 21");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('M') == 22,
+ "'M' converts to 22");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('N') == 23,
+ "'N' converts to 23");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('O') == 24,
+ "'O' converts to 24");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('P') == 25,
+ "'P' converts to 25");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('Q') == 26,
+ "'Q' converts to 26");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('R') == 27,
+ "'R' converts to 27");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('S') == 28,
+ "'S' converts to 28");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('T') == 29,
+ "'T' converts to 29");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('U') == 30,
+ "'U' converts to 30");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('V') == 31,
+ "'V' converts to 31");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('W') == 32,
+ "'W' converts to 32");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('X') == 33,
+ "'X' converts to 33");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('Y') == 34,
+ "'Y' converts to 34");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('Z') == 35,
+ "'Z' converts to 35");
+
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('a') == 10,
+ "'a' converts to 10");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('b') == 11,
+ "'b' converts to 11");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('c') == 12,
+ "'c' converts to 12");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('d') == 13,
+ "'d' converts to 13");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('e') == 14,
+ "'e' converts to 14");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('f') == 15,
+ "'f' converts to 15");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('g') == 16,
+ "'g' converts to 16");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('h') == 17,
+ "'h' converts to 17");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('i') == 18,
+ "'i' converts to 18");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('j') == 19,
+ "'j' converts to 19");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('k') == 20,
+ "'k' converts to 20");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('l') == 21,
+ "'l' converts to 21");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('m') == 22,
+ "'m' converts to 22");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('n') == 23,
+ "'n' converts to 23");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('o') == 24,
+ "'o' converts to 24");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('p') == 25,
+ "'p' converts to 25");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('q') == 26,
+ "'q' converts to 26");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('r') == 27,
+ "'r' converts to 27");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('s') == 28,
+ "'s' converts to 28");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('t') == 29,
+ "'t' converts to 29");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('u') == 30,
+ "'u' converts to 30");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('v') == 31,
+ "'v' converts to 31");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('w') == 32,
+ "'w' converts to 32");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('x') == 33,
+ "'x' converts to 33");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('y') == 34,
+ "'y' converts to 34");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('z') == 35,
+ "'z' converts to 35");
+
+ // char16_t
+
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'0') == 0,
+ "u'0' converts to 0");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'1') == 1,
+ "u'1' converts to 1");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'2') == 2,
+ "u'2' converts to 2");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'3') == 3,
+ "u'3' converts to 3");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'4') == 4,
+ "u'4' converts to 4");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'5') == 5,
+ "u'5' converts to 5");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'6') == 6,
+ "u'6' converts to 6");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'7') == 7,
+ "u'7' converts to 7");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'8') == 8,
+ "u'8' converts to 8");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'9') == 9,
+ "u'9' converts to 9");
+
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'A') == 10,
+ "u'A' converts to 10");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'B') == 11,
+ "u'B' converts to 11");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'C') == 12,
+ "u'C' converts to 12");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'D') == 13,
+ "u'D' converts to 13");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'E') == 14,
+ "u'E' converts to 14");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'F') == 15,
+ "u'F' converts to 15");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'G') == 16,
+ "u'G' converts to 16");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'H') == 17,
+ "u'H' converts to 17");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'I') == 18,
+ "u'I' converts to 18");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'J') == 19,
+ "u'J' converts to 19");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'K') == 20,
+ "u'K' converts to 20");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'L') == 21,
+ "u'L' converts to 21");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'M') == 22,
+ "u'M' converts to 22");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'N') == 23,
+ "u'N' converts to 23");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'O') == 24,
+ "u'O' converts to 24");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'P') == 25,
+ "u'P' converts to 25");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'Q') == 26,
+ "u'Q' converts to 26");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'R') == 27,
+ "u'R' converts to 27");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'S') == 28,
+ "u'S' converts to 28");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'T') == 29,
+ "u'T' converts to 29");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'U') == 30,
+ "u'U' converts to 30");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'V') == 31,
+ "u'V' converts to 31");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'W') == 32,
+ "u'W' converts to 32");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'X') == 33,
+ "u'X' converts to 33");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'Y') == 34,
+ "u'Y' converts to 34");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'Z') == 35,
+ "u'Z' converts to 35");
+
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'a') == 10,
+ "u'a' converts to 10");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'b') == 11,
+ "u'b' converts to 11");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'c') == 12,
+ "u'c' converts to 12");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'd') == 13,
+ "u'd' converts to 13");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'e') == 14,
+ "u'e' converts to 14");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'f') == 15,
+ "u'f' converts to 15");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'g') == 16,
+ "u'g' converts to 16");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'h') == 17,
+ "u'h' converts to 17");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'i') == 18,
+ "u'i' converts to 18");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'j') == 19,
+ "u'j' converts to 19");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'k') == 20,
+ "u'k' converts to 20");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'l') == 21,
+ "u'l' converts to 21");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'm') == 22,
+ "u'm' converts to 22");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'n') == 23,
+ "u'n' converts to 23");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'o') == 24,
+ "u'o' converts to 24");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'p') == 25,
+ "u'p' converts to 25");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'q') == 26,
+ "u'q' converts to 26");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'r') == 27,
+ "u'r' converts to 27");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u's') == 28,
+ "u's' converts to 28");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u't') == 29,
+ "u't' converts to 29");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'u') == 30,
+ "u'u' converts to 30");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'v') == 31,
+ "u'v' converts to 31");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'w') == 32,
+ "u'w' converts to 32");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'x') == 33,
+ "u'x' converts to 33");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'y') == 34,
+ "u'y' converts to 34");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'z') == 35,
+ "u'z' converts to 35");
+
+ // char32_t
+
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'0') == 0,
+ "U'0' converts to 0");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'1') == 1,
+ "U'1' converts to 1");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'2') == 2,
+ "U'2' converts to 2");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'3') == 3,
+ "U'3' converts to 3");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'4') == 4,
+ "U'4' converts to 4");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'5') == 5,
+ "U'5' converts to 5");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'6') == 6,
+ "U'6' converts to 6");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'7') == 7,
+ "U'7' converts to 7");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'8') == 8,
+ "U'8' converts to 8");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'9') == 9,
+ "U'9' converts to 9");
+
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'A') == 10,
+ "U'A' converts to 10");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'B') == 11,
+ "U'B' converts to 11");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'C') == 12,
+ "U'C' converts to 12");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'D') == 13,
+ "U'D' converts to 13");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'E') == 14,
+ "U'E' converts to 14");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'F') == 15,
+ "U'F' converts to 15");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'G') == 16,
+ "U'G' converts to 16");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'H') == 17,
+ "U'H' converts to 17");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'I') == 18,
+ "U'I' converts to 18");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'J') == 19,
+ "U'J' converts to 19");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'K') == 20,
+ "U'K' converts to 20");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'L') == 21,
+ "U'L' converts to 21");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'M') == 22,
+ "U'M' converts to 22");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'N') == 23,
+ "U'N' converts to 23");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'O') == 24,
+ "U'O' converts to 24");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'P') == 25,
+ "U'P' converts to 25");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'Q') == 26,
+ "U'Q' converts to 26");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'R') == 27,
+ "U'R' converts to 27");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'S') == 28,
+ "U'S' converts to 28");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'T') == 29,
+ "U'T' converts to 29");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'U') == 30,
+ "U'U' converts to 30");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'V') == 31,
+ "U'V' converts to 31");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'W') == 32,
+ "U'W' converts to 32");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'X') == 33,
+ "U'X' converts to 33");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'Y') == 34,
+ "U'Y' converts to 34");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'Z') == 35,
+ "U'Z' converts to 35");
+
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'a') == 10,
+ "U'a' converts to 10");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'b') == 11,
+ "U'b' converts to 11");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'c') == 12,
+ "U'c' converts to 12");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'd') == 13,
+ "U'd' converts to 13");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'e') == 14,
+ "U'e' converts to 14");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'f') == 15,
+ "U'f' converts to 15");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'g') == 16,
+ "U'g' converts to 16");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'h') == 17,
+ "U'h' converts to 17");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'i') == 18,
+ "U'i' converts to 18");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'j') == 19,
+ "U'j' converts to 19");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'k') == 20,
+ "U'k' converts to 20");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'l') == 21,
+ "U'l' converts to 21");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'm') == 22,
+ "U'm' converts to 22");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'n') == 23,
+ "U'n' converts to 23");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'o') == 24,
+ "U'o' converts to 24");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'p') == 25,
+ "U'p' converts to 25");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'q') == 26,
+ "U'q' converts to 26");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'r') == 27,
+ "U'r' converts to 27");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U's') == 28,
+ "U's' converts to 28");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U't') == 29,
+ "U't' converts to 29");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'u') == 30,
+ "U'u' converts to 30");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'v') == 31,
+ "U'v' converts to 31");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'w') == 32,
+ "U'w' converts to 32");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'x') == 33,
+ "U'x' converts to 33");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'y') == 34,
+ "U'y' converts to 34");
+ MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'z') == 35,
+ "U'z' converts to 35");
+}
+
+static void TestIsAsciiDigit() {
+ // char
+
+ static_assert(!IsAsciiDigit('/'), "'/' isn't an ASCII digit");
+ static_assert('/' == 0x2F, "'/' has value 0x2F");
+
+ static_assert('0' == 0x30, "'0' has value 0x30");
+ static_assert(IsAsciiDigit('0'), "'0' is an ASCII digit");
+ static_assert(IsAsciiDigit('1'), "'1' is an ASCII digit");
+ static_assert(IsAsciiDigit('5'), "'5' is an ASCII digit");
+ static_assert(IsAsciiDigit('8'), "'8' is an ASCII digit");
+ static_assert(IsAsciiDigit('9'), "'9' is an ASCII digit");
+
+ static_assert('9' == 0x39, "'9' has value 0x39");
+ static_assert(':' == 0x3A, "':' has value 0x3A");
+ static_assert(!IsAsciiDigit(':'), "':' isn't an ASCII digit");
+
+ static_assert(!IsAsciiDigit('@'), "'@' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit('A'), "'A' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit('B'), "'B' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit('M'), "'M' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit('Y'), "'Y' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit('Z'), "'Z' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit('['), "'[' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit('`'), "'`' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit('a'), "'a' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit('b'), "'b' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit('m'), "'m' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit('y'), "'y' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit('z'), "'z' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit('{'), "'{' isn't an ASCII digit");
+
+ // char16_t
+
+ static_assert(!IsAsciiDigit(u'/'), "u'/' isn't an ASCII digit");
+ static_assert(u'/' == 0x2F, "u'/' has value 0x2F");
+ static_assert(u'0' == 0x30, "u'0' has value 0x30");
+ static_assert(IsAsciiDigit(u'0'), "u'0' is an ASCII digit");
+ static_assert(IsAsciiDigit(u'1'), "u'1' is an ASCII digit");
+ static_assert(IsAsciiDigit(u'5'), "u'5' is an ASCII digit");
+ static_assert(IsAsciiDigit(u'8'), "u'8' is an ASCII digit");
+ static_assert(IsAsciiDigit(u'9'), "u'9' is an ASCII digit");
+
+ static_assert(u'9' == 0x39, "u'9' has value 0x39");
+ static_assert(u':' == 0x3A, "u':' has value 0x3A");
+ static_assert(!IsAsciiDigit(u':'), "u':' isn't an ASCII digit");
+
+ static_assert(!IsAsciiDigit(u'@'), "u'@' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(u'A'), "u'A' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(u'B'), "u'B' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(u'M'), "u'M' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(u'Y'), "u'Y' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(u'Z'), "u'Z' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(u'['), "u'[' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(u'`'), "u'`' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(u'a'), "u'a' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(u'b'), "u'b' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(u'm'), "u'm' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(u'y'), "u'y' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(u'z'), "u'z' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(u'{'), "u'{' isn't an ASCII digit");
+
+ // char32_t
+
+ static_assert(!IsAsciiDigit(U'/'), "U'/' isn't an ASCII digit");
+ static_assert(U'/' == 0x2F, "U'/' has value 0x2F");
+
+ static_assert(U'0' == 0x30, "U'0' has value 0x30");
+ static_assert(IsAsciiDigit(U'0'), "U'0' is an ASCII digit");
+ static_assert(IsAsciiDigit(U'1'), "U'1' is an ASCII digit");
+ static_assert(IsAsciiDigit(U'5'), "U'5' is an ASCII digit");
+ static_assert(IsAsciiDigit(U'8'), "U'8' is an ASCII digit");
+ static_assert(IsAsciiDigit(U'9'), "U'9' is an ASCII digit");
+
+ static_assert(U'9' == 0x39, "U'9' has value 0x39");
+ static_assert(U':' == 0x3A, "U':' has value 0x3A");
+ static_assert(!IsAsciiDigit(U':'), "U':' isn't an ASCII digit");
+
+ static_assert(!IsAsciiDigit(U'@'), "U'@' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(U'A'), "U'A' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(U'B'), "U'B' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(U'M'), "U'M' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(U'Y'), "U'Y' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(U'Z'), "U'Z' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(U'['), "U'[' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(U'`'), "U'`' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(U'a'), "U'a' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(U'b'), "U'b' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(U'm'), "U'm' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(U'y'), "U'y' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(U'z'), "U'z' isn't an ASCII digit");
+ static_assert(!IsAsciiDigit(U'{'), "U'{' isn't an ASCII digit");
+}
+
+int main() {
+ TestIsAscii();
+ TestIsAsciiNullTerminated();
+ TestIsAsciiAlpha();
+ TestIsAsciiUppercaseAlpha();
+ TestIsAsciiLowercaseAlpha();
+ TestIsAsciiAlphanumeric();
+ TestAsciiAlphanumericToNumber();
+ TestIsAsciiDigit();
+}
diff --git a/mfbt/tests/TestThreadSafeWeakPtr.cpp b/mfbt/tests/TestThreadSafeWeakPtr.cpp
new file mode 100644
index 0000000000..3670d15bc0
--- /dev/null
+++ b/mfbt/tests/TestThreadSafeWeakPtr.cpp
@@ -0,0 +1,127 @@
+/* -*- 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/ThreadSafeWeakPtr.h"
+
+using mozilla::SupportsThreadSafeWeakPtr;
+using mozilla::ThreadSafeWeakPtr;
+
+// To have a class C support weak pointers, inherit from
+// SupportsThreadSafeWeakPtr<C>.
+class C : public SupportsThreadSafeWeakPtr<C> {
+ public:
+ MOZ_DECLARE_REFCOUNTED_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() {}
+};
+
+// Test that declaring a ThreadSafeWeakPtr pointing to an incomplete type
+// builds.
+class Incomplete;
+class D {
+ ThreadSafeWeakPtr<Incomplete> mMember;
+};
+
+int main() {
+ RefPtr<C> c1 = new C;
+ MOZ_RELEASE_ASSERT(c1->mNum == 0);
+
+ // Get weak pointers to c1. The first time,
+ // a reference-counted ThreadSafeWeakReference object is created that
+ // can live beyond the lifetime of 'c1'. The ThreadSafeWeakReference
+ // object will be notified of 'c1's destruction.
+ ThreadSafeWeakPtr<C> w1(c1);
+ {
+ RefPtr<C> s1(w1);
+ // Test a weak pointer for validity before using it.
+ MOZ_RELEASE_ASSERT(s1);
+ MOZ_RELEASE_ASSERT(s1 == c1);
+ s1->mNum = 1;
+ s1->act();
+ }
+
+ // Test taking another ThreadSafeWeakPtr<C> to c1
+ ThreadSafeWeakPtr<C> w2(c1);
+ {
+ RefPtr<C> s2(w2);
+ MOZ_RELEASE_ASSERT(s2);
+ MOZ_RELEASE_ASSERT(s2 == c1);
+ MOZ_RELEASE_ASSERT(w1 == s2);
+ MOZ_RELEASE_ASSERT(s2->mNum == 1);
+ }
+
+ // Test that when a ThreadSafeWeakPtr is destroyed, it does not destroy the
+ // object that it points to, and it does not affect other ThreadSafeWeakPtrs
+ // pointing to the same object (e.g. it does not destroy the
+ // ThreadSafeWeakReference object).
+ {
+ ThreadSafeWeakPtr<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
+ // ThreadSafeWeakPtr'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
+ // ThreadSafeWeakPtr points to
+ RefPtr<C> c2 = new C;
+ c2->mNum = 2;
+ {
+ RefPtr<C> s2(w2);
+ MOZ_RELEASE_ASSERT(s2->mNum == 1); // w2 was pointing to c1
+ }
+ w2 = c2;
+ {
+ RefPtr<C> s2(w2);
+ MOZ_RELEASE_ASSERT(s2);
+ MOZ_RELEASE_ASSERT(s2 == c2);
+ MOZ_RELEASE_ASSERT(s2 != c1);
+ MOZ_RELEASE_ASSERT(w1 != s2);
+ MOZ_RELEASE_ASSERT(s2->mNum == 2);
+ }
+
+ // Destroying the underlying object clears weak pointers to it.
+ // It should not affect pointers that are not currently pointing to it.
+ c1 = nullptr;
+ {
+ RefPtr<C> s1(w1);
+ MOZ_RELEASE_ASSERT(
+ !s1, "Deleting an object should clear ThreadSafeWeakPtr's to it.");
+ MOZ_RELEASE_ASSERT(w1.IsDead(), "The weak pointer is now dead");
+ MOZ_RELEASE_ASSERT(!w1.IsNull(), "The weak pointer isn't null");
+
+ RefPtr<C> s2(w2);
+ MOZ_RELEASE_ASSERT(s2,
+ "Deleting an object should not clear ThreadSafeWeakPtr "
+ "that are not pointing to it.");
+ MOZ_RELEASE_ASSERT(!w2.IsDead(), "The weak pointer isn't dead");
+ MOZ_RELEASE_ASSERT(!w2.IsNull(), "The weak pointer isn't null");
+ }
+
+ c2 = nullptr;
+ {
+ RefPtr<C> s2(w2);
+ MOZ_RELEASE_ASSERT(
+ !s2, "Deleting an object should clear ThreadSafeWeakPtr's to it.");
+ MOZ_RELEASE_ASSERT(w2.IsDead(), "The weak pointer is now dead");
+ MOZ_RELEASE_ASSERT(!w2.IsNull(), "The weak pointer isn't null");
+ }
+}
diff --git a/mfbt/tests/TestTypedEnum.cpp b/mfbt/tests/TestTypedEnum.cpp
new file mode 100644
index 0000000000..cddbb39e0b
--- /dev/null
+++ b/mfbt/tests/TestTypedEnum.cpp
@@ -0,0 +1,502 @@
+/* -*- 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/TypedEnumBits.h"
+
+#include <stdint.h>
+#include <type_traits>
+
+// A rough feature check for is_literal_type. Not very carefully checked.
+// Feel free to amend as needed. is_literal_type was removed in C++20.
+// We leave ANDROID out because it's using stlport which doesn't have
+// std::is_literal_type.
+#if __cplusplus >= 201103L && __cplusplus < 202002L && !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() {
+ static_assert(!std::is_convertible_v<T, bool>, "should not be convertible");
+ static_assert(!std::is_convertible_v<T, int>, "should not be convertible");
+ static_assert(!std::is_convertible_v<T, uint64_t>,
+ "should not be convertible");
+
+ static_assert(!std::is_convertible_v<bool, T>, "should not be convertible");
+ static_assert(!std::is_convertible_v<int, T>, "should not be convertible");
+ static_assert(!std::is_convertible_v<uint64_t, T>,
+ "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.
+{
+ static_assert(std::is_same_v<decltype(aT1 | aT2), decltype(aT1 & aT2)>,
+ "binary ops should have the same result type");
+ static_assert(std::is_same_v<decltype(aT1 | aT2), decltype(aT1 ^ aT2)>,
+ "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);
+
+ // Part 4:
+ // Test short-circuit evaluation.
+ auto Explode = [] {
+ // This function should never be called. Return an arbitrary value.
+ MOZ_RELEASE_ASSERT(false);
+ return false;
+ };
+ if (result) {
+ MOZ_RELEASE_ASSERT(result || Explode());
+ MOZ_RELEASE_ASSERT(!(!result && Explode()));
+ } else {
+ MOZ_RELEASE_ASSERT(!(result && Explode()));
+ MOZ_RELEASE_ASSERT(!result || Explode());
+ }
+}
+
+// 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() {
+ // 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(!std::is_convertible_v<T1, T2>, "should not be convertible");
+ static_assert(!std::is_convertible_v<T1, decltype(T2::A)>,
+ "should not be convertible");
+ static_assert(!std::is_convertible_v<T1, decltype(T2::A | T2::B)>,
+ "should not be convertible");
+
+ static_assert(!std::is_convertible_v<decltype(T1::A), T2>,
+ "should not be convertible");
+ static_assert(!std::is_convertible_v<decltype(T1::A), decltype(T2::A)>,
+ "should not be convertible");
+ static_assert(
+ !std::is_convertible_v<decltype(T1::A), decltype(T2::A | T2::B)>,
+ "should not be convertible");
+
+ static_assert(!std::is_convertible_v<decltype(T1::A | T1::B), T2>,
+ "should not be convertible");
+ static_assert(
+ !std::is_convertible_v<decltype(T1::A | T1::B), decltype(T2::A)>,
+ "should not be convertible");
+ static_assert(
+ !std::is_convertible_v<decltype(T1::A | T1::B), decltype(T2::A | T2::B)>,
+ "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 0000000000..03f9033fe5
--- /dev/null
+++ b/mfbt/tests/TestUniquePtr.cpp
@@ -0,0 +1,609 @@
+/* -*- 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 <stddef.h>
+
+#include <memory> // For unique_ptr
+#include <type_traits>
+#include <utility>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "mozilla/Vector.h"
+
+using mozilla::DefaultDelete;
+using mozilla::MakeUnique;
+using mozilla::UniqueFreePtr;
+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 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() = default;
+ void operator()(int* p) { delete p; }
+ };
+ UniquePtr<Ptr, Deleter> u(new int, Deleter());
+}
+
+static bool TestDefaultFreeGuts() {
+ static_assert(std::is_same_v<NewInt::DeleterType, DefaultDelete<int> >,
+ "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(std::move(n1));
+ CHECK(!n1);
+ CHECK(n1.get() == nullptr);
+ CHECK(n2.get() == p1);
+
+ std::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 = std::move(b1);
+ CHECK(gADestructorCalls == 2);
+ CHECK(gBDestructorCalls == 1);
+
+ UniqueA a3(std::move(a2));
+ a3 = nullptr;
+ CHECK(gADestructorCalls == 3);
+ CHECK(gBDestructorCalls == 2);
+
+ B* bp3 = new B;
+ bp3->mX = 42;
+ UniqueB b2(bp3);
+ UniqueA a4(std::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 = std::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 = std::move(c1);
+ CHECK(gADestructorCalls == 8);
+ CHECK(gBDestructorCalls == 4);
+
+ c1.reset(new B);
+
+ UniqueA a8(std::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() = default;
+
+ 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 = std::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(std::move(id1));
+ CHECK(id1 == nullptr);
+ CHECK(nullptr != id2);
+ CHECK(&id1.get_deleter() == &id2.get_deleter());
+
+ IntDeleterRef id3(std::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 = std::move(b1);
+
+ BDeleterRef b2(new B, delA);
+
+ ADeleterRef a2(std::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 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() = default;
+
+ 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(std::is_same_v<IntArray::DeleterType, DefaultDelete<int[]> >,
+ "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(std::move(n1));
+ CHECK(!n1);
+ CHECK(n1.get() == nullptr);
+ CHECK(n2.get() == p1);
+
+ std::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 = std::move(n2);
+ CHECK(n1.get() == p2);
+ n1 = std::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() = default;
+ Q(const Q&) = default;
+
+ 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;
+}
+
+static bool TestVoid() {
+ // UniquePtr<void> supports all operations except operator*() and
+ // operator->().
+ UniqueFreePtr<void> p1(malloc(1));
+ UniqueFreePtr<void> p2;
+
+ auto x = p1.get();
+ CHECK(x != nullptr);
+ CHECK((std::is_same_v<decltype(x), void*>));
+
+ p2.reset(p1.release());
+ CHECK(p1.get() == nullptr);
+ CHECK(p2.get() != nullptr);
+
+ p1 = std::move(p2);
+ CHECK(p1);
+ CHECK(!p2);
+
+ p1.swap(p2);
+ CHECK(!p1);
+ CHECK(p2);
+
+ p2 = nullptr;
+ CHECK(!p2);
+
+ return true;
+}
+
+static bool TestTempPtrToSetter() {
+ static int sFooRefcount = 0;
+ struct Foo {
+ Foo() { sFooRefcount += 1; }
+
+ ~Foo() { sFooRefcount -= 1; }
+ };
+
+ const auto AllocByOutvar = [](Foo** out) -> bool {
+ *out = new Foo;
+ return true;
+ };
+
+ {
+ UniquePtr<Foo> f;
+ (void)AllocByOutvar(mozilla::TempPtrToSetter(&f));
+ CHECK(sFooRefcount == 1);
+ }
+ CHECK(sFooRefcount == 0);
+
+ {
+ std::unique_ptr<Foo> f;
+ (void)AllocByOutvar(mozilla::TempPtrToSetter(&f));
+ CHECK(sFooRefcount == 1);
+ }
+ CHECK(sFooRefcount == 0);
+
+ 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;
+ }
+ if (!TestVoid()) {
+ return 1;
+ }
+ if (!TestTempPtrToSetter()) {
+ return 1;
+ }
+ return 0;
+}
diff --git a/mfbt/tests/TestUtf8.cpp b/mfbt/tests/TestUtf8.cpp
new file mode 100644
index 0000000000..b3ff9e9ee8
--- /dev/null
+++ b/mfbt/tests/TestUtf8.cpp
@@ -0,0 +1,755 @@
+/* -*- 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/. */
+
+#define MOZ_PRETEND_NO_JSRUST 1
+
+#include "mozilla/Utf8.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/EnumSet.h"
+#include "mozilla/IntegerRange.h"
+#include "mozilla/Span.h"
+
+using mozilla::ArrayLength;
+using mozilla::AsChars;
+using mozilla::DecodeOneUtf8CodePoint;
+using mozilla::EnumSet;
+using mozilla::IntegerRange;
+using mozilla::IsAscii;
+using mozilla::IsUtf8;
+using mozilla::Span;
+using mozilla::Utf8Unit;
+
+// Disable the C++ 2a warning. See bug #1509926
+#if defined(__clang__) && (__clang_major__ >= 6)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wc++2a-compat"
+#endif
+
+static void TestUtf8Unit() {
+ Utf8Unit c('A');
+ MOZ_RELEASE_ASSERT(c.toChar() == 'A');
+ MOZ_RELEASE_ASSERT(c == Utf8Unit('A'));
+ MOZ_RELEASE_ASSERT(c != Utf8Unit('B'));
+ MOZ_RELEASE_ASSERT(c.toUint8() == 0x41);
+
+ unsigned char asUnsigned = 'A';
+ MOZ_RELEASE_ASSERT(c.toUnsignedChar() == asUnsigned);
+ MOZ_RELEASE_ASSERT(Utf8Unit('B').toUnsignedChar() != asUnsigned);
+
+ Utf8Unit first('@');
+ Utf8Unit second('#');
+
+ MOZ_RELEASE_ASSERT(first != second);
+
+ first = second;
+ MOZ_RELEASE_ASSERT(first == second);
+}
+
+template <typename Char>
+struct ToUtf8Units {
+ public:
+ explicit ToUtf8Units(const Char* aStart, const Char* aEnd)
+ : lead(Utf8Unit(aStart[0])), iter(aStart + 1), end(aEnd) {
+ MOZ_RELEASE_ASSERT(!IsAscii(aStart[0]));
+ }
+
+ const Utf8Unit lead;
+ const Char* iter;
+ const Char* const end;
+};
+
+class AssertIfCalled {
+ public:
+ template <typename... Args>
+ void operator()(Args&&... aArgs) {
+ MOZ_RELEASE_ASSERT(false, "AssertIfCalled instance was called");
+ }
+};
+
+// NOTE: For simplicity in treating |aCharN| identically regardless whether it's
+// a string literal or a more-generalized array, we require |aCharN| be
+// null-terminated.
+
+template <typename Char, size_t N>
+static void ExpectValidCodePoint(const Char (&aCharN)[N],
+ char32_t aExpectedCodePoint) {
+ MOZ_RELEASE_ASSERT(aCharN[N - 1] == 0,
+ "array must be null-terminated for |aCharN + N - 1| to "
+ "compute the value of |aIter| as altered by "
+ "DecodeOneUtf8CodePoint");
+
+ ToUtf8Units<Char> simpleUnit(aCharN, aCharN + N - 1);
+ auto simple =
+ DecodeOneUtf8CodePoint(simpleUnit.lead, &simpleUnit.iter, simpleUnit.end);
+ MOZ_RELEASE_ASSERT(simple.isSome());
+ MOZ_RELEASE_ASSERT(*simple == aExpectedCodePoint);
+ MOZ_RELEASE_ASSERT(simpleUnit.iter == simpleUnit.end);
+
+ ToUtf8Units<Char> complexUnit(aCharN, aCharN + N - 1);
+ auto complex = DecodeOneUtf8CodePoint(
+ complexUnit.lead, &complexUnit.iter, complexUnit.end, AssertIfCalled(),
+ AssertIfCalled(), AssertIfCalled(), AssertIfCalled(), AssertIfCalled());
+ MOZ_RELEASE_ASSERT(complex.isSome());
+ MOZ_RELEASE_ASSERT(*complex == aExpectedCodePoint);
+ MOZ_RELEASE_ASSERT(complexUnit.iter == complexUnit.end);
+}
+
+enum class InvalidUtf8Reason {
+ BadLeadUnit,
+ NotEnoughUnits,
+ BadTrailingUnit,
+ BadCodePoint,
+ NotShortestForm,
+};
+
+template <typename Char, size_t N>
+static void ExpectInvalidCodePointHelper(const Char (&aCharN)[N],
+ InvalidUtf8Reason aExpectedReason,
+ uint8_t aExpectedUnitsAvailable,
+ uint8_t aExpectedUnitsNeeded,
+ char32_t aExpectedBadCodePoint,
+ uint8_t aExpectedUnitsObserved) {
+ MOZ_RELEASE_ASSERT(aCharN[N - 1] == 0,
+ "array must be null-terminated for |aCharN + N - 1| to "
+ "compute the value of |aIter| as altered by "
+ "DecodeOneUtf8CodePoint");
+
+ ToUtf8Units<Char> simpleUnit(aCharN, aCharN + N - 1);
+ auto simple =
+ DecodeOneUtf8CodePoint(simpleUnit.lead, &simpleUnit.iter, simpleUnit.end);
+ MOZ_RELEASE_ASSERT(simple.isNothing());
+ MOZ_RELEASE_ASSERT(static_cast<const void*>(simpleUnit.iter) == aCharN);
+
+ EnumSet<InvalidUtf8Reason> reasons;
+ uint8_t unitsAvailable;
+ uint8_t unitsNeeded;
+ char32_t badCodePoint;
+ uint8_t unitsObserved;
+
+ struct OnNotShortestForm {
+ EnumSet<InvalidUtf8Reason>& reasons;
+ char32_t& badCodePoint;
+ uint8_t& unitsObserved;
+
+ void operator()(char32_t aBadCodePoint, uint8_t aUnitsObserved) {
+ reasons += InvalidUtf8Reason::NotShortestForm;
+ badCodePoint = aBadCodePoint;
+ unitsObserved = aUnitsObserved;
+ }
+ };
+
+ ToUtf8Units<Char> complexUnit(aCharN, aCharN + N - 1);
+ auto complex = DecodeOneUtf8CodePoint(
+ complexUnit.lead, &complexUnit.iter, complexUnit.end,
+ [&reasons]() { reasons += InvalidUtf8Reason::BadLeadUnit; },
+ [&reasons, &unitsAvailable, &unitsNeeded](uint8_t aUnitsAvailable,
+ uint8_t aUnitsNeeded) {
+ reasons += InvalidUtf8Reason::NotEnoughUnits;
+ unitsAvailable = aUnitsAvailable;
+ unitsNeeded = aUnitsNeeded;
+ },
+ [&reasons, &unitsObserved](uint8_t aUnitsObserved) {
+ reasons += InvalidUtf8Reason::BadTrailingUnit;
+ unitsObserved = aUnitsObserved;
+ },
+ [&reasons, &badCodePoint, &unitsObserved](char32_t aBadCodePoint,
+ uint8_t aUnitsObserved) {
+ reasons += InvalidUtf8Reason::BadCodePoint;
+ badCodePoint = aBadCodePoint;
+ unitsObserved = aUnitsObserved;
+ },
+ [&reasons, &badCodePoint, &unitsObserved](char32_t aBadCodePoint,
+ uint8_t aUnitsObserved) {
+ reasons += InvalidUtf8Reason::NotShortestForm;
+ badCodePoint = aBadCodePoint;
+ unitsObserved = aUnitsObserved;
+ });
+ MOZ_RELEASE_ASSERT(complex.isNothing());
+ MOZ_RELEASE_ASSERT(static_cast<const void*>(complexUnit.iter) == aCharN);
+
+ bool alreadyIterated = false;
+ for (InvalidUtf8Reason reason : reasons) {
+ MOZ_RELEASE_ASSERT(!alreadyIterated);
+ alreadyIterated = true;
+
+ switch (reason) {
+ case InvalidUtf8Reason::BadLeadUnit:
+ break;
+
+ case InvalidUtf8Reason::NotEnoughUnits:
+ MOZ_RELEASE_ASSERT(unitsAvailable == aExpectedUnitsAvailable);
+ MOZ_RELEASE_ASSERT(unitsNeeded == aExpectedUnitsNeeded);
+ break;
+
+ case InvalidUtf8Reason::BadTrailingUnit:
+ MOZ_RELEASE_ASSERT(unitsObserved == aExpectedUnitsObserved);
+ break;
+
+ case InvalidUtf8Reason::BadCodePoint:
+ MOZ_RELEASE_ASSERT(badCodePoint == aExpectedBadCodePoint);
+ MOZ_RELEASE_ASSERT(unitsObserved == aExpectedUnitsObserved);
+ break;
+
+ case InvalidUtf8Reason::NotShortestForm:
+ MOZ_RELEASE_ASSERT(badCodePoint == aExpectedBadCodePoint);
+ MOZ_RELEASE_ASSERT(unitsObserved == aExpectedUnitsObserved);
+ break;
+ }
+ }
+}
+
+// NOTE: For simplicity in treating |aCharN| identically regardless whether it's
+// a string literal or a more-generalized array, we require |aCharN| be
+// null-terminated in all these functions.
+
+template <typename Char, size_t N>
+static void ExpectBadLeadUnit(const Char (&aCharN)[N]) {
+ ExpectInvalidCodePointHelper(aCharN, InvalidUtf8Reason::BadLeadUnit, 0xFF,
+ 0xFF, 0xFFFFFFFF, 0xFF);
+}
+
+template <typename Char, size_t N>
+static void ExpectNotEnoughUnits(const Char (&aCharN)[N],
+ uint8_t aExpectedUnitsAvailable,
+ uint8_t aExpectedUnitsNeeded) {
+ ExpectInvalidCodePointHelper(aCharN, InvalidUtf8Reason::NotEnoughUnits,
+ aExpectedUnitsAvailable, aExpectedUnitsNeeded,
+ 0xFFFFFFFF, 0xFF);
+}
+
+template <typename Char, size_t N>
+static void ExpectBadTrailingUnit(const Char (&aCharN)[N],
+ uint8_t aExpectedUnitsObserved) {
+ ExpectInvalidCodePointHelper(aCharN, InvalidUtf8Reason::BadTrailingUnit, 0xFF,
+ 0xFF, 0xFFFFFFFF, aExpectedUnitsObserved);
+}
+
+template <typename Char, size_t N>
+static void ExpectNotShortestForm(const Char (&aCharN)[N],
+ char32_t aExpectedBadCodePoint,
+ uint8_t aExpectedUnitsObserved) {
+ ExpectInvalidCodePointHelper(aCharN, InvalidUtf8Reason::NotShortestForm, 0xFF,
+ 0xFF, aExpectedBadCodePoint,
+ aExpectedUnitsObserved);
+}
+
+template <typename Char, size_t N>
+static void ExpectBadCodePoint(const Char (&aCharN)[N],
+ char32_t aExpectedBadCodePoint,
+ uint8_t aExpectedUnitsObserved) {
+ ExpectInvalidCodePointHelper(aCharN, InvalidUtf8Reason::BadCodePoint, 0xFF,
+ 0xFF, aExpectedBadCodePoint,
+ aExpectedUnitsObserved);
+}
+
+static void TestIsUtf8() {
+ // Note we include the U+0000 NULL in this one -- and that's fine.
+ static const char asciiBytes[] = u8"How about a nice game of chess?";
+ MOZ_RELEASE_ASSERT(IsUtf8(Span(asciiBytes, ArrayLength(asciiBytes))));
+
+ static const char endNonAsciiBytes[] = u8"Life is like a 🌯";
+ MOZ_RELEASE_ASSERT(
+ IsUtf8(Span(endNonAsciiBytes, ArrayLength(endNonAsciiBytes) - 1)));
+
+ static const unsigned char badLeading[] = {0x80};
+ MOZ_RELEASE_ASSERT(
+ !IsUtf8(AsChars(Span(badLeading, ArrayLength(badLeading)))));
+
+ // Byte-counts
+
+ // 1
+ static const char oneBytes[] = u8"A"; // U+0041 LATIN CAPITAL LETTER A
+ constexpr size_t oneBytesLen = ArrayLength(oneBytes);
+ static_assert(oneBytesLen == 2, "U+0041 plus nul");
+ MOZ_RELEASE_ASSERT(IsUtf8(Span(oneBytes, oneBytesLen)));
+
+ // 2
+ static const char twoBytes[] = u8"؆"; // U+0606 ARABIC-INDIC CUBE ROOT
+ constexpr size_t twoBytesLen = ArrayLength(twoBytes);
+ static_assert(twoBytesLen == 3, "U+0606 in two bytes plus nul");
+ MOZ_RELEASE_ASSERT(IsUtf8(Span(twoBytes, twoBytesLen)));
+
+ ExpectValidCodePoint(twoBytes, 0x0606);
+
+ // 3
+ static const char threeBytes[] = u8"᨞"; // U+1A1E BUGINESE PALLAWA
+ constexpr size_t threeBytesLen = ArrayLength(threeBytes);
+ static_assert(threeBytesLen == 4, "U+1A1E in three bytes plus nul");
+ MOZ_RELEASE_ASSERT(IsUtf8(Span(threeBytes, threeBytesLen)));
+
+ ExpectValidCodePoint(threeBytes, 0x1A1E);
+
+ // 4
+ static const char fourBytes[] =
+ u8"🁡"; // U+1F061 DOMINO TILE HORIZONTAL-06-06
+ constexpr size_t fourBytesLen = ArrayLength(fourBytes);
+ static_assert(fourBytesLen == 5, "U+1F061 in four bytes plus nul");
+ MOZ_RELEASE_ASSERT(IsUtf8(Span(fourBytes, fourBytesLen)));
+
+ ExpectValidCodePoint(fourBytes, 0x1F061);
+
+ // Max code point
+ static const char maxCodePoint[] = u8"􏿿"; // U+10FFFF
+ constexpr size_t maxCodePointLen = ArrayLength(maxCodePoint);
+ static_assert(maxCodePointLen == 5, "U+10FFFF in four bytes plus nul");
+ MOZ_RELEASE_ASSERT(IsUtf8(Span(maxCodePoint, maxCodePointLen)));
+
+ ExpectValidCodePoint(maxCodePoint, 0x10FFFF);
+
+ // One past max code point
+ static const unsigned char onePastMaxCodePoint[] = {0xF4, 0x90, 0x80, 0x80,
+ 0x0};
+ constexpr size_t onePastMaxCodePointLen = ArrayLength(onePastMaxCodePoint);
+ MOZ_RELEASE_ASSERT(
+ !IsUtf8(AsChars(Span(onePastMaxCodePoint, onePastMaxCodePointLen))));
+
+ ExpectBadCodePoint(onePastMaxCodePoint, 0x110000, 4);
+
+ // Surrogate-related testing
+
+ // (Note that the various code unit sequences here are null-terminated to
+ // simplify life for ExpectValidCodePoint, which presumes null termination.)
+
+ static const unsigned char justBeforeSurrogates[] = {0xED, 0x9F, 0xBF, 0x0};
+ constexpr size_t justBeforeSurrogatesLen =
+ ArrayLength(justBeforeSurrogates) - 1;
+ MOZ_RELEASE_ASSERT(
+ IsUtf8(AsChars(Span(justBeforeSurrogates, justBeforeSurrogatesLen))));
+
+ ExpectValidCodePoint(justBeforeSurrogates, 0xD7FF);
+
+ static const unsigned char leastSurrogate[] = {0xED, 0xA0, 0x80, 0x0};
+ constexpr size_t leastSurrogateLen = ArrayLength(leastSurrogate) - 1;
+ MOZ_RELEASE_ASSERT(!IsUtf8(AsChars(Span(leastSurrogate, leastSurrogateLen))));
+
+ ExpectBadCodePoint(leastSurrogate, 0xD800, 3);
+
+ static const unsigned char arbitraryHighSurrogate[] = {0xED, 0xA2, 0x87, 0x0};
+ constexpr size_t arbitraryHighSurrogateLen =
+ ArrayLength(arbitraryHighSurrogate) - 1;
+ MOZ_RELEASE_ASSERT(!IsUtf8(
+ AsChars(Span(arbitraryHighSurrogate, arbitraryHighSurrogateLen))));
+
+ ExpectBadCodePoint(arbitraryHighSurrogate, 0xD887, 3);
+
+ static const unsigned char arbitraryLowSurrogate[] = {0xED, 0xB7, 0xAF, 0x0};
+ constexpr size_t arbitraryLowSurrogateLen =
+ ArrayLength(arbitraryLowSurrogate) - 1;
+ MOZ_RELEASE_ASSERT(
+ !IsUtf8(AsChars(Span(arbitraryLowSurrogate, arbitraryLowSurrogateLen))));
+
+ ExpectBadCodePoint(arbitraryLowSurrogate, 0xDDEF, 3);
+
+ static const unsigned char greatestSurrogate[] = {0xED, 0xBF, 0xBF, 0x0};
+ constexpr size_t greatestSurrogateLen = ArrayLength(greatestSurrogate) - 1;
+ MOZ_RELEASE_ASSERT(
+ !IsUtf8(AsChars(Span(greatestSurrogate, greatestSurrogateLen))));
+
+ ExpectBadCodePoint(greatestSurrogate, 0xDFFF, 3);
+
+ static const unsigned char justAfterSurrogates[] = {0xEE, 0x80, 0x80, 0x0};
+ constexpr size_t justAfterSurrogatesLen =
+ ArrayLength(justAfterSurrogates) - 1;
+ MOZ_RELEASE_ASSERT(
+ IsUtf8(AsChars(Span(justAfterSurrogates, justAfterSurrogatesLen))));
+
+ ExpectValidCodePoint(justAfterSurrogates, 0xE000);
+}
+
+static void TestDecodeOneValidUtf8CodePoint() {
+ // NOTE: DecodeOneUtf8CodePoint decodes only *non*-ASCII code points that
+ // consist of multiple code units, so there are no ASCII tests below.
+
+ // Length two.
+
+ ExpectValidCodePoint(u8"€", 0x80); // <control>
+ ExpectValidCodePoint(u8"©", 0xA9); // COPYRIGHT SIGN
+ ExpectValidCodePoint(u8"¶", 0xB6); // PILCROW SIGN
+ ExpectValidCodePoint(u8"¾", 0xBE); // VULGAR FRACTION THREE QUARTERS
+ ExpectValidCodePoint(u8"÷", 0xF7); // DIVISION SIGN
+ ExpectValidCodePoint(u8"ÿ", 0xFF); // LATIN SMALL LETTER Y WITH DIAERESIS
+ ExpectValidCodePoint(u8"Ā", 0x100); // LATIN CAPITAL LETTER A WITH MACRON
+ ExpectValidCodePoint(u8"IJ", 0x132); // LATIN CAPITAL LETTER LIGATURE IJ
+ ExpectValidCodePoint(u8"ͼ", 0x37C); // GREEK SMALL DOTTED LUNATE SIGMA SYMBOL
+ ExpectValidCodePoint(u8"Ӝ",
+ 0x4DC); // CYRILLIC CAPITAL LETTER ZHE WITTH DIAERESIS
+ ExpectValidCodePoint(u8"۩", 0x6E9); // ARABIC PLACE OF SAJDAH
+ ExpectValidCodePoint(u8"߿", 0x7FF); // <not assigned>
+
+ // Length three.
+
+ ExpectValidCodePoint(u8"ࠀ", 0x800); // SAMARITAN LETTER ALAF
+ ExpectValidCodePoint(u8"ࡁ", 0x841); // MANDAIC LETTER AB
+ ExpectValidCodePoint(u8"ࣿ", 0x8FF); // ARABIC MARK SIDEWAYS NOON GHUNNA
+ ExpectValidCodePoint(u8"ஆ", 0xB86); // TAMIL LETTER AA
+ ExpectValidCodePoint(u8"༃",
+ 0xF03); // TIBETAN MARK GTER YIG MGO -UM GTER TSHEG MA
+ ExpectValidCodePoint(
+ u8"࿉",
+ 0xFC9); // TIBETAN SYMBOL NOR BU (but on my system it really looks like
+ // SOFT-SERVE ICE CREAM FROM ABOVE THE PLANE if you ask me)
+ ExpectValidCodePoint(u8"ဪ", 0x102A); // MYANMAR LETTER AU
+ ExpectValidCodePoint(u8"ᚏ", 0x168F); // OGHAM LETTER RUIS
+ ExpectValidCodePoint("\xE2\x80\xA8", 0x2028); // (the hated) LINE SEPARATOR
+ ExpectValidCodePoint("\xE2\x80\xA9",
+ 0x2029); // (the hated) PARAGRAPH SEPARATOR
+ ExpectValidCodePoint(u8"☬", 0x262C); // ADI SHAKTI
+ ExpectValidCodePoint(u8"㊮", 0x32AE); // CIRCLED IDEOGRAPH RESOURCE
+ ExpectValidCodePoint(u8"㏖", 0x33D6); // SQUARE MOL
+ ExpectValidCodePoint(u8"ꔄ", 0xA504); // VAI SYLLABLE WEEN
+ ExpectValidCodePoint(u8"ퟕ", 0xD7D5); // HANGUL JONGSEONG RIEUL-SSANGKIYEOK
+ ExpectValidCodePoint(u8"퟿", 0xD7FF); // <not assigned>
+ ExpectValidCodePoint(u8"", 0xE000); // <Private Use>
+ ExpectValidCodePoint(u8"鱗", 0xF9F2); // CJK COMPATIBILITY IDEOGRAPH-F9F
+ ExpectValidCodePoint(
+ u8"﷽", 0xFDFD); // ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHHHEEEEM
+ ExpectValidCodePoint(u8"￿", 0xFFFF); // <not assigned>
+
+ // Length four.
+ ExpectValidCodePoint(u8"𐀀", 0x10000); // LINEAR B SYLLABLE B008 A
+ ExpectValidCodePoint(u8"𔑀", 0x14440); // ANATOLIAN HIEROGLYPH A058
+ ExpectValidCodePoint(u8"𝛗", 0x1D6D7); // MATHEMATICAL BOLD SMALL PHI
+ ExpectValidCodePoint(u8"💩", 0x1F4A9); // PILE OF POO
+ ExpectValidCodePoint(u8"🔫", 0x1F52B); // PISTOL
+ ExpectValidCodePoint(u8"🥌", 0x1F94C); // CURLING STONE
+ ExpectValidCodePoint(u8"🥏", 0x1F94F); // FLYING DISC
+ ExpectValidCodePoint(u8"𠍆", 0x20346); // CJK UNIFIED IDEOGRAPH-20346
+ ExpectValidCodePoint(u8"𡠺", 0x2183A); // CJK UNIFIED IDEOGRAPH-2183A
+ ExpectValidCodePoint(u8"񁟶", 0x417F6); // <not assigned>
+ ExpectValidCodePoint(u8"񾠶", 0x7E836); // <not assigned>
+ ExpectValidCodePoint(u8"󾽧", 0xFEF67); // <Plane 15 Private Use>
+ ExpectValidCodePoint(u8"􏿿", 0x10FFFF); //
+}
+
+static void TestDecodeBadLeadUnit() {
+ // These tests are actually exhaustive.
+
+ unsigned char badLead[] = {'\0', '\0'};
+
+ for (uint8_t lead : IntegerRange(0b1000'0000, 0b1100'0000)) {
+ badLead[0] = lead;
+ ExpectBadLeadUnit(badLead);
+ }
+
+ {
+ uint8_t lead = 0b1111'1000;
+ do {
+ badLead[0] = lead;
+ ExpectBadLeadUnit(badLead);
+ if (lead == 0b1111'1111) {
+ break;
+ }
+
+ lead++;
+ } while (true);
+ }
+}
+
+static void TestTooFewOrBadTrailingUnits() {
+ // Lead unit indicates a two-byte code point.
+
+ char truncatedTwo[] = {'\0', '\0'};
+ char badTrailTwo[] = {'\0', '\0', '\0'};
+
+ for (uint8_t lead : IntegerRange(0b1100'0000, 0b1110'0000)) {
+ truncatedTwo[0] = lead;
+ ExpectNotEnoughUnits(truncatedTwo, 1, 2);
+
+ badTrailTwo[0] = lead;
+ for (uint8_t trail : IntegerRange(0b0000'0000, 0b1000'0000)) {
+ badTrailTwo[1] = trail;
+ ExpectBadTrailingUnit(badTrailTwo, 2);
+ }
+
+ for (uint8_t trail : IntegerRange(0b1100'0000, 0b1111'1111)) {
+ badTrailTwo[1] = trail;
+ ExpectBadTrailingUnit(badTrailTwo, 2);
+ }
+ }
+
+ // Lead unit indicates a three-byte code point.
+
+ char truncatedThreeOne[] = {'\0', '\0'};
+ char truncatedThreeTwo[] = {'\0', '\0', '\0'};
+ unsigned char badTrailThree[] = {'\0', '\0', '\0', '\0'};
+
+ for (uint8_t lead : IntegerRange(0b1110'0000, 0b1111'0000)) {
+ truncatedThreeOne[0] = lead;
+ ExpectNotEnoughUnits(truncatedThreeOne, 1, 3);
+
+ truncatedThreeTwo[0] = lead;
+ ExpectNotEnoughUnits(truncatedThreeTwo, 2, 3);
+
+ badTrailThree[0] = lead;
+ badTrailThree[2] = 0b1011'1111; // make valid to test overreads
+ for (uint8_t mid : IntegerRange(0b0000'0000, 0b1000'0000)) {
+ badTrailThree[1] = mid;
+ ExpectBadTrailingUnit(badTrailThree, 2);
+ }
+ {
+ uint8_t mid = 0b1100'0000;
+ do {
+ badTrailThree[1] = mid;
+ ExpectBadTrailingUnit(badTrailThree, 2);
+ if (mid == 0b1111'1111) {
+ break;
+ }
+
+ mid++;
+ } while (true);
+ }
+
+ badTrailThree[1] = 0b1011'1111;
+ for (uint8_t last : IntegerRange(0b0000'0000, 0b1000'0000)) {
+ badTrailThree[2] = last;
+ ExpectBadTrailingUnit(badTrailThree, 3);
+ }
+ {
+ uint8_t last = 0b1100'0000;
+ do {
+ badTrailThree[2] = last;
+ ExpectBadTrailingUnit(badTrailThree, 3);
+ if (last == 0b1111'1111) {
+ break;
+ }
+
+ last++;
+ } while (true);
+ }
+ }
+
+ // Lead unit indicates a four-byte code point.
+
+ char truncatedFourOne[] = {'\0', '\0'};
+ char truncatedFourTwo[] = {'\0', '\0', '\0'};
+ char truncatedFourThree[] = {'\0', '\0', '\0', '\0'};
+
+ unsigned char badTrailFour[] = {'\0', '\0', '\0', '\0', '\0'};
+
+ for (uint8_t lead : IntegerRange(0b1111'0000, 0b1111'1000)) {
+ truncatedFourOne[0] = lead;
+ ExpectNotEnoughUnits(truncatedFourOne, 1, 4);
+
+ truncatedFourTwo[0] = lead;
+ ExpectNotEnoughUnits(truncatedFourTwo, 2, 4);
+
+ truncatedFourThree[0] = lead;
+ ExpectNotEnoughUnits(truncatedFourThree, 3, 4);
+
+ badTrailFour[0] = lead;
+ badTrailFour[2] = badTrailFour[3] = 0b1011'1111; // test for overreads
+ for (uint8_t second : IntegerRange(0b0000'0000, 0b1000'0000)) {
+ badTrailFour[1] = second;
+ ExpectBadTrailingUnit(badTrailFour, 2);
+ }
+ {
+ uint8_t second = 0b1100'0000;
+ do {
+ badTrailFour[1] = second;
+ ExpectBadTrailingUnit(badTrailFour, 2);
+ if (second == 0b1111'1111) {
+ break;
+ }
+
+ second++;
+ } while (true);
+ }
+
+ badTrailFour[1] = badTrailFour[3] = 0b1011'1111; // test for overreads
+ for (uint8_t third : IntegerRange(0b0000'0000, 0b1000'0000)) {
+ badTrailFour[2] = third;
+ ExpectBadTrailingUnit(badTrailFour, 3);
+ }
+ {
+ uint8_t third = 0b1100'0000;
+ do {
+ badTrailFour[2] = third;
+ ExpectBadTrailingUnit(badTrailFour, 3);
+ if (third == 0b1111'1111) {
+ break;
+ }
+
+ third++;
+ } while (true);
+ }
+
+ badTrailFour[2] = 0b1011'1111;
+ for (uint8_t fourth : IntegerRange(0b0000'0000, 0b1000'0000)) {
+ badTrailFour[3] = fourth;
+ ExpectBadTrailingUnit(badTrailFour, 4);
+ }
+ {
+ uint8_t fourth = 0b1100'0000;
+ do {
+ badTrailFour[3] = fourth;
+ ExpectBadTrailingUnit(badTrailFour, 4);
+ if (fourth == 0b1111'1111) {
+ break;
+ }
+
+ fourth++;
+ } while (true);
+ }
+ }
+}
+
+static void TestBadSurrogate() {
+ // These tests are actually exhaustive.
+
+ ExpectValidCodePoint("\xED\x9F\xBF", 0xD7FF); // last before surrogates
+ ExpectValidCodePoint("\xEE\x80\x80", 0xE000); // first after surrogates
+
+ // First invalid surrogate encoding is { 0xED, 0xA0, 0x80 }. Last invalid
+ // surrogate encoding is { 0xED, 0xBF, 0xBF }.
+
+ char badSurrogate[] = {'\xED', '\0', '\0', '\0'};
+
+ for (char32_t c = 0xD800; c < 0xE000; c++) {
+ badSurrogate[1] = 0b1000'0000 ^ ((c & 0b1111'1100'0000) >> 6);
+ badSurrogate[2] = 0b1000'0000 ^ ((c & 0b0000'0011'1111));
+
+ ExpectBadCodePoint(badSurrogate, c, 3);
+ }
+}
+
+static void TestBadTooBig() {
+ // These tests are actually exhaustive.
+
+ ExpectValidCodePoint("\xF4\x8F\xBF\xBF", 0x10'FFFF); // last code point
+
+ // Four-byte code points are
+ //
+ // 0b1111'0xxx 0b10xx'xxxx 0b10xx'xxxx 0b10xx'xxxx
+ //
+ // with 3 + 6 + 6 + 6 == 21 unconstrained bytes, so the structurally
+ // representable limit (exclusive) is 2**21 - 1 == 2097152.
+
+ char tooLargeCodePoint[] = {'\0', '\0', '\0', '\0', '\0'};
+
+ for (char32_t c = 0x11'0000; c < (1 << 21); c++) {
+ tooLargeCodePoint[0] =
+ 0b1111'0000 ^ ((c & 0b1'1100'0000'0000'0000'0000) >> 18);
+ tooLargeCodePoint[1] =
+ 0b1000'0000 ^ ((c & 0b0'0011'1111'0000'0000'0000) >> 12);
+ tooLargeCodePoint[2] =
+ 0b1000'0000 ^ ((c & 0b0'0000'0000'1111'1100'0000) >> 6);
+ tooLargeCodePoint[3] = 0b1000'0000 ^ ((c & 0b0'0000'0000'0000'0011'1111));
+
+ ExpectBadCodePoint(tooLargeCodePoint, c, 4);
+ }
+}
+
+static void TestBadCodePoint() {
+ TestBadSurrogate();
+ TestBadTooBig();
+}
+
+static void TestNotShortestForm() {
+ {
+ // One-byte in two-byte.
+
+ char oneInTwo[] = {'\0', '\0', '\0'};
+
+ for (char32_t c = '\0'; c < 0x80; c++) {
+ oneInTwo[0] = 0b1100'0000 ^ ((c & 0b0111'1100'0000) >> 6);
+ oneInTwo[1] = 0b1000'0000 ^ ((c & 0b0000'0011'1111));
+
+ ExpectNotShortestForm(oneInTwo, c, 2);
+ }
+
+ // One-byte in three-byte.
+
+ char oneInThree[] = {'\0', '\0', '\0', '\0'};
+
+ for (char32_t c = '\0'; c < 0x80; c++) {
+ oneInThree[0] = 0b1110'0000 ^ ((c & 0b1111'0000'0000'0000) >> 12);
+ oneInThree[1] = 0b1000'0000 ^ ((c & 0b0000'1111'1100'0000) >> 6);
+ oneInThree[2] = 0b1000'0000 ^ ((c & 0b0000'0000'0011'1111));
+
+ ExpectNotShortestForm(oneInThree, c, 3);
+ }
+
+ // One-byte in four-byte.
+
+ char oneInFour[] = {'\0', '\0', '\0', '\0', '\0'};
+
+ for (char32_t c = '\0'; c < 0x80; c++) {
+ oneInFour[0] = 0b1111'0000 ^ ((c & 0b1'1100'0000'0000'0000'0000) >> 18);
+ oneInFour[1] = 0b1000'0000 ^ ((c & 0b0'0011'1111'0000'0000'0000) >> 12);
+ oneInFour[2] = 0b1000'0000 ^ ((c & 0b0'0000'0000'1111'1100'0000) >> 6);
+ oneInFour[3] = 0b1000'0000 ^ ((c & 0b0'0000'0000'0000'0011'1111));
+
+ ExpectNotShortestForm(oneInFour, c, 4);
+ }
+ }
+
+ {
+ // Two-byte in three-byte.
+
+ char twoInThree[] = {'\0', '\0', '\0', '\0'};
+
+ for (char32_t c = 0x80; c < 0x800; c++) {
+ twoInThree[0] = 0b1110'0000 ^ ((c & 0b1111'0000'0000'0000) >> 12);
+ twoInThree[1] = 0b1000'0000 ^ ((c & 0b0000'1111'1100'0000) >> 6);
+ twoInThree[2] = 0b1000'0000 ^ ((c & 0b0000'0000'0011'1111));
+
+ ExpectNotShortestForm(twoInThree, c, 3);
+ }
+
+ // Two-byte in four-byte.
+
+ char twoInFour[] = {'\0', '\0', '\0', '\0', '\0'};
+
+ for (char32_t c = 0x80; c < 0x800; c++) {
+ twoInFour[0] = 0b1111'0000 ^ ((c & 0b1'1100'0000'0000'0000'0000) >> 18);
+ twoInFour[1] = 0b1000'0000 ^ ((c & 0b0'0011'1111'0000'0000'0000) >> 12);
+ twoInFour[2] = 0b1000'0000 ^ ((c & 0b0'0000'0000'1111'1100'0000) >> 6);
+ twoInFour[3] = 0b1000'0000 ^ ((c & 0b0'0000'0000'0000'0011'1111));
+
+ ExpectNotShortestForm(twoInFour, c, 4);
+ }
+ }
+
+ {
+ // Three-byte in four-byte.
+
+ char threeInFour[] = {'\0', '\0', '\0', '\0', '\0'};
+
+ for (char32_t c = 0x800; c < 0x1'0000; c++) {
+ threeInFour[0] = 0b1111'0000 ^ ((c & 0b1'1100'0000'0000'0000'0000) >> 18);
+ threeInFour[1] = 0b1000'0000 ^ ((c & 0b0'0011'1111'0000'0000'0000) >> 12);
+ threeInFour[2] = 0b1000'0000 ^ ((c & 0b0'0000'0000'1111'1100'0000) >> 6);
+ threeInFour[3] = 0b1000'0000 ^ ((c & 0b0'0000'0000'0000'0011'1111));
+
+ ExpectNotShortestForm(threeInFour, c, 4);
+ }
+ }
+}
+
+static void TestDecodeOneInvalidUtf8CodePoint() {
+ TestDecodeBadLeadUnit();
+ TestTooFewOrBadTrailingUnits();
+ TestBadCodePoint();
+ TestNotShortestForm();
+}
+
+static void TestDecodeOneUtf8CodePoint() {
+ TestDecodeOneValidUtf8CodePoint();
+ TestDecodeOneInvalidUtf8CodePoint();
+}
+
+int main() {
+ TestUtf8Unit();
+ TestIsUtf8();
+ TestDecodeOneUtf8CodePoint();
+ return 0;
+}
+
+#if defined(__clang__) && (__clang_major__ >= 6)
+# pragma clang diagnostic pop
+#endif
diff --git a/mfbt/tests/TestVariant.cpp b/mfbt/tests/TestVariant.cpp
new file mode 100644
index 0000000000..552be723b8
--- /dev/null
+++ b/mfbt/tests/TestVariant.cpp
@@ -0,0 +1,1153 @@
+/* -*- 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 <type_traits>
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Variant.h"
+
+#include <tuple>
+
+using mozilla::MakeUnique;
+using mozilla::UniquePtr;
+using mozilla::Variant;
+
+struct Destroyer {
+ static int destroyedCount;
+ ~Destroyer() { destroyedCount++; }
+};
+
+int Destroyer::destroyedCount = 0;
+
+static void testDetails() {
+ printf("testDetails\n");
+
+ using mozilla::detail::Nth;
+
+ // Test Nth with a list of 1 item.
+ static_assert(std::is_same_v<typename Nth<0, int>::Type, int>,
+ "Nth<0, int>::Type should be int");
+
+ // Test Nth with a list of more than 1 item.
+ static_assert(std::is_same_v<typename Nth<0, int, char>::Type, int>,
+ "Nth<0, int, char>::Type should be int");
+ static_assert(std::is_same_v<typename Nth<1, int, char>::Type, char>,
+ "Nth<1, int, char>::Type should be char");
+
+ using mozilla::detail::SelectVariantType;
+
+ // SelectVariantType for zero items (shouldn't happen, but `count` should
+ // still work ok.)
+ static_assert(SelectVariantType<int, char>::count == 0,
+ "SelectVariantType<int, char>::count should be 0");
+
+ // SelectVariantType for 1 type, for all combinations from/to T, const T,
+ // const T&, T&&
+ // - type to type
+ static_assert(std::is_same_v<typename SelectVariantType<int, int>::Type, int>,
+ "SelectVariantType<int, int>::Type should be int");
+ static_assert(SelectVariantType<int, int>::count == 1,
+ "SelectVariantType<int, int>::count should be 1");
+
+ // - type to const type
+ static_assert(std::is_same_v<typename SelectVariantType<int, const int>::Type,
+ const int>,
+ "SelectVariantType<int, const int>::Type should be const int");
+ static_assert(SelectVariantType<int, const int>::count == 1,
+ "SelectVariantType<int, const int>::count should be 1");
+
+ // - type to const type&
+ static_assert(
+ std::is_same_v<typename SelectVariantType<int, const int&>::Type,
+ const int&>,
+ "SelectVariantType<int, const int&>::Type should be const int&");
+ static_assert(SelectVariantType<int, const int&>::count == 1,
+ "SelectVariantType<int, const int&>::count should be 1");
+
+ // - type to type&&
+ static_assert(
+ std::is_same_v<typename SelectVariantType<int, int&&>::Type, int&&>,
+ "SelectVariantType<int, int&&>::Type should be int&&");
+ static_assert(SelectVariantType<int, int&&>::count == 1,
+ "SelectVariantType<int, int&&>::count should be 1");
+
+ // - const type to type
+ static_assert(
+ std::is_same_v<typename SelectVariantType<const int, int>::Type, int>,
+ "SelectVariantType<const int, int>::Type should be int");
+ static_assert(SelectVariantType<const int, int>::count == 1,
+ "SelectVariantType<const int, int>::count should be 1");
+
+ // - const type to const type
+ static_assert(
+ std::is_same_v<typename SelectVariantType<const int, const int>::Type,
+ const int>,
+ "SelectVariantType<const int, const int>::Type should be const int");
+ static_assert(SelectVariantType<const int, const int>::count == 1,
+ "SelectVariantType<const int, const int>::count should be 1");
+
+ // - const type to const type&
+ static_assert(
+ std::is_same_v<typename SelectVariantType<const int, const int&>::Type,
+ const int&>,
+ "SelectVariantType<const int, const int&>::Type should be const int&");
+ static_assert(SelectVariantType<const int, const int&>::count == 1,
+ "SelectVariantType<const int, const int&>::count should be 1");
+
+ // - const type to type&&
+ static_assert(
+ std::is_same_v<typename SelectVariantType<const int, int&&>::Type, int&&>,
+ "SelectVariantType<const int, int&&>::Type should be int&&");
+ static_assert(SelectVariantType<const int, int&&>::count == 1,
+ "SelectVariantType<const int, int&&>::count should be 1");
+
+ // - const type& to type
+ static_assert(
+ std::is_same_v<typename SelectVariantType<const int&, int>::Type, int>,
+ "SelectVariantType<const int&, int>::Type should be int");
+ static_assert(SelectVariantType<const int&, int>::count == 1,
+ "SelectVariantType<const int&, int>::count should be 1");
+
+ // - const type& to const type
+ static_assert(
+ std::is_same_v<typename SelectVariantType<const int&, const int>::Type,
+ const int>,
+ "SelectVariantType<const int&, const int>::Type should be const int");
+ static_assert(SelectVariantType<const int&, const int>::count == 1,
+ "SelectVariantType<const int&, const int>::count should be 1");
+
+ // - const type& to const type&
+ static_assert(
+ std::is_same_v<typename SelectVariantType<const int&, const int&>::Type,
+ const int&>,
+ "SelectVariantType<const int&, const int&>::Type should be const int&");
+ static_assert(SelectVariantType<const int&, const int&>::count == 1,
+ "SelectVariantType<const int&, const int&>::count should be 1");
+
+ // - const type& to type&&
+ static_assert(
+ std::is_same_v<typename SelectVariantType<const int&, int&&>::Type,
+ int&&>,
+ "SelectVariantType<const int&, int&&>::Type should be int&&");
+ static_assert(SelectVariantType<const int&, int&&>::count == 1,
+ "SelectVariantType<const int&, int&&>::count should be 1");
+
+ // - type&& to type
+ static_assert(
+ std::is_same_v<typename SelectVariantType<int&&, int>::Type, int>,
+ "SelectVariantType<int&&, int>::Type should be int");
+ static_assert(SelectVariantType<int&&, int>::count == 1,
+ "SelectVariantType<int&&, int>::count should be 1");
+
+ // - type&& to const type
+ static_assert(
+ std::is_same_v<typename SelectVariantType<int&&, const int>::Type,
+ const int>,
+ "SelectVariantType<int&&, const int>::Type should be const int");
+ static_assert(SelectVariantType<int&&, const int>::count == 1,
+ "SelectVariantType<int&&, const int>::count should be 1");
+
+ // - type&& to const type&
+ static_assert(
+ std::is_same_v<typename SelectVariantType<int&&, const int&>::Type,
+ const int&>,
+ "SelectVariantType<int&&, const int&>::Type should be const int&");
+ static_assert(SelectVariantType<int&&, const int&>::count == 1,
+ "SelectVariantType<int&&, const int&>::count should be 1");
+
+ // - type&& to type&&
+ static_assert(
+ std::is_same_v<typename SelectVariantType<int&&, int&&>::Type, int&&>,
+ "SelectVariantType<int&&, int&&>::Type should be int&&");
+ static_assert(SelectVariantType<int&&, int&&>::count == 1,
+ "SelectVariantType<int&&, int&&>::count should be 1");
+
+ // SelectVariantType for two different types.
+ // (Don't test all combinations, trust that the above tests are sufficient.)
+ static_assert(
+ std::is_same_v<typename SelectVariantType<int, int, char>::Type, int>,
+ "SelectVariantType<int, int, char>::Type should be int");
+ static_assert(SelectVariantType<int, int, char>::count == 1,
+ "SelectVariantType<int, int, char>::count should be 1");
+ static_assert(
+ std::is_same_v<typename SelectVariantType<char, int, char>::Type, char>,
+ "SelectVariantType<char, int, char>::Type should be char");
+ static_assert(SelectVariantType<char, int, char>::count == 1,
+ "SelectVariantType<char, int, char>::count should be 1");
+
+ // SelectVariantType for two identical types.
+ static_assert(
+ std::is_same_v<typename SelectVariantType<int, int, int>::Type, int>,
+ "SelectVariantType<int, int, int>::Type should be int");
+ static_assert(SelectVariantType<int, int, int>::count == 2,
+ "SelectVariantType<int, int, int>::count should be 2");
+
+ // SelectVariantType for two identical types, with others around.
+ static_assert(
+ std::is_same_v<typename SelectVariantType<int, char, int, int>::Type,
+ int>,
+ "SelectVariantType<int, char, int, int>::Type should be int");
+ static_assert(SelectVariantType<int, char, int, int>::count == 2,
+ "SelectVariantType<int, char, int, int>::count should be 2");
+
+ static_assert(
+ std::is_same_v<typename SelectVariantType<int, int, char, int>::Type,
+ int>,
+ "SelectVariantType<int, int, char, int>::Type should be int");
+ static_assert(SelectVariantType<int, int, char, int>::count == 2,
+ "SelectVariantType<int, int, char, int>::count should be 2");
+
+ static_assert(
+ std::is_same_v<typename SelectVariantType<int, int, int, char>::Type,
+ int>,
+ "SelectVariantType<int, int, int, char>::Type should be int");
+ static_assert(SelectVariantType<int, int, int, char>::count == 2,
+ "SelectVariantType<int, int, int, char>::count should be 2");
+
+ static_assert(
+ std::is_same_v<
+ typename SelectVariantType<int, char, int, char, int, char>::Type,
+ int>,
+ "SelectVariantType<int, char, int, char, int, char>::Type should be int");
+ static_assert(
+ SelectVariantType<int, char, int, char, int, char>::count == 2,
+ "SelectVariantType<int, char, int, char, int, char>::count should be 2");
+
+ // SelectVariantType for two identically-selectable types (first one wins!).
+ static_assert(
+ std::is_same_v<typename SelectVariantType<int, int, const int>::Type,
+ int>,
+ "SelectVariantType<int, int, const int>::Type should be int");
+ static_assert(SelectVariantType<int, int, const int>::count == 2,
+ "SelectVariantType<int, int, const int>::count should be 2");
+ static_assert(
+ std::is_same_v<typename SelectVariantType<int, const int, int>::Type,
+ const int>,
+ "SelectVariantType<int, const int, int>::Type should be const int");
+ static_assert(SelectVariantType<int, const int, int>::count == 2,
+ "SelectVariantType<int, const int, int>::count should be 2");
+ static_assert(
+ std::is_same_v<typename SelectVariantType<int, const int, int&&>::Type,
+ const int>,
+ "SelectVariantType<int, const int, int&&>::Type should be const int");
+ static_assert(SelectVariantType<int, const int, int&&>::count == 2,
+ "SelectVariantType<int, const int, int&&>::count should be 2");
+}
+
+static void testSimple() {
+ printf("testSimple\n");
+ using V = Variant<uint32_t, uint64_t>;
+
+ // Non-const lvalue.
+ V 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);
+
+ MOZ_RELEASE_ASSERT(v.is<1>());
+ MOZ_RELEASE_ASSERT(!v.is<0>());
+ static_assert(std::is_same_v<decltype(v.as<1>()), uint64_t&>,
+ "v.as<1>() should return a uint64_t&");
+ MOZ_RELEASE_ASSERT(v.as<1>() == 1);
+
+ // Const lvalue.
+ const V& cv = v;
+ MOZ_RELEASE_ASSERT(cv.is<uint64_t>());
+ MOZ_RELEASE_ASSERT(!cv.is<uint32_t>());
+ MOZ_RELEASE_ASSERT(cv.as<uint64_t>() == 1);
+
+ MOZ_RELEASE_ASSERT(cv.is<1>());
+ MOZ_RELEASE_ASSERT(!cv.is<0>());
+ static_assert(std::is_same_v<decltype(cv.as<1>()), const uint64_t&>,
+ "cv.as<1>() should return a const uint64_t&");
+ MOZ_RELEASE_ASSERT(cv.as<1>() == 1);
+
+ // Non-const rvalue, using a function to create a temporary.
+ auto MakeV = []() { return V(uint64_t(1)); };
+ MOZ_RELEASE_ASSERT(MakeV().is<uint64_t>());
+ MOZ_RELEASE_ASSERT(!MakeV().is<uint32_t>());
+ MOZ_RELEASE_ASSERT(MakeV().as<uint64_t>() == 1);
+
+ MOZ_RELEASE_ASSERT(MakeV().is<1>());
+ MOZ_RELEASE_ASSERT(!MakeV().is<0>());
+ static_assert(std::is_same_v<decltype(MakeV().as<1>()), uint64_t&&>,
+ "MakeV().as<1>() should return a uint64_t&&");
+ MOZ_RELEASE_ASSERT(MakeV().as<1>() == 1);
+
+ // Const rvalue, using a function to create a temporary.
+ auto MakeCV = []() -> const V { return V(uint64_t(1)); };
+ MOZ_RELEASE_ASSERT(MakeCV().is<uint64_t>());
+ MOZ_RELEASE_ASSERT(!MakeCV().is<uint32_t>());
+ MOZ_RELEASE_ASSERT(MakeCV().as<uint64_t>() == 1);
+
+ MOZ_RELEASE_ASSERT(MakeCV().is<1>());
+ MOZ_RELEASE_ASSERT(!MakeCV().is<0>());
+ static_assert(std::is_same_v<decltype(MakeCV().as<1>()), const uint64_t&&>,
+ "MakeCV().as<1>() should return a const uint64_t&&");
+ MOZ_RELEASE_ASSERT(MakeCV().as<1>() == 1);
+}
+
+static void testDuplicate() {
+ printf("testDuplicate\n");
+ Variant<uint32_t, uint64_t, uint32_t> v(uint64_t(1));
+ MOZ_RELEASE_ASSERT(v.is<uint64_t>());
+ MOZ_RELEASE_ASSERT(v.as<uint64_t>() == 1);
+ // Note: uint32_t is not unique, so `v.is<uint32_t>()` is not allowed.
+
+ MOZ_RELEASE_ASSERT(v.is<1>());
+ MOZ_RELEASE_ASSERT(!v.is<0>());
+ MOZ_RELEASE_ASSERT(!v.is<2>());
+ static_assert(std::is_same_v<decltype(v.as<0>()), uint32_t&>,
+ "as<0>() should return a uint64_t");
+ static_assert(std::is_same_v<decltype(v.as<1>()), uint64_t&>,
+ "as<1>() should return a uint64_t");
+ static_assert(std::is_same_v<decltype(v.as<2>()), uint32_t&>,
+ "as<2>() should return a uint64_t");
+ MOZ_RELEASE_ASSERT(v.as<1>() == 1);
+ MOZ_RELEASE_ASSERT(v.extract<1>() == 1);
+}
+
+static void testConstructionWithVariantType() {
+ Variant<uint32_t, uint64_t, uint32_t> v(mozilla::VariantType<uint64_t>{}, 3);
+ MOZ_RELEASE_ASSERT(v.is<uint64_t>());
+ // MOZ_RELEASE_ASSERT(!v.is<uint32_t>()); // uint32_t is not unique!
+ MOZ_RELEASE_ASSERT(v.as<uint64_t>() == 3);
+}
+
+static void testConstructionWithVariantIndex() {
+ Variant<uint32_t, uint64_t, uint32_t> v(mozilla::VariantIndex<2>{}, 2);
+ MOZ_RELEASE_ASSERT(!v.is<uint64_t>());
+ // Note: uint32_t is not unique, so `v.is<uint32_t>()` is not allowed.
+
+ MOZ_RELEASE_ASSERT(!v.is<1>());
+ MOZ_RELEASE_ASSERT(!v.is<0>());
+ MOZ_RELEASE_ASSERT(v.is<2>());
+ MOZ_RELEASE_ASSERT(v.as<2>() == 2);
+ MOZ_RELEASE_ASSERT(v.extract<2>() == 2);
+}
+
+static void testEmplaceWithType() {
+ printf("testEmplaceWithType\n");
+ Variant<uint32_t, uint64_t, uint32_t> v1(mozilla::VariantIndex<0>{}, 0);
+ v1.emplace<uint64_t>(3);
+ MOZ_RELEASE_ASSERT(v1.is<uint64_t>());
+ MOZ_RELEASE_ASSERT(v1.as<uint64_t>() == 3);
+
+ Variant<UniquePtr<int>, char> v2('a');
+ v2.emplace<UniquePtr<int>>();
+ MOZ_RELEASE_ASSERT(v2.is<UniquePtr<int>>());
+ MOZ_RELEASE_ASSERT(!v2.as<UniquePtr<int>>().get());
+
+ Variant<UniquePtr<int>, char> v3('a');
+ v3.emplace<UniquePtr<int>>(MakeUnique<int>(4));
+ MOZ_RELEASE_ASSERT(v3.is<UniquePtr<int>>());
+ MOZ_RELEASE_ASSERT(*v3.as<UniquePtr<int>>().get() == 4);
+}
+
+static void testEmplaceWithIndex() {
+ printf("testEmplaceWithIndex\n");
+ Variant<uint32_t, uint64_t, uint32_t> v1(mozilla::VariantIndex<1>{}, 0);
+ v1.emplace<2>(2);
+ MOZ_RELEASE_ASSERT(!v1.is<uint64_t>());
+ MOZ_RELEASE_ASSERT(!v1.is<1>());
+ MOZ_RELEASE_ASSERT(!v1.is<0>());
+ MOZ_RELEASE_ASSERT(v1.is<2>());
+ MOZ_RELEASE_ASSERT(v1.as<2>() == 2);
+ MOZ_RELEASE_ASSERT(v1.extract<2>() == 2);
+
+ Variant<UniquePtr<int>, char> v2('a');
+ v2.emplace<0>();
+ MOZ_RELEASE_ASSERT(v2.is<UniquePtr<int>>());
+ MOZ_RELEASE_ASSERT(!v2.is<1>());
+ MOZ_RELEASE_ASSERT(v2.is<0>());
+ MOZ_RELEASE_ASSERT(!v2.as<0>().get());
+ MOZ_RELEASE_ASSERT(!v2.extract<0>().get());
+
+ Variant<UniquePtr<int>, char> v3('a');
+ v3.emplace<0>(MakeUnique<int>(4));
+ MOZ_RELEASE_ASSERT(v3.is<UniquePtr<int>>());
+ MOZ_RELEASE_ASSERT(!v3.is<1>());
+ MOZ_RELEASE_ASSERT(v3.is<0>());
+ MOZ_RELEASE_ASSERT(*v3.as<0>().get() == 4);
+ MOZ_RELEASE_ASSERT(*v3.extract<0>().get() == 4);
+}
+
+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(std::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(std::move(v3));
+
+ Variant<char, UniquePtr<Destroyer>> v5('a');
+ v5 = std::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> v1(d);
+ MOZ_RELEASE_ASSERT(Destroyer::destroyedCount ==
+ 0); // None destroyed yet.
+ }
+
+ MOZ_RELEASE_ASSERT(Destroyer::destroyedCount ==
+ 1); // v1's copy of d is destroyed.
+
+ {
+ Variant<char, UniquePtr<char[]>, Destroyer> v2(
+ mozilla::VariantIndex<2>{});
+ v2.emplace<Destroyer>(d);
+ MOZ_RELEASE_ASSERT(Destroyer::destroyedCount ==
+ 2); // v2's initial value is destroyed.
+ }
+
+ MOZ_RELEASE_ASSERT(Destroyer::destroyedCount ==
+ 3); // v2's second value is destroyed.
+ }
+
+ MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 4); // 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);
+}
+
+// Matcher that returns a description of how its call-operator was invoked.
+struct Describer {
+ enum class ParameterSize { NA, U8, U32, U64 };
+ enum class ParameterQualifier {
+ NA,
+ ParamLREF,
+ ParamCLREF,
+ ParamRREF,
+ ParamCRREF
+ };
+ enum class ThisQualifier { NA, ThisLREF, ThisCLREF, ThisRREF, ThisCRREF };
+
+ using Result =
+ std::tuple<ParameterSize, ParameterQualifier, ThisQualifier, uint64_t>;
+
+#define RESULT(SIZE, PQUAL, TQUAL, VALUE) \
+ Describer::Result(Describer::ParameterSize::SIZE, \
+ Describer::ParameterQualifier::PQUAL, \
+ Describer::ThisQualifier::TQUAL, VALUE)
+
+#define CALL(TYPE, SIZE, PQUAL, TREF, TQUAL) \
+ Result operator()(TYPE aValue) TREF { \
+ return RESULT(SIZE, PQUAL, TQUAL, aValue); \
+ }
+
+ // All combinations of possible call operators:
+ // Every line, the parameter integer type changes.
+ // Every 3 lines, the parameter type changes constness.
+ // Every 6 lines, the parameter changes reference l/r-valueness.
+ // Every 12 lines, the member function qualifier changes constness.
+ // After 24 lines, the member function qualifier changes ref l/r-valueness.
+ CALL(uint8_t&, U8, ParamLREF, &, ThisLREF)
+ CALL(uint32_t&, U32, ParamLREF, &, ThisLREF)
+ CALL(uint64_t&, U64, ParamLREF, &, ThisLREF)
+
+ CALL(const uint8_t&, U8, ParamCLREF, &, ThisLREF)
+ CALL(const uint32_t&, U32, ParamCLREF, &, ThisLREF)
+ CALL(const uint64_t&, U64, ParamCLREF, &, ThisLREF)
+
+ CALL(uint8_t&&, U8, ParamRREF, &, ThisLREF)
+ CALL(uint32_t&&, U32, ParamRREF, &, ThisLREF)
+ CALL(uint64_t&&, U64, ParamRREF, &, ThisLREF)
+
+ CALL(const uint8_t&&, U8, ParamCRREF, &, ThisLREF)
+ CALL(const uint32_t&&, U32, ParamCRREF, &, ThisLREF)
+ CALL(const uint64_t&&, U64, ParamCRREF, &, ThisLREF)
+
+ CALL(uint8_t&, U8, ParamLREF, const&, ThisCLREF)
+ CALL(uint32_t&, U32, ParamLREF, const&, ThisCLREF)
+ CALL(uint64_t&, U64, ParamLREF, const&, ThisCLREF)
+
+ CALL(const uint8_t&, U8, ParamCLREF, const&, ThisCLREF)
+ CALL(const uint32_t&, U32, ParamCLREF, const&, ThisCLREF)
+ CALL(const uint64_t&, U64, ParamCLREF, const&, ThisCLREF)
+
+ CALL(uint8_t&&, U8, ParamRREF, const&, ThisCLREF)
+ CALL(uint32_t&&, U32, ParamRREF, const&, ThisCLREF)
+ CALL(uint64_t&&, U64, ParamRREF, const&, ThisCLREF)
+
+ CALL(const uint8_t&&, U8, ParamCRREF, const&, ThisCLREF)
+ CALL(const uint32_t&&, U32, ParamCRREF, const&, ThisCLREF)
+ CALL(const uint64_t&&, U64, ParamCRREF, const&, ThisCLREF)
+
+ CALL(uint8_t&, U8, ParamLREF, &&, ThisRREF)
+ CALL(uint32_t&, U32, ParamLREF, &&, ThisRREF)
+ CALL(uint64_t&, U64, ParamLREF, &&, ThisRREF)
+
+ CALL(const uint8_t&, U8, ParamCLREF, &&, ThisRREF)
+ CALL(const uint32_t&, U32, ParamCLREF, &&, ThisRREF)
+ CALL(const uint64_t&, U64, ParamCLREF, &&, ThisRREF)
+
+ CALL(uint8_t&&, U8, ParamRREF, &&, ThisRREF)
+ CALL(uint32_t&&, U32, ParamRREF, &&, ThisRREF)
+ CALL(uint64_t&&, U64, ParamRREF, &&, ThisRREF)
+
+ CALL(const uint8_t&&, U8, ParamCRREF, &&, ThisRREF)
+ CALL(const uint32_t&&, U32, ParamCRREF, &&, ThisRREF)
+ CALL(const uint64_t&&, U64, ParamCRREF, &&, ThisRREF)
+
+ CALL(uint8_t&, U8, ParamLREF, const&&, ThisCRREF)
+ CALL(uint32_t&, U32, ParamLREF, const&&, ThisCRREF)
+ CALL(uint64_t&, U64, ParamLREF, const&&, ThisCRREF)
+
+ CALL(const uint8_t&, U8, ParamCLREF, const&&, ThisCRREF)
+ CALL(const uint32_t&, U32, ParamCLREF, const&&, ThisCRREF)
+ CALL(const uint64_t&, U64, ParamCLREF, const&&, ThisCRREF)
+
+ CALL(uint8_t&&, U8, ParamRREF, const&&, ThisCRREF)
+ CALL(uint32_t&&, U32, ParamRREF, const&&, ThisCRREF)
+ CALL(uint64_t&&, U64, ParamRREF, const&&, ThisCRREF)
+
+ CALL(const uint8_t&&, U8, ParamCRREF, const&&, ThisCRREF)
+ CALL(const uint32_t&&, U32, ParamCRREF, const&&, ThisCRREF)
+ CALL(const uint64_t&&, U64, ParamCRREF, const&&, ThisCRREF)
+
+#undef CALL
+
+ // Catch-all, to verify that there is no call with any type other than the
+ // expected ones above.
+ template <typename Other>
+ Result operator()(const Other&) {
+ MOZ_RELEASE_ASSERT(false);
+ return RESULT(NA, NA, NA, 0);
+ }
+};
+
+static void testMatching() {
+ printf("testMatching\n");
+ using V = Variant<uint8_t, uint32_t, uint64_t>;
+
+ Describer desc;
+ const Describer descConst;
+ auto MakeDescriber = []() { return Describer(); };
+ auto MakeConstDescriber = []() -> const Describer { return Describer(); };
+
+ V v1(uint8_t(1));
+ V v2(uint32_t(2));
+ V v3(uint64_t(3));
+
+ const V& constRef1 = v1;
+ const V& constRef2 = v2;
+ const V& constRef3 = v3;
+
+ // Create a temporary variant by returning a copy of one.
+ auto CopyV = [](const V& aV) { return aV; };
+
+ // Create a temporary variant by returning a const copy of one.
+ auto CopyConstV = [](const V& aV) -> const V { return aV; };
+
+ // All combinations of possible calls:
+ // Every line, the variant integer type changes.
+ // Every 3 lines, the variant type changes constness.
+ // Every 6 lines, the variant changes reference l/r-valueness.
+ // Every 12 lines, the matcher changes constness.
+ // After 24 lines, the matcher changes ref l/r-valueness.
+ MOZ_RELEASE_ASSERT(v1.match(desc) == RESULT(U8, ParamLREF, ThisLREF, 1));
+ MOZ_RELEASE_ASSERT(v2.match(desc) == RESULT(U32, ParamLREF, ThisLREF, 2));
+ MOZ_RELEASE_ASSERT(v3.match(desc) == RESULT(U64, ParamLREF, ThisLREF, 3));
+
+ MOZ_RELEASE_ASSERT(constRef1.match(desc) ==
+ RESULT(U8, ParamCLREF, ThisLREF, 1));
+ MOZ_RELEASE_ASSERT(constRef2.match(desc) ==
+ RESULT(U32, ParamCLREF, ThisLREF, 2));
+ MOZ_RELEASE_ASSERT(constRef3.match(desc) ==
+ RESULT(U64, ParamCLREF, ThisLREF, 3));
+
+ MOZ_RELEASE_ASSERT(CopyV(v1).match(desc) ==
+ RESULT(U8, ParamRREF, ThisLREF, 1));
+ MOZ_RELEASE_ASSERT(CopyV(v2).match(desc) ==
+ RESULT(U32, ParamRREF, ThisLREF, 2));
+ MOZ_RELEASE_ASSERT(CopyV(v3).match(desc) ==
+ RESULT(U64, ParamRREF, ThisLREF, 3));
+
+ MOZ_RELEASE_ASSERT(CopyConstV(v1).match(desc) ==
+ RESULT(U8, ParamCRREF, ThisLREF, 1));
+ MOZ_RELEASE_ASSERT(CopyConstV(v2).match(desc) ==
+ RESULT(U32, ParamCRREF, ThisLREF, 2));
+ MOZ_RELEASE_ASSERT(CopyConstV(v3).match(desc) ==
+ RESULT(U64, ParamCRREF, ThisLREF, 3));
+
+ MOZ_RELEASE_ASSERT(v1.match(descConst) ==
+ RESULT(U8, ParamLREF, ThisCLREF, 1));
+ MOZ_RELEASE_ASSERT(v2.match(descConst) ==
+ RESULT(U32, ParamLREF, ThisCLREF, 2));
+ MOZ_RELEASE_ASSERT(v3.match(descConst) ==
+ RESULT(U64, ParamLREF, ThisCLREF, 3));
+
+ MOZ_RELEASE_ASSERT(constRef1.match(descConst) ==
+ RESULT(U8, ParamCLREF, ThisCLREF, 1));
+ MOZ_RELEASE_ASSERT(constRef2.match(descConst) ==
+ RESULT(U32, ParamCLREF, ThisCLREF, 2));
+ MOZ_RELEASE_ASSERT(constRef3.match(descConst) ==
+ RESULT(U64, ParamCLREF, ThisCLREF, 3));
+
+ MOZ_RELEASE_ASSERT(CopyV(v1).match(descConst) ==
+ RESULT(U8, ParamRREF, ThisCLREF, 1));
+ MOZ_RELEASE_ASSERT(CopyV(v2).match(descConst) ==
+ RESULT(U32, ParamRREF, ThisCLREF, 2));
+ MOZ_RELEASE_ASSERT(CopyV(v3).match(descConst) ==
+ RESULT(U64, ParamRREF, ThisCLREF, 3));
+
+ MOZ_RELEASE_ASSERT(CopyConstV(v1).match(descConst) ==
+ RESULT(U8, ParamCRREF, ThisCLREF, 1));
+ MOZ_RELEASE_ASSERT(CopyConstV(v2).match(descConst) ==
+ RESULT(U32, ParamCRREF, ThisCLREF, 2));
+ MOZ_RELEASE_ASSERT(CopyConstV(v3).match(descConst) ==
+ RESULT(U64, ParamCRREF, ThisCLREF, 3));
+
+ MOZ_RELEASE_ASSERT(v1.match(MakeDescriber()) ==
+ RESULT(U8, ParamLREF, ThisRREF, 1));
+ MOZ_RELEASE_ASSERT(v2.match(MakeDescriber()) ==
+ RESULT(U32, ParamLREF, ThisRREF, 2));
+ MOZ_RELEASE_ASSERT(v3.match(MakeDescriber()) ==
+ RESULT(U64, ParamLREF, ThisRREF, 3));
+
+ MOZ_RELEASE_ASSERT(constRef1.match(MakeDescriber()) ==
+ RESULT(U8, ParamCLREF, ThisRREF, 1));
+ MOZ_RELEASE_ASSERT(constRef2.match(MakeDescriber()) ==
+ RESULT(U32, ParamCLREF, ThisRREF, 2));
+ MOZ_RELEASE_ASSERT(constRef3.match(MakeDescriber()) ==
+ RESULT(U64, ParamCLREF, ThisRREF, 3));
+
+ MOZ_RELEASE_ASSERT(CopyV(v1).match(MakeDescriber()) ==
+ RESULT(U8, ParamRREF, ThisRREF, 1));
+ MOZ_RELEASE_ASSERT(CopyV(v2).match(MakeDescriber()) ==
+ RESULT(U32, ParamRREF, ThisRREF, 2));
+ MOZ_RELEASE_ASSERT(CopyV(v3).match(MakeDescriber()) ==
+ RESULT(U64, ParamRREF, ThisRREF, 3));
+
+ MOZ_RELEASE_ASSERT(CopyConstV(v1).match(MakeDescriber()) ==
+ RESULT(U8, ParamCRREF, ThisRREF, 1));
+ MOZ_RELEASE_ASSERT(CopyConstV(v2).match(MakeDescriber()) ==
+ RESULT(U32, ParamCRREF, ThisRREF, 2));
+ MOZ_RELEASE_ASSERT(CopyConstV(v3).match(MakeDescriber()) ==
+ RESULT(U64, ParamCRREF, ThisRREF, 3));
+
+ MOZ_RELEASE_ASSERT(v1.match(MakeConstDescriber()) ==
+ RESULT(U8, ParamLREF, ThisCRREF, 1));
+ MOZ_RELEASE_ASSERT(v2.match(MakeConstDescriber()) ==
+ RESULT(U32, ParamLREF, ThisCRREF, 2));
+ MOZ_RELEASE_ASSERT(v3.match(MakeConstDescriber()) ==
+ RESULT(U64, ParamLREF, ThisCRREF, 3));
+
+ MOZ_RELEASE_ASSERT(constRef1.match(MakeConstDescriber()) ==
+ RESULT(U8, ParamCLREF, ThisCRREF, 1));
+ MOZ_RELEASE_ASSERT(constRef2.match(MakeConstDescriber()) ==
+ RESULT(U32, ParamCLREF, ThisCRREF, 2));
+ MOZ_RELEASE_ASSERT(constRef3.match(MakeConstDescriber()) ==
+ RESULT(U64, ParamCLREF, ThisCRREF, 3));
+
+ MOZ_RELEASE_ASSERT(CopyV(v1).match(MakeConstDescriber()) ==
+ RESULT(U8, ParamRREF, ThisCRREF, 1));
+ MOZ_RELEASE_ASSERT(CopyV(v2).match(MakeConstDescriber()) ==
+ RESULT(U32, ParamRREF, ThisCRREF, 2));
+ MOZ_RELEASE_ASSERT(CopyV(v3).match(MakeConstDescriber()) ==
+ RESULT(U64, ParamRREF, ThisCRREF, 3));
+
+ MOZ_RELEASE_ASSERT(CopyConstV(v1).match(MakeConstDescriber()) ==
+ RESULT(U8, ParamCRREF, ThisCRREF, 1));
+ MOZ_RELEASE_ASSERT(CopyConstV(v2).match(MakeConstDescriber()) ==
+ RESULT(U32, ParamCRREF, ThisCRREF, 2));
+ MOZ_RELEASE_ASSERT(CopyConstV(v3).match(MakeConstDescriber()) ==
+ RESULT(U64, ParamCRREF, ThisCRREF, 3));
+}
+
+static void testMatchingLambda() {
+ printf("testMatchingLambda\n");
+ using V = Variant<uint8_t, uint32_t, uint64_t>;
+
+ // Note: Lambdas' call operators are const by default (unless the lambda is
+ // declared `mutable`).
+ // There is no need to test mutable lambdas, nor rvalue lambda, because there
+ // would be no way to distinguish how each lambda is actually invoked because
+ // there is only one choice of call operator in each overload set.
+ auto desc = [](auto&& a) {
+ if constexpr (std::is_same_v<decltype(a), uint8_t&>) {
+ return RESULT(U8, ParamLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint8_t&>) {
+ return RESULT(U8, ParamCLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), uint8_t&&>) {
+ return RESULT(U8, ParamRREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint8_t&&>) {
+ return RESULT(U8, ParamCRREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), uint32_t&>) {
+ return RESULT(U32, ParamLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint32_t&>) {
+ return RESULT(U32, ParamCLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), uint32_t&&>) {
+ return RESULT(U32, ParamRREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint32_t&&>) {
+ return RESULT(U32, ParamCRREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), uint64_t&>) {
+ return RESULT(U64, ParamLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint64_t&>) {
+ return RESULT(U64, ParamCLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), uint64_t&&>) {
+ return RESULT(U64, ParamRREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint64_t&&>) {
+ return RESULT(U64, ParamCRREF, NA, a);
+ } else {
+ // We don't expect any other type.
+ // Tech note: We can't just do `static_assert(false)` which would always
+ // fail during the initial parsing. So we depend on the templated
+ // parameter to delay computing `false` until actual instantiation.
+ static_assert(sizeof(a) == size_t(-1));
+ return RESULT(NA, NA, NA, 0);
+ }
+ };
+
+ V v1(uint8_t(1));
+ V v2(uint32_t(2));
+ V v3(uint64_t(3));
+
+ const V& constRef1 = v1;
+ const V& constRef2 = v2;
+ const V& constRef3 = v3;
+
+ // Create a temporary variant by returning a copy of one.
+ auto CopyV = [](const V& aV) { return aV; };
+
+ // Create a temporary variant by returning a const copy of one.
+ auto CopyConstV = [](const V& aV) -> const V { return aV; };
+
+ MOZ_RELEASE_ASSERT(v1.match(desc) == RESULT(U8, ParamLREF, NA, 1));
+ MOZ_RELEASE_ASSERT(v2.match(desc) == RESULT(U32, ParamLREF, NA, 2));
+ MOZ_RELEASE_ASSERT(v3.match(desc) == RESULT(U64, ParamLREF, NA, 3));
+
+ MOZ_RELEASE_ASSERT(constRef1.match(desc) == RESULT(U8, ParamCLREF, NA, 1));
+ MOZ_RELEASE_ASSERT(constRef2.match(desc) == RESULT(U32, ParamCLREF, NA, 2));
+ MOZ_RELEASE_ASSERT(constRef3.match(desc) == RESULT(U64, ParamCLREF, NA, 3));
+
+ MOZ_RELEASE_ASSERT(CopyV(v1).match(desc) == RESULT(U8, ParamRREF, NA, 1));
+ MOZ_RELEASE_ASSERT(CopyV(v2).match(desc) == RESULT(U32, ParamRREF, NA, 2));
+ MOZ_RELEASE_ASSERT(CopyV(v3).match(desc) == RESULT(U64, ParamRREF, NA, 3));
+
+ MOZ_RELEASE_ASSERT(CopyConstV(v1).match(desc) ==
+ RESULT(U8, ParamCRREF, NA, 1));
+ MOZ_RELEASE_ASSERT(CopyConstV(v2).match(desc) ==
+ RESULT(U32, ParamCRREF, NA, 2));
+ MOZ_RELEASE_ASSERT(CopyConstV(v3).match(desc) ==
+ RESULT(U64, ParamCRREF, NA, 3));
+}
+
+static void testMatchingLambdaWithIndex() {
+ printf("testMatchingLambdaWithIndex\n");
+ using V = Variant<uint8_t, uint32_t, uint64_t>;
+
+ // Note: Lambdas' call operators are const by default (unless the lambda is
+ // declared `mutable`), hence the use of "...Const" strings below.
+ // There is no need to test mutable lambdas, nor rvalue lambda, because there
+ // would be no way to distinguish how each lambda is actually invoked because
+ // there is only one choice of call operator in each overload set.
+ auto desc = [](auto aIndex, auto&& a) {
+ static_assert(
+ std::is_same_v<decltype(aIndex), uint_fast8_t>,
+ "Expected a uint_fast8_t index for a Variant with 3 alternatives");
+ if constexpr (std::is_same_v<decltype(a), uint8_t&>) {
+ MOZ_RELEASE_ASSERT(aIndex == 0);
+ return RESULT(U8, ParamLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint8_t&>) {
+ MOZ_RELEASE_ASSERT(aIndex == 0);
+ return RESULT(U8, ParamCLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), uint8_t&&>) {
+ MOZ_RELEASE_ASSERT(aIndex == 0);
+ return RESULT(U8, ParamRREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint8_t&&>) {
+ MOZ_RELEASE_ASSERT(aIndex == 0);
+ return RESULT(U8, ParamCRREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), uint32_t&>) {
+ MOZ_RELEASE_ASSERT(aIndex == 1);
+ return RESULT(U32, ParamLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint32_t&>) {
+ MOZ_RELEASE_ASSERT(aIndex == 1);
+ return RESULT(U32, ParamCLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), uint32_t&&>) {
+ MOZ_RELEASE_ASSERT(aIndex == 1);
+ return RESULT(U32, ParamRREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint32_t&&>) {
+ MOZ_RELEASE_ASSERT(aIndex == 1);
+ return RESULT(U32, ParamCRREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), uint64_t&>) {
+ MOZ_RELEASE_ASSERT(aIndex == 2);
+ return RESULT(U64, ParamLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint64_t&>) {
+ MOZ_RELEASE_ASSERT(aIndex == 2);
+ return RESULT(U64, ParamCLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), uint64_t&&>) {
+ MOZ_RELEASE_ASSERT(aIndex == 2);
+ return RESULT(U64, ParamRREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint64_t&&>) {
+ MOZ_RELEASE_ASSERT(aIndex == 2);
+ return RESULT(U64, ParamCRREF, NA, a);
+ } else {
+ // We don't expect any other type.
+ // Tech note: We can't just do `static_assert(false)` which would always
+ // fail during the initial parsing. So we depend on the templated
+ // parameter to delay computing `false` until actual instantiation.
+ static_assert(sizeof(a) == size_t(-1));
+ return RESULT(NA, NA, NA, 0);
+ }
+ };
+
+ V v1(uint8_t(1));
+ V v2(uint32_t(2));
+ V v3(uint64_t(3));
+
+ const V& constRef1 = v1;
+ const V& constRef2 = v2;
+ const V& constRef3 = v3;
+
+ // Create a temporary variant by returning a copy of one.
+ auto CopyV = [](const V& aV) { return aV; };
+
+ // Create a temporary variant by returning a const copy of one.
+ auto CopyConstV = [](const V& aV) -> const V { return aV; };
+
+ MOZ_RELEASE_ASSERT(v1.match(desc) == RESULT(U8, ParamLREF, NA, 1));
+ MOZ_RELEASE_ASSERT(v2.match(desc) == RESULT(U32, ParamLREF, NA, 2));
+ MOZ_RELEASE_ASSERT(v3.match(desc) == RESULT(U64, ParamLREF, NA, 3));
+
+ MOZ_RELEASE_ASSERT(constRef1.match(desc) == RESULT(U8, ParamCLREF, NA, 1));
+ MOZ_RELEASE_ASSERT(constRef2.match(desc) == RESULT(U32, ParamCLREF, NA, 2));
+ MOZ_RELEASE_ASSERT(constRef3.match(desc) == RESULT(U64, ParamCLREF, NA, 3));
+
+ MOZ_RELEASE_ASSERT(CopyV(v1).match(desc) == RESULT(U8, ParamRREF, NA, 1));
+ MOZ_RELEASE_ASSERT(CopyV(v2).match(desc) == RESULT(U32, ParamRREF, NA, 2));
+ MOZ_RELEASE_ASSERT(CopyV(v3).match(desc) == RESULT(U64, ParamRREF, NA, 3));
+
+ MOZ_RELEASE_ASSERT(CopyConstV(v1).match(desc) ==
+ RESULT(U8, ParamCRREF, NA, 1));
+ MOZ_RELEASE_ASSERT(CopyConstV(v2).match(desc) ==
+ RESULT(U32, ParamCRREF, NA, 2));
+ MOZ_RELEASE_ASSERT(CopyConstV(v3).match(desc) ==
+ RESULT(U64, ParamCRREF, NA, 3));
+}
+
+static void testMatchingLambdas() {
+ printf("testMatchingLambdas\n");
+ using V = Variant<uint8_t, uint32_t, uint64_t>;
+
+ auto desc8 = [](auto&& a) {
+ if constexpr (std::is_same_v<decltype(a), uint8_t&>) {
+ return RESULT(U8, ParamLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint8_t&>) {
+ return RESULT(U8, ParamCLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), uint8_t&&>) {
+ return RESULT(U8, ParamRREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint8_t&&>) {
+ return RESULT(U8, ParamCRREF, NA, a);
+ } else {
+ // We don't expect any other type.
+ // Tech note: We can't just do `static_assert(false)` which would always
+ // fail during the initial parsing. So we depend on the templated
+ // parameter to delay computing `false` until actual instantiation.
+ static_assert(sizeof(a) == size_t(-1));
+ return RESULT(NA, NA, NA, 0);
+ }
+ };
+ auto desc32 = [](auto&& a) {
+ if constexpr (std::is_same_v<decltype(a), uint32_t&>) {
+ return RESULT(U32, ParamLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint32_t&>) {
+ return RESULT(U32, ParamCLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), uint32_t&&>) {
+ return RESULT(U32, ParamRREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint32_t&&>) {
+ return RESULT(U32, ParamCRREF, NA, a);
+ } else {
+ // We don't expect any other type.
+ // Tech note: We can't just do `static_assert(false)` which would always
+ // fail during the initial parsing. So we depend on the templated
+ // parameter to delay computing `false` until actual instantiation.
+ static_assert(sizeof(a) == size_t(-1));
+ return RESULT(NA, NA, NA, 0);
+ }
+ };
+ auto desc64 = [](auto&& a) {
+ if constexpr (std::is_same_v<decltype(a), uint64_t&>) {
+ return RESULT(U64, ParamLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint64_t&>) {
+ return RESULT(U64, ParamCLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), uint64_t&&>) {
+ return RESULT(U64, ParamRREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint64_t&&>) {
+ return RESULT(U64, ParamCRREF, NA, a);
+ } else {
+ // We don't expect any other type.
+ // Tech note: We can't just do `static_assert(false)` which would always
+ // fail during the initial parsing. So we depend on the templated
+ // parameter to delay computing `false` until actual instantiation.
+ static_assert(sizeof(a) == size_t(-1));
+ return RESULT(NA, NA, NA, 0);
+ }
+ };
+
+ V v1(uint8_t(1));
+ V v2(uint32_t(2));
+ V v3(uint64_t(3));
+
+ const V& constRef1 = v1;
+ const V& constRef2 = v2;
+ const V& constRef3 = v3;
+
+ // Create a temporary variant by returning a copy of one.
+ auto CopyV = [](const V& aV) { return aV; };
+
+ // Create a temporary variant by returning a const copy of one.
+ auto CopyConstV = [](const V& aV) -> const V { return aV; };
+
+ MOZ_RELEASE_ASSERT(v1.match(desc8, desc32, desc64) ==
+ RESULT(U8, ParamLREF, NA, 1));
+ MOZ_RELEASE_ASSERT(v2.match(desc8, desc32, desc64) ==
+ RESULT(U32, ParamLREF, NA, 2));
+ MOZ_RELEASE_ASSERT(v3.match(desc8, desc32, desc64) ==
+ RESULT(U64, ParamLREF, NA, 3));
+
+ MOZ_RELEASE_ASSERT(constRef1.match(desc8, desc32, desc64) ==
+ RESULT(U8, ParamCLREF, NA, 1));
+ MOZ_RELEASE_ASSERT(constRef2.match(desc8, desc32, desc64) ==
+ RESULT(U32, ParamCLREF, NA, 2));
+ MOZ_RELEASE_ASSERT(constRef3.match(desc8, desc32, desc64) ==
+ RESULT(U64, ParamCLREF, NA, 3));
+
+ MOZ_RELEASE_ASSERT(CopyV(v1).match(desc8, desc32, desc64) ==
+ RESULT(U8, ParamRREF, NA, 1));
+ MOZ_RELEASE_ASSERT(CopyV(v2).match(desc8, desc32, desc64) ==
+ RESULT(U32, ParamRREF, NA, 2));
+ MOZ_RELEASE_ASSERT(CopyV(v3).match(desc8, desc32, desc64) ==
+ RESULT(U64, ParamRREF, NA, 3));
+
+ MOZ_RELEASE_ASSERT(CopyConstV(v1).match(desc8, desc32, desc64) ==
+ RESULT(U8, ParamCRREF, NA, 1));
+ MOZ_RELEASE_ASSERT(CopyConstV(v2).match(desc8, desc32, desc64) ==
+ RESULT(U32, ParamCRREF, NA, 2));
+ MOZ_RELEASE_ASSERT(CopyConstV(v3).match(desc8, desc32, desc64) ==
+ RESULT(U64, ParamCRREF, NA, 3));
+}
+
+static void testMatchingLambdasWithIndex() {
+ printf("testMatchingLambdasWithIndex\n");
+ using V = Variant<uint8_t, uint32_t, uint64_t>;
+
+ auto desc8 = [](size_t aIndex, auto&& a) {
+ MOZ_RELEASE_ASSERT(aIndex == 0);
+ if constexpr (std::is_same_v<decltype(a), uint8_t&>) {
+ return RESULT(U8, ParamLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint8_t&>) {
+ return RESULT(U8, ParamCLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), uint8_t&&>) {
+ return RESULT(U8, ParamRREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint8_t&&>) {
+ return RESULT(U8, ParamCRREF, NA, a);
+ } else {
+ // We don't expect any other type.
+ // Tech note: We can't just do `static_assert(false)` which would always
+ // fail during the initial parsing. So we depend on the templated
+ // parameter to delay computing `false` until actual instantiation.
+ static_assert(sizeof(a) == size_t(-1));
+ return RESULT(NA, NA, NA, 0);
+ }
+ };
+ auto desc32 = [](size_t aIndex, auto&& a) {
+ MOZ_RELEASE_ASSERT(aIndex == 1);
+ if constexpr (std::is_same_v<decltype(a), uint32_t&>) {
+ return RESULT(U32, ParamLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint32_t&>) {
+ return RESULT(U32, ParamCLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), uint32_t&&>) {
+ return RESULT(U32, ParamRREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint32_t&&>) {
+ return RESULT(U32, ParamCRREF, NA, a);
+ } else {
+ // We don't expect any other type.
+ // Tech note: We can't just do `static_assert(false)` which would always
+ // fail during the initial parsing. So we depend on the templated
+ // parameter to delay computing `false` until actual instantiation.
+ static_assert(sizeof(a) == size_t(-1));
+ return RESULT(NA, NA, NA, 0);
+ }
+ };
+ auto desc64 = [](size_t aIndex, auto&& a) {
+ MOZ_RELEASE_ASSERT(aIndex == 2);
+ if constexpr (std::is_same_v<decltype(a), uint64_t&>) {
+ return RESULT(U64, ParamLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint64_t&>) {
+ return RESULT(U64, ParamCLREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), uint64_t&&>) {
+ return RESULT(U64, ParamRREF, NA, a);
+ } else if constexpr (std::is_same_v<decltype(a), const uint64_t&&>) {
+ return RESULT(U64, ParamCRREF, NA, a);
+ } else {
+ // We don't expect any other type.
+ // Tech note: We can't just do `static_assert(false)` which would always
+ // fail during the initial parsing. So we depend on the templated
+ // parameter to delay computing `false` until actual instantiation.
+ static_assert(sizeof(a) == size_t(-1));
+ return RESULT(NA, NA, NA, 0);
+ }
+ };
+
+ V v1(uint8_t(1));
+ V v2(uint32_t(2));
+ V v3(uint64_t(3));
+
+ const V& constRef1 = v1;
+ const V& constRef2 = v2;
+ const V& constRef3 = v3;
+
+ // Create a temporary variant by returning a copy of one.
+ auto CopyV = [](const V& aV) { return aV; };
+
+ // Create a temporary variant by returning a const copy of one.
+ auto CopyConstV = [](const V& aV) -> const V { return aV; };
+
+ MOZ_RELEASE_ASSERT(v1.match(desc8, desc32, desc64) ==
+ RESULT(U8, ParamLREF, NA, 1));
+ MOZ_RELEASE_ASSERT(v2.match(desc8, desc32, desc64) ==
+ RESULT(U32, ParamLREF, NA, 2));
+ MOZ_RELEASE_ASSERT(v3.match(desc8, desc32, desc64) ==
+ RESULT(U64, ParamLREF, NA, 3));
+
+ MOZ_RELEASE_ASSERT(constRef1.match(desc8, desc32, desc64) ==
+ RESULT(U8, ParamCLREF, NA, 1));
+ MOZ_RELEASE_ASSERT(constRef2.match(desc8, desc32, desc64) ==
+ RESULT(U32, ParamCLREF, NA, 2));
+ MOZ_RELEASE_ASSERT(constRef3.match(desc8, desc32, desc64) ==
+ RESULT(U64, ParamCLREF, NA, 3));
+
+ MOZ_RELEASE_ASSERT(CopyV(v1).match(desc8, desc32, desc64) ==
+ RESULT(U8, ParamRREF, NA, 1));
+ MOZ_RELEASE_ASSERT(CopyV(v2).match(desc8, desc32, desc64) ==
+ RESULT(U32, ParamRREF, NA, 2));
+ MOZ_RELEASE_ASSERT(CopyV(v3).match(desc8, desc32, desc64) ==
+ RESULT(U64, ParamRREF, NA, 3));
+
+ MOZ_RELEASE_ASSERT(CopyConstV(v1).match(desc8, desc32, desc64) ==
+ RESULT(U8, ParamCRREF, NA, 1));
+ MOZ_RELEASE_ASSERT(CopyConstV(v2).match(desc8, desc32, desc64) ==
+ RESULT(U32, ParamCRREF, NA, 2));
+ MOZ_RELEASE_ASSERT(CopyConstV(v3).match(desc8, desc32, desc64) ==
+ RESULT(U64, ParamCRREF, NA, 3));
+}
+
+#undef RESULT
+
+static void testAddTagToHash() {
+ printf("testAddToHash\n");
+ using V = Variant<uint8_t, uint16_t, uint32_t, uint64_t>;
+
+ // We don't know what our hash function is, and these are certainly not all
+ // true under all hash functions. But they are probably true under almost any
+ // decent hash function, and our aim is simply to establish that the tag
+ // *does* influence the hash value.
+ {
+ mozilla::HashNumber h8 = V(uint8_t(1)).addTagToHash(0);
+ mozilla::HashNumber h16 = V(uint16_t(1)).addTagToHash(0);
+ mozilla::HashNumber h32 = V(uint32_t(1)).addTagToHash(0);
+ mozilla::HashNumber h64 = V(uint64_t(1)).addTagToHash(0);
+
+ MOZ_RELEASE_ASSERT(h8 != h16 && h8 != h32 && h8 != h64);
+ MOZ_RELEASE_ASSERT(h16 != h32 && h16 != h64);
+ MOZ_RELEASE_ASSERT(h32 != h64);
+ }
+
+ {
+ mozilla::HashNumber h8 = V(uint8_t(1)).addTagToHash(0x124356);
+ mozilla::HashNumber h16 = V(uint16_t(1)).addTagToHash(0x124356);
+ mozilla::HashNumber h32 = V(uint32_t(1)).addTagToHash(0x124356);
+ mozilla::HashNumber h64 = V(uint64_t(1)).addTagToHash(0x124356);
+
+ MOZ_RELEASE_ASSERT(h8 != h16 && h8 != h32 && h8 != h64);
+ MOZ_RELEASE_ASSERT(h16 != h32 && h16 != h64);
+ MOZ_RELEASE_ASSERT(h32 != h64);
+ }
+}
+
+int main() {
+ testDetails();
+ testSimple();
+ testDuplicate();
+ testConstructionWithVariantType();
+ testConstructionWithVariantIndex();
+ testEmplaceWithType();
+ testEmplaceWithIndex();
+ testCopy();
+ testMove();
+ testDestructor();
+ testEquality();
+ testMatching();
+ testMatchingLambda();
+ testMatchingLambdaWithIndex();
+ testMatchingLambdas();
+ testMatchingLambdasWithIndex();
+ testAddTagToHash();
+
+ printf("TestVariant OK!\n");
+ return 0;
+}
diff --git a/mfbt/tests/TestVector.cpp b/mfbt/tests/TestVector.cpp
new file mode 100644
index 0000000000..021d02976b
--- /dev/null
+++ b/mfbt/tests/TestVector.cpp
@@ -0,0 +1,792 @@
+/* -*- 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 <utility>
+
+#include "mozilla/IntegerRange.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
+
+using mozilla::IntegerRange;
+using mozilla::MakeUnique;
+using mozilla::UniquePtr;
+using mozilla::Vector;
+using mozilla::detail::VectorTesting;
+
+struct mozilla::detail::VectorTesting {
+ static void testReserved();
+ static void testConstRange();
+ static void testEmplaceBack();
+ static void testReverse();
+ static void testExtractRawBuffer();
+ static void testExtractOrCopyRawBuffer();
+ static void testReplaceRawBuffer();
+ static void testInsert();
+ static void testErase();
+ static void testShrinkStorageToFit();
+ static void testAppend();
+};
+
+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(std::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(std::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(std::move(rhs.k)) {
+ rhs.j = 0;
+ rhs.k.reset(0);
+ moveCount++;
+ }
+
+ ~S() { destructCount++; }
+
+ S& operator=(S&& rhs) {
+ j = rhs.j;
+ rhs.j = 0;
+ k = std::move(rhs.k);
+ rhs.k.reset();
+ moveCount++;
+ return *this;
+ }
+
+ bool operator==(const S& rhs) const { return j == rhs.j && *k == *rhs.k; }
+
+ S(const S&) = delete;
+ S& operator=(const S&) = delete;
+};
+
+size_t S::constructCount = 0;
+size_t S::moveCount = 0;
+size_t S::destructCount = 0;
+
+} // namespace
+
+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(std::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(std::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(std::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);
+}
+
+void mozilla::detail::VectorTesting::testReplaceRawBuffer() {
+ S::resetCounts();
+
+ S* s = nullptr;
+
+ {
+ Vector<S> v;
+ MOZ_RELEASE_ASSERT(v.reserve(4));
+ v.infallibleEmplaceBack(1, 2);
+ v.infallibleEmplaceBack(3, 4);
+ MOZ_ASSERT(S::constructCount == 2);
+ s = v.extractRawBuffer();
+ }
+
+ MOZ_ASSERT(S::constructCount == 2);
+ MOZ_ASSERT(S::moveCount == 0);
+ MOZ_ASSERT(S::destructCount == 0);
+
+ {
+ Vector<S, 10> v;
+ v.replaceRawBuffer(s, 2);
+ MOZ_ASSERT(v.length() == 2);
+ MOZ_ASSERT(v.reserved() == 2);
+ MOZ_ASSERT(v.capacity() == 10);
+ MOZ_ASSERT(v[0].j == 1);
+ MOZ_ASSERT(v[1].j == 3);
+ MOZ_ASSERT(S::destructCount == 2);
+ }
+
+ MOZ_ASSERT(S::constructCount == 2);
+ MOZ_ASSERT(S::moveCount == 2);
+ MOZ_ASSERT(S::destructCount == 4);
+
+ S::resetCounts();
+
+ {
+ Vector<S, 2> v;
+ MOZ_RELEASE_ASSERT(v.reserve(4));
+ v.infallibleEmplaceBack(9, 10);
+ MOZ_ASSERT(S::constructCount == 1);
+ s = v.extractRawBuffer();
+ MOZ_ASSERT(S::constructCount == 1);
+ MOZ_ASSERT(S::moveCount == 0);
+ }
+
+ MOZ_ASSERT(S::destructCount == 0);
+
+ {
+ Vector<S> v;
+ v.replaceRawBuffer(s, 1, 4);
+ MOZ_ASSERT(v.length() == 1);
+ MOZ_ASSERT(v.reserved() == 4);
+ MOZ_ASSERT(v.capacity() == 4);
+ MOZ_ASSERT(v[0].j == 9);
+ for (size_t i = 0; i < 5; i++) MOZ_RELEASE_ASSERT(v.emplaceBack(i, i));
+ MOZ_ASSERT(v.length() == 6);
+ MOZ_ASSERT(v.reserved() == 6);
+ MOZ_ASSERT(S::constructCount == 6);
+ MOZ_ASSERT(S::moveCount == 4);
+ MOZ_ASSERT(S::destructCount == 4);
+ }
+
+ MOZ_ASSERT(S::destructCount == 10);
+}
+
+void mozilla::detail::VectorTesting::testInsert() {
+ S::resetCounts();
+
+ Vector<S, 8> vec;
+ MOZ_RELEASE_ASSERT(vec.reserve(8));
+ for (size_t i = 0; i < 7; i++) {
+ vec.infallibleEmplaceBack(i, i * i);
+ }
+
+ MOZ_RELEASE_ASSERT(vec.length() == 7);
+ MOZ_ASSERT(vec.reserved() == 8);
+ MOZ_RELEASE_ASSERT(S::constructCount == 7);
+ MOZ_RELEASE_ASSERT(S::moveCount == 0);
+ MOZ_RELEASE_ASSERT(S::destructCount == 0);
+
+ S s(42, 43);
+ MOZ_RELEASE_ASSERT(vec.insert(vec.begin() + 4, std::move(s)));
+
+ for (size_t i = 0; i < vec.length(); i++) {
+ const S& s = vec[i];
+ MOZ_RELEASE_ASSERT(s.k);
+ if (i < 4) {
+ MOZ_RELEASE_ASSERT(s.j == i && *s.k == i * i);
+ } else if (i == 4) {
+ MOZ_RELEASE_ASSERT(s.j == 42 && *s.k == 43);
+ } else {
+ MOZ_RELEASE_ASSERT(s.j == i - 1 && *s.k == (i - 1) * (i - 1));
+ }
+ }
+
+ MOZ_RELEASE_ASSERT(vec.length() == 8);
+ MOZ_ASSERT(vec.reserved() == 8);
+ MOZ_RELEASE_ASSERT(S::constructCount == 8);
+ MOZ_RELEASE_ASSERT(S::moveCount == 1 /* move in insert() call */ +
+ 1 /* move the back() element */ +
+ 3 /* elements to shift */);
+ MOZ_RELEASE_ASSERT(S::destructCount == 1);
+}
+
+void mozilla::detail::VectorTesting::testErase() {
+ S::resetCounts();
+
+ Vector<S, 8> vec;
+ MOZ_RELEASE_ASSERT(vec.reserve(8));
+ for (size_t i = 0; i < 7; i++) {
+ vec.infallibleEmplaceBack(i, i * i);
+ }
+
+ // vec: [0, 1, 2, 3, 4, 5, 6]
+ MOZ_RELEASE_ASSERT(vec.length() == 7);
+ MOZ_ASSERT(vec.reserved() == 8);
+ MOZ_RELEASE_ASSERT(S::constructCount == 7);
+ MOZ_RELEASE_ASSERT(S::moveCount == 0);
+ MOZ_RELEASE_ASSERT(S::destructCount == 0);
+ S::resetCounts();
+
+ vec.erase(&vec[4]);
+ // vec: [0, 1, 2, 3, 5, 6]
+ MOZ_RELEASE_ASSERT(vec.length() == 6);
+ MOZ_ASSERT(vec.reserved() == 8);
+ MOZ_RELEASE_ASSERT(S::constructCount == 0);
+ // 5 and 6 should have been moved into 4 and 5.
+ MOZ_RELEASE_ASSERT(S::moveCount == 2);
+ MOZ_RELEASE_ASSERT(S::destructCount == 1);
+ MOZ_RELEASE_ASSERT(vec[4] == S(5, 5 * 5));
+ MOZ_RELEASE_ASSERT(vec[5] == S(6, 6 * 6));
+ S::resetCounts();
+
+ vec.erase(&vec[3], &vec[5]);
+ // vec: [0, 1, 2, 6]
+ MOZ_RELEASE_ASSERT(vec.length() == 4);
+ MOZ_ASSERT(vec.reserved() == 8);
+ MOZ_RELEASE_ASSERT(S::constructCount == 0);
+ // 6 should have been moved into 3.
+ MOZ_RELEASE_ASSERT(S::moveCount == 1);
+ MOZ_RELEASE_ASSERT(S::destructCount == 2);
+ MOZ_RELEASE_ASSERT(vec[3] == S(6, 6 * 6));
+
+ S s2(2, 2 * 2);
+ S::resetCounts();
+
+ vec.eraseIfEqual(s2);
+ // vec: [0, 1, 6]
+ MOZ_RELEASE_ASSERT(vec.length() == 3);
+ MOZ_ASSERT(vec.reserved() == 8);
+ MOZ_RELEASE_ASSERT(S::constructCount == 0);
+ // 6 should have been moved into 2.
+ MOZ_RELEASE_ASSERT(S::moveCount == 1);
+ MOZ_RELEASE_ASSERT(S::destructCount == 1);
+ MOZ_RELEASE_ASSERT(vec[2] == S(6, 6 * 6));
+ S::resetCounts();
+
+ // Predicate to find one element.
+ vec.eraseIf([](const S& s) { return s.j == 1; });
+ // vec: [0, 6]
+ MOZ_RELEASE_ASSERT(vec.length() == 2);
+ MOZ_ASSERT(vec.reserved() == 8);
+ MOZ_RELEASE_ASSERT(S::constructCount == 0);
+ // 6 should have been moved into 1.
+ MOZ_RELEASE_ASSERT(S::moveCount == 1);
+ MOZ_RELEASE_ASSERT(S::destructCount == 1);
+ MOZ_RELEASE_ASSERT(vec[1] == S(6, 6 * 6));
+ S::resetCounts();
+
+ // Generic predicate that flags everything.
+ vec.eraseIf([](auto&&) { return true; });
+ // vec: []
+ MOZ_RELEASE_ASSERT(vec.length() == 0);
+ MOZ_ASSERT(vec.reserved() == 8);
+ MOZ_RELEASE_ASSERT(S::constructCount == 0);
+ MOZ_RELEASE_ASSERT(S::moveCount == 0);
+ MOZ_RELEASE_ASSERT(S::destructCount == 2);
+
+ for (size_t i = 0; i < 7; i++) {
+ vec.infallibleEmplaceBack(i, i * i);
+ }
+ // vec: [0, 1, 2, 3, 4, 5, 6]
+ MOZ_RELEASE_ASSERT(vec.length() == 7);
+ S::resetCounts();
+
+ // Predicate that flags all even numbers.
+ vec.eraseIf([](const S& s) { return s.j % 2 == 0; });
+ // vec: [1 (was 0), 3 (was 1), 5 (was 2)]
+ MOZ_RELEASE_ASSERT(vec.length() == 3);
+ MOZ_ASSERT(vec.reserved() == 8);
+ MOZ_RELEASE_ASSERT(S::constructCount == 0);
+ MOZ_RELEASE_ASSERT(S::moveCount == 3);
+ MOZ_RELEASE_ASSERT(S::destructCount == 4);
+}
+
+void mozilla::detail::VectorTesting::testShrinkStorageToFit() {
+ // Vectors not using inline storage realloc capacity to exact length.
+ {
+ Vector<int, 0> v1;
+ MOZ_RELEASE_ASSERT(v1.reserve(10));
+ v1.infallibleAppend(1);
+ MOZ_ASSERT(v1.length() == 1);
+ MOZ_ASSERT(v1.reserved() == 10);
+ MOZ_ASSERT(v1.capacity() >= 10);
+ v1.shrinkStorageToFit();
+ MOZ_ASSERT(v1.length() == 1);
+ MOZ_ASSERT(v1.reserved() == 1);
+ MOZ_ASSERT(v1.capacity() == 1);
+ }
+
+ // Vectors using inline storage do nothing.
+ {
+ Vector<int, 2> v2;
+ MOZ_RELEASE_ASSERT(v2.reserve(2));
+ v2.infallibleAppend(1);
+ MOZ_ASSERT(v2.length() == 1);
+ MOZ_ASSERT(v2.reserved() == 2);
+ MOZ_ASSERT(v2.capacity() == 2);
+ v2.shrinkStorageToFit();
+ MOZ_ASSERT(v2.length() == 1);
+ MOZ_ASSERT(v2.reserved() == 2);
+ MOZ_ASSERT(v2.capacity() == 2);
+ }
+
+ // shrinkStorageToFit uses inline storage if possible.
+ {
+ Vector<int, 2> v;
+ MOZ_RELEASE_ASSERT(v.reserve(4));
+ v.infallibleAppend(1);
+ MOZ_ASSERT(v.length() == 1);
+ MOZ_ASSERT(v.reserved() == 4);
+ MOZ_ASSERT(v.capacity() >= 4);
+ v.shrinkStorageToFit();
+ MOZ_ASSERT(v.length() == 1);
+ MOZ_ASSERT(v.reserved() == 1);
+ MOZ_ASSERT(v.capacity() == 2);
+ }
+
+ // Non-pod shrinking to non-inline storage.
+ {
+ static size_t sConstructCounter = 0;
+ static size_t sCopyCounter = 0;
+ static size_t sMoveCounter = 0;
+ static size_t sDestroyCounter = 0;
+ struct NonPod {
+ int mSomething = 10;
+
+ NonPod() { sConstructCounter++; }
+
+ NonPod(const NonPod& aOther) : mSomething(aOther.mSomething) {
+ sCopyCounter++;
+ }
+ NonPod(NonPod&& aOther) : mSomething(aOther.mSomething) {
+ sMoveCounter++;
+ }
+ ~NonPod() { sDestroyCounter++; }
+ };
+
+ Vector<NonPod, 5> v;
+ MOZ_RELEASE_ASSERT(v.reserve(10));
+ for (size_t i = 0; i < 8; ++i) {
+ v.infallibleEmplaceBack();
+ }
+ MOZ_RELEASE_ASSERT(sConstructCounter == 8);
+ MOZ_RELEASE_ASSERT(sCopyCounter == 0);
+ MOZ_RELEASE_ASSERT(sMoveCounter == 0);
+ MOZ_RELEASE_ASSERT(sDestroyCounter == 0);
+ MOZ_RELEASE_ASSERT(v.length() == 8);
+ MOZ_ASSERT(v.reserved() == 10);
+ MOZ_RELEASE_ASSERT(v.capacity() >= 10);
+ MOZ_RELEASE_ASSERT(v.shrinkStorageToFit());
+
+ MOZ_RELEASE_ASSERT(sConstructCounter == 8);
+ MOZ_RELEASE_ASSERT(sCopyCounter == 0);
+ MOZ_RELEASE_ASSERT(sMoveCounter == 8);
+ MOZ_RELEASE_ASSERT(sDestroyCounter == 8);
+ MOZ_RELEASE_ASSERT(v.length() == 8);
+ MOZ_ASSERT(v.reserved() == 8);
+ MOZ_RELEASE_ASSERT(v.capacity() == 8);
+ }
+
+ // Non-POD shrinking to inline storage.
+ {
+ static size_t sConstructCounter = 0;
+ static size_t sCopyCounter = 0;
+ static size_t sMoveCounter = 0;
+ static size_t sDestroyCounter = 0;
+ struct NonPod {
+ int mSomething = 10;
+
+ NonPod() { sConstructCounter++; }
+
+ NonPod(const NonPod& aOther) : mSomething(aOther.mSomething) {
+ sCopyCounter++;
+ }
+ NonPod(NonPod&& aOther) : mSomething(aOther.mSomething) {
+ sMoveCounter++;
+ }
+ ~NonPod() { sDestroyCounter++; }
+ };
+
+ Vector<NonPod, 5> v;
+ MOZ_RELEASE_ASSERT(v.reserve(10));
+ for (size_t i = 0; i < 3; ++i) {
+ v.infallibleEmplaceBack();
+ }
+ MOZ_RELEASE_ASSERT(sConstructCounter == 3);
+ MOZ_RELEASE_ASSERT(sCopyCounter == 0);
+ MOZ_RELEASE_ASSERT(sMoveCounter == 0);
+ MOZ_RELEASE_ASSERT(sDestroyCounter == 0);
+ MOZ_RELEASE_ASSERT(v.length() == 3);
+ MOZ_ASSERT(v.reserved() == 10);
+ MOZ_RELEASE_ASSERT(v.capacity() >= 10);
+ MOZ_RELEASE_ASSERT(v.shrinkStorageToFit());
+
+ MOZ_RELEASE_ASSERT(sConstructCounter == 3);
+ MOZ_RELEASE_ASSERT(sCopyCounter == 0);
+ MOZ_RELEASE_ASSERT(sMoveCounter == 3);
+ MOZ_RELEASE_ASSERT(sDestroyCounter == 3);
+ MOZ_RELEASE_ASSERT(v.length() == 3);
+ MOZ_ASSERT(v.reserved() == 3);
+ MOZ_RELEASE_ASSERT(v.capacity() == 5);
+ }
+}
+
+void mozilla::detail::VectorTesting::testAppend() {
+ // Test moving append/appendAll with a move-only type
+ Vector<UniquePtr<int>> bv;
+ for (const int val : IntegerRange<int>(0, 3)) {
+ MOZ_RELEASE_ASSERT(bv.append(MakeUnique<int>(val)));
+ }
+
+ Vector<UniquePtr<int>> otherbv;
+ for (const int val : IntegerRange<int>(3, 8)) {
+ MOZ_RELEASE_ASSERT(otherbv.append(MakeUnique<int>(val)));
+ }
+ MOZ_RELEASE_ASSERT(bv.appendAll(std::move(otherbv)));
+
+ MOZ_RELEASE_ASSERT(otherbv.length() == 0);
+ MOZ_RELEASE_ASSERT(bv.length() == 8);
+ for (const int val : IntegerRange<int>(0, 8)) {
+ MOZ_RELEASE_ASSERT(*bv[val] == val);
+ }
+}
+
+// Vector with no inline storage should occupy the absolute minimum space in
+// non-debug builds. (Debug adds a laundry list of other constraints, none
+// directly relevant to shipping builds, that aren't worth precisely modeling.)
+#ifndef DEBUG
+
+template <typename T>
+struct NoInlineStorageLayout {
+ T* mBegin;
+ size_t mLength;
+ struct CRAndStorage {
+ size_t mCapacity;
+ } mTail;
+};
+
+// Only one of these should be necessary, but test a few of them for good
+// measure.
+static_assert(sizeof(Vector<int, 0>) == sizeof(NoInlineStorageLayout<int>),
+ "Vector of int without inline storage shouldn't occupy dead "
+ "space for that absence of storage");
+
+static_assert(sizeof(Vector<bool, 0>) == sizeof(NoInlineStorageLayout<bool>),
+ "Vector of bool without inline storage shouldn't occupy dead "
+ "space for that absence of storage");
+
+static_assert(sizeof(Vector<S, 0>) == sizeof(NoInlineStorageLayout<S>),
+ "Vector of S without inline storage shouldn't occupy dead "
+ "space for that absence of storage");
+
+#endif // DEBUG
+
+static void TestVectorBeginNonNull() {
+ // Vector::begin() should never return nullptr, to accommodate callers that
+ // (either for hygiene, or for semantic reasons) need a non-null pointer even
+ // for zero elements.
+
+ Vector<bool, 0> bvec0;
+ MOZ_RELEASE_ASSERT(bvec0.length() == 0);
+ MOZ_RELEASE_ASSERT(bvec0.begin() != nullptr);
+
+ Vector<bool, 1> bvec1;
+ MOZ_RELEASE_ASSERT(bvec1.length() == 0);
+ MOZ_RELEASE_ASSERT(bvec1.begin() != nullptr);
+
+ Vector<bool, 64> bvec64;
+ MOZ_RELEASE_ASSERT(bvec64.length() == 0);
+ MOZ_RELEASE_ASSERT(bvec64.begin() != nullptr);
+
+ Vector<int, 0> ivec0;
+ MOZ_RELEASE_ASSERT(ivec0.length() == 0);
+ MOZ_RELEASE_ASSERT(ivec0.begin() != nullptr);
+
+ Vector<int, 1> ivec1;
+ MOZ_RELEASE_ASSERT(ivec1.length() == 0);
+ MOZ_RELEASE_ASSERT(ivec1.begin() != nullptr);
+
+ Vector<int, 64> ivec64;
+ MOZ_RELEASE_ASSERT(ivec64.length() == 0);
+ MOZ_RELEASE_ASSERT(ivec64.begin() != nullptr);
+
+ Vector<long, 0> lvec0;
+ MOZ_RELEASE_ASSERT(lvec0.length() == 0);
+ MOZ_RELEASE_ASSERT(lvec0.begin() != nullptr);
+
+ Vector<long, 1> lvec1;
+ MOZ_RELEASE_ASSERT(lvec1.length() == 0);
+ MOZ_RELEASE_ASSERT(lvec1.begin() != nullptr);
+
+ Vector<long, 64> lvec64;
+ MOZ_RELEASE_ASSERT(lvec64.length() == 0);
+ MOZ_RELEASE_ASSERT(lvec64.begin() != nullptr);
+
+ // Vector<T, N> doesn't guarantee N inline elements -- the actual count is
+ // capped so that any Vector fits in a not-crazy amount of space -- so the
+ // code below won't overflow stacks or anything crazy.
+ struct VeryBig {
+ int array[16 * 1024 * 1024];
+ };
+
+ Vector<VeryBig, 0> vbvec0;
+ MOZ_RELEASE_ASSERT(vbvec0.length() == 0);
+ MOZ_RELEASE_ASSERT(vbvec0.begin() != nullptr);
+
+ Vector<VeryBig, 1> vbvec1;
+ MOZ_RELEASE_ASSERT(vbvec1.length() == 0);
+ MOZ_RELEASE_ASSERT(vbvec1.begin() != nullptr);
+
+ Vector<VeryBig, 64> vbvec64;
+ MOZ_RELEASE_ASSERT(vbvec64.length() == 0);
+ MOZ_RELEASE_ASSERT(vbvec64.begin() != nullptr);
+}
+
+int main() {
+ VectorTesting::testReserved();
+ VectorTesting::testConstRange();
+ VectorTesting::testEmplaceBack();
+ VectorTesting::testReverse();
+ VectorTesting::testExtractRawBuffer();
+ VectorTesting::testExtractOrCopyRawBuffer();
+ VectorTesting::testReplaceRawBuffer();
+ VectorTesting::testInsert();
+ VectorTesting::testErase();
+ VectorTesting::testShrinkStorageToFit();
+ VectorTesting::testAppend();
+ TestVectorBeginNonNull();
+}
diff --git a/mfbt/tests/TestWeakPtr.cpp b/mfbt/tests/TestWeakPtr.cpp
new file mode 100644
index 0000000000..0599975a9c
--- /dev/null
+++ b/mfbt/tests/TestWeakPtr.cpp
@@ -0,0 +1,145 @@
+/* -*- 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;
+
+static char IamB[] = "B";
+static char IamC[] = "C";
+static char IamD[] = "D";
+
+class B : public SupportsWeakPtr {
+ public:
+ char const* whoAmI() const { return IamB; }
+};
+
+// To have a class C support weak pointers, inherit from SupportsWeakPtr.
+class C : public SupportsWeakPtr {
+ public:
+ int mNum;
+
+ C() : mNum(0) {}
+
+ ~C() {
+ // Setting mNum in the destructor allows us to test against use-after-free
+ // below
+ mNum = 0xDEAD;
+ }
+
+ char const* whoAmI() const { return IamC; }
+
+ void act() {}
+
+ bool isConst() { return false; }
+
+ bool isConst() const { return true; }
+};
+
+// Derived from a class that supports weakptr, but doesn't implement itself
+// To check upcast works as expected
+class D : public B {
+ public:
+ char const* whoAmI() const { return IamD; }
+};
+
+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.");
+
+ // Check that we correctly upcast to the base class supporting weakptr
+ D* d = new D;
+ WeakPtr<B> db = d;
+
+ // You should be able to use WeakPtr<D> even if it's a base class which
+ // implements SupportsWeakPtr.
+ WeakPtr<D> weakd = d;
+
+ MOZ_RELEASE_ASSERT(db->whoAmI() == IamB);
+ MOZ_RELEASE_ASSERT(weakd.get() == db.get());
+
+ delete d;
+
+ MOZ_RELEASE_ASSERT(!db);
+ MOZ_RELEASE_ASSERT(!weakd);
+}
diff --git a/mfbt/tests/TestWinArchDefs.cpp b/mfbt/tests/TestWinArchDefs.cpp
new file mode 100644
index 0000000000..d8965d3d7c
--- /dev/null
+++ b/mfbt/tests/TestWinArchDefs.cpp
@@ -0,0 +1,58 @@
+/* -*- 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/. */
+
+// This code tests the consistency of architecture-specific predefined macros
+// inherited from MSVC, before and after windows.h inclusion. See
+// https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros for a
+// list of such macros.
+
+// If this test compiles, it is successful. See bug 1866562 for an example
+// where mingwclang builds were failing to compile this code.
+
+#if defined(_M_IX86)
+constexpr auto kIX86 = _M_IX86;
+#endif
+
+#if defined(_M_X64)
+constexpr auto kX64 = _M_X64;
+#endif
+
+#if defined(_M_AMD64)
+constexpr auto kAMD64 = _M_AMD64;
+#endif
+
+#if defined(_M_ARM)
+constexpr auto kARM = _M_ARM;
+#endif
+
+#if defined(_M_ARM64)
+constexpr auto kARM64 = _M_ARM64;
+#endif
+
+#include <windows.h>
+
+#if defined(_M_IX86)
+static_assert(kIX86 == _M_IX86);
+#endif
+
+#if defined(_M_X64)
+static_assert(kX64 == _M_X64);
+#endif
+
+#if defined(_M_AMD64)
+static_assert(kAMD64 == _M_AMD64);
+#endif
+
+#if defined(_M_ARM)
+static_assert(kARM == _M_ARM);
+#endif
+
+#if defined(_M_ARM64)
+static_assert(kARM64 == _M_ARM64);
+#endif
+
+// If this test compiles, it is successful.
+int main() { return 0; }
diff --git a/mfbt/tests/TestWrappingOperations.cpp b/mfbt/tests/TestWrappingOperations.cpp
new file mode 100644
index 0000000000..c1cbb8ae6a
--- /dev/null
+++ b/mfbt/tests/TestWrappingOperations.cpp
@@ -0,0 +1,587 @@
+/* -*- 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/WrappingOperations.h"
+
+#include <stdint.h>
+
+using mozilla::WrappingAdd;
+using mozilla::WrappingMultiply;
+using mozilla::WrappingSubtract;
+using mozilla::WrapToSigned;
+
+// NOTE: In places below |-FOO_MAX - 1| is used instead of |-FOO_MIN| because
+// in C++ numeric literals are full expressions -- the |-| in a negative
+// number is technically separate. So with most compilers that limit
+// |int| to the signed 32-bit range, something like |-2147483648| is
+// operator-() applied to an *unsigned* expression. And MSVC, at least,
+// warns when you do that. (The operation is well-defined, but it likely
+// doesn't do what was intended.) So we do the usual workaround for this
+// (see your local copy of <stdint.h> for a likely demo of this), writing
+// it out by negating the max value and subtracting 1.
+
+static_assert(WrapToSigned(uint8_t(17)) == 17,
+ "no wraparound should work, 8-bit");
+static_assert(WrapToSigned(uint8_t(128)) == -128,
+ "works for 8-bit numbers, wraparound low end");
+static_assert(WrapToSigned(uint8_t(128 + 7)) == -128 + 7,
+ "works for 8-bit numbers, wraparound mid");
+static_assert(WrapToSigned(uint8_t(128 + 127)) == -128 + 127,
+ "works for 8-bit numbers, wraparound high end");
+
+static_assert(WrapToSigned(uint16_t(12345)) == 12345,
+ "no wraparound should work, 16-bit");
+static_assert(WrapToSigned(uint16_t(32768)) == -32768,
+ "works for 16-bit numbers, wraparound low end");
+static_assert(WrapToSigned(uint16_t(32768 + 42)) == -32768 + 42,
+ "works for 16-bit numbers, wraparound mid");
+static_assert(WrapToSigned(uint16_t(32768 + 32767)) == -32768 + 32767,
+ "works for 16-bit numbers, wraparound high end");
+
+static_assert(WrapToSigned(uint32_t(8675309)) == 8675309,
+ "no wraparound should work, 32-bit");
+static_assert(WrapToSigned(uint32_t(2147483648)) == -2147483647 - 1,
+ "works for 32-bit numbers, wraparound low end");
+static_assert(WrapToSigned(uint32_t(2147483648 + 42)) == -2147483647 - 1 + 42,
+ "works for 32-bit numbers, wraparound mid");
+static_assert(WrapToSigned(uint32_t(2147483648 + 2147483647)) ==
+ -2147483647 - 1 + 2147483647,
+ "works for 32-bit numbers, wraparound high end");
+
+static_assert(WrapToSigned(uint64_t(4152739164)) == 4152739164,
+ "no wraparound should work, 64-bit");
+static_assert(WrapToSigned(uint64_t(9223372036854775808ULL)) ==
+ -9223372036854775807LL - 1,
+ "works for 64-bit numbers, wraparound low end");
+static_assert(WrapToSigned(uint64_t(9223372036854775808ULL + 8005552368LL)) ==
+ -9223372036854775807LL - 1 + 8005552368LL,
+ "works for 64-bit numbers, wraparound mid");
+static_assert(WrapToSigned(uint64_t(9223372036854775808ULL +
+ 9223372036854775807ULL)) ==
+ -9223372036854775807LL - 1 + 9223372036854775807LL,
+ "works for 64-bit numbers, wraparound high end");
+
+template <typename T>
+inline constexpr bool TestEqual(T aX, T aY) {
+ return aX == aY;
+}
+
+static void TestWrappingAdd8() {
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(uint8_t(0), uint8_t(128)), uint8_t(128)),
+ "zero plus anything is anything");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(uint8_t(17), uint8_t(42)), uint8_t(59)),
+ "17 + 42 == 59");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(uint8_t(255), uint8_t(1)), uint8_t(0)),
+ "all bits plus one overflows to zero");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(uint8_t(128), uint8_t(127)), uint8_t(255)),
+ "high bit plus all lower bits is all bits");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(uint8_t(128), uint8_t(193)), uint8_t(65)),
+ "128 + 193 is 256 + 65");
+
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(int8_t(0), int8_t(-128)), int8_t(-128)),
+ "zero plus anything is anything");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(int8_t(123), int8_t(8)), int8_t(-125)),
+ "overflow to negative");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(int8_t(5), int8_t(-123)), int8_t(-118)),
+ "5 - 123 is -118");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(int8_t(-85), int8_t(-73)), int8_t(98)),
+ "underflow to positive");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(int8_t(-128), int8_t(127)), int8_t(-1)),
+ "high bit plus all lower bits is -1");
+}
+
+static void TestWrappingAdd16() {
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(uint16_t(0), uint16_t(32768)), uint16_t(32768)),
+ "zero plus anything is anything");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(uint16_t(24389), uint16_t(2682)), uint16_t(27071)),
+ "24389 + 2682 == 27071");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(uint16_t(65535), uint16_t(1)), uint16_t(0)),
+ "all bits plus one overflows to zero");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(uint16_t(32768), uint16_t(32767)), uint16_t(65535)),
+ "high bit plus all lower bits is all bits");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(uint16_t(32768), uint16_t(47582)), uint16_t(14814)),
+ "32768 + 47582 is 65536 + 14814");
+
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(int16_t(0), int16_t(-32768)), int16_t(-32768)),
+ "zero plus anything is anything");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(int16_t(32765), int16_t(8)), int16_t(-32763)),
+ "overflow to negative");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(int16_t(5), int16_t(-28933)), int16_t(-28928)),
+ "5 - 28933 is -28928");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(int16_t(-23892), int16_t(-12893)), int16_t(28751)),
+ "underflow to positive");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(int16_t(-32768), int16_t(32767)), int16_t(-1)),
+ "high bit plus all lower bits is -1");
+}
+
+static void TestWrappingAdd32() {
+ MOZ_RELEASE_ASSERT(TestEqual(WrappingAdd(uint32_t(0), uint32_t(2147483648)),
+ uint32_t(2147483648)),
+ "zero plus anything is anything");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(uint32_t(1398742328), uint32_t(714192829)),
+ uint32_t(2112935157)),
+ "1398742328 + 714192829 == 2112935157");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(uint32_t(4294967295), uint32_t(1)), uint32_t(0)),
+ "all bits plus one overflows to zero");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(uint32_t(2147483648), uint32_t(2147483647)),
+ uint32_t(4294967295)),
+ "high bit plus all lower bits is all bits");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(uint32_t(2147483648), uint32_t(3146492712)),
+ uint32_t(999009064)),
+ "2147483648 + 3146492712 is 4294967296 + 999009064");
+
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(int32_t(0), int32_t(-2147483647 - 1)),
+ int32_t(-2147483647 - 1)),
+ "zero plus anything is anything");
+ MOZ_RELEASE_ASSERT(TestEqual(WrappingAdd(int32_t(2147483645), int32_t(8)),
+ int32_t(-2147483643)),
+ "overflow to negative");
+ MOZ_RELEASE_ASSERT(TestEqual(WrappingAdd(int32_t(257), int32_t(-23947248)),
+ int32_t(-23946991)),
+ "257 - 23947248 is -23946991");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(int32_t(-2147483220), int32_t(-12893)),
+ int32_t(2147471183)),
+ "underflow to positive");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(int32_t(-32768), int32_t(32767)), int32_t(-1)),
+ "high bit plus all lower bits is -1");
+}
+
+static void TestWrappingAdd64() {
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(uint64_t(0), uint64_t(9223372036854775808ULL)),
+ uint64_t(9223372036854775808ULL)),
+ "zero plus anything is anything");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(uint64_t(70368744177664), uint64_t(3740873592)),
+ uint64_t(70372485051256)),
+ "70368744177664 + 3740873592 == 70372485051256");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(uint64_t(18446744073709551615ULL), uint64_t(1)),
+ uint64_t(0)),
+ "all bits plus one overflows to zero");
+ MOZ_RELEASE_ASSERT(TestEqual(WrappingAdd(uint64_t(9223372036854775808ULL),
+ uint64_t(9223372036854775807ULL)),
+ uint64_t(18446744073709551615ULL)),
+ "high bit plus all lower bits is all bits");
+ MOZ_RELEASE_ASSERT(TestEqual(WrappingAdd(uint64_t(14552598638644786479ULL),
+ uint64_t(3894174382537247221ULL)),
+ uint64_t(28947472482084)),
+ "9223372036854775808 + 3146492712 is 18446744073709551616 "
+ "+ 28947472482084");
+
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(int64_t(0), int64_t(-9223372036854775807LL - 1)),
+ int64_t(-9223372036854775807LL - 1)),
+ "zero plus anything is anything");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(int64_t(9223372036854775802LL), int64_t(8)),
+ int64_t(-9223372036854775806LL)),
+ "overflow to negative");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingAdd(int64_t(37482739294298742LL),
+ int64_t(-437843573929483498LL)),
+ int64_t(-400360834635184756LL)),
+ "37482739294298742 - 437843573929483498 is -400360834635184756");
+ MOZ_RELEASE_ASSERT(TestEqual(WrappingAdd(int64_t(-9127837934058953374LL),
+ int64_t(-4173572032144775807LL)),
+ int64_t(5145334107505822435L)),
+ "underflow to positive");
+ MOZ_RELEASE_ASSERT(TestEqual(WrappingAdd(int64_t(-9223372036854775807LL - 1),
+ int64_t(9223372036854775807LL)),
+ int64_t(-1)),
+ "high bit plus all lower bits is -1");
+}
+
+static void TestWrappingAdd() {
+ TestWrappingAdd8();
+ TestWrappingAdd16();
+ TestWrappingAdd32();
+ TestWrappingAdd64();
+}
+
+static void TestWrappingSubtract8() {
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(uint8_t(0), uint8_t(128)), uint8_t(128)),
+ "zero minus half is half");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(uint8_t(17), uint8_t(42)), uint8_t(231)),
+ "17 - 42 == -25 added to 256 is 231");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(uint8_t(0), uint8_t(1)), uint8_t(255)),
+ "zero underflows to all bits");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(uint8_t(128), uint8_t(127)), uint8_t(1)),
+ "128 - 127 == 1");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(uint8_t(128), uint8_t(193)), uint8_t(191)),
+ "128 - 193 is -65 so -65 + 256 == 191");
+
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(int8_t(0), int8_t(-128)), int8_t(-128)),
+ "zero minus high bit wraps to high bit");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(int8_t(-126), int8_t(4)), int8_t(126)),
+ "underflow to positive");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(int8_t(5), int8_t(-123)), int8_t(-128)),
+ "overflow to negative");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(int8_t(-85), int8_t(-73)), int8_t(-12)),
+ "negative minus smaller negative");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(int8_t(-128), int8_t(127)), int8_t(1)),
+ "underflow to 1");
+}
+
+static void TestWrappingSubtract16() {
+ MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint16_t(0), uint16_t(32768)),
+ uint16_t(32768)),
+ "zero minus half is half");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(uint16_t(24389), uint16_t(2682)),
+ uint16_t(21707)),
+ "24389 - 2682 == 21707");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(uint16_t(0), uint16_t(1)), uint16_t(65535)),
+ "zero underflows to all bits");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(uint16_t(32768), uint16_t(32767)),
+ uint16_t(1)),
+ "high bit minus all lower bits is one");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(uint16_t(32768), uint16_t(47582)),
+ uint16_t(50722)),
+ "32768 - 47582 + 65536 is 50722");
+
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(int16_t(0), int16_t(-32768)), int16_t(-32768)),
+ "zero minus high bit wraps to high bit");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(int16_t(-32766), int16_t(4)), int16_t(32766)),
+ "underflow to positive");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(int16_t(5), int16_t(-28933)), int16_t(28938)),
+ "5 - -28933 is 28938");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(int16_t(-23892), int16_t(-12893)),
+ int16_t(-10999)),
+ "negative minus smaller negative");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(int16_t(-32768), int16_t(32767)), int16_t(1)),
+ "underflow to 1");
+}
+
+static void TestWrappingSubtract32() {
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(uint32_t(0), uint32_t(2147483648)),
+ uint32_t(2147483648)),
+ "zero minus half is half");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(uint32_t(1398742328), uint32_t(714192829)),
+ uint32_t(684549499)),
+ "1398742328 - 714192829 == 684549499");
+ MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint32_t(0), uint32_t(1)),
+ uint32_t(4294967295)),
+ "zero underflows to all bits");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(uint32_t(2147483648), uint32_t(2147483647)),
+ uint32_t(1)),
+ "high bit minus all lower bits is one");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(uint32_t(2147483648), uint32_t(3146492712)),
+ uint32_t(3295958232)),
+ "2147483648 - 3146492712 + 4294967296 is 3295958232");
+
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(int32_t(0), int32_t(-2147483647 - 1)),
+ int32_t(-2147483647 - 1)),
+ "zero minus high bit wraps to high bit");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(int32_t(-2147483646), int32_t(4)),
+ int32_t(2147483646)),
+ "underflow to positive");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(int32_t(257), int32_t(-23947248)),
+ int32_t(23947505)),
+ "257 - -23947248 is 23947505");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(int32_t(-2147483220), int32_t(-12893)),
+ int32_t(-2147470327)),
+ "negative minus smaller negative");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(int32_t(-2147483647 - 1), int32_t(2147483647)),
+ int32_t(1)),
+ "underflow to 1");
+}
+
+static void TestWrappingSubtract64() {
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(uint64_t(0), uint64_t(9223372036854775808ULL)),
+ uint64_t(9223372036854775808ULL)),
+ "zero minus half is half");
+ MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint64_t(70368744177664),
+ uint64_t(3740873592)),
+ uint64_t(70365003304072)),
+ "70368744177664 - 3740873592 == 70365003304072");
+ MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint64_t(0), uint64_t(1)),
+ uint64_t(18446744073709551615ULL)),
+ "zero underflows to all bits");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(uint64_t(9223372036854775808ULL),
+ uint64_t(9223372036854775807ULL)),
+ uint64_t(1)),
+ "high bit minus all lower bits is one");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(uint64_t(14552598638644786479ULL),
+ uint64_t(3894174382537247221ULL)),
+ uint64_t(10658424256107539258ULL)),
+ "14552598638644786479 - 39763621533397112216 is 10658424256107539258L");
+
+ MOZ_RELEASE_ASSERT(
+ TestEqual(
+ WrappingSubtract(int64_t(0), int64_t(-9223372036854775807LL - 1)),
+ int64_t(-9223372036854775807LL - 1)),
+ "zero minus high bit wraps to high bit");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(int64_t(-9223372036854775802LL), int64_t(8)),
+ int64_t(9223372036854775806LL)),
+ "overflow to negative");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(int64_t(37482739294298742LL),
+ int64_t(-437843573929483498LL)),
+ int64_t(475326313223782240)),
+ "37482739294298742 - -437843573929483498 is 475326313223782240");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(int64_t(-9127837934058953374LL),
+ int64_t(-4173572032144775807LL)),
+ int64_t(-4954265901914177567LL)),
+ "negative minus smaller negative");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingSubtract(int64_t(-9223372036854775807LL - 1),
+ int64_t(9223372036854775807LL)),
+ int64_t(1)),
+ "underflow to 1");
+}
+
+static void TestWrappingSubtract() {
+ TestWrappingSubtract8();
+ TestWrappingSubtract16();
+ TestWrappingSubtract32();
+ TestWrappingSubtract64();
+}
+
+static void TestWrappingMultiply8() {
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(uint8_t(0), uint8_t(128)), uint8_t(0)),
+ "zero times anything is zero");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(uint8_t(128), uint8_t(1)), uint8_t(128)),
+ "1 times anything is anything");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(uint8_t(2), uint8_t(128)), uint8_t(0)),
+ "2 times high bit overflows, produces zero");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(uint8_t(8), uint8_t(16)), uint8_t(128)),
+ "multiply that populates the high bit produces that value");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(uint8_t(127), uint8_t(127)), uint8_t(1)),
+ "multiplying signed maxvals overflows all the way to 1");
+
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int8_t(0), int8_t(-128)), int8_t(0)),
+ "zero times anything is zero");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int8_t(-128), int8_t(1)), int8_t(-128)),
+ "1 times anything is anything");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int8_t(2), int8_t(-128)), int8_t(0)),
+ "2 times min overflows, produces zero");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int8_t(16), int8_t(24)), int8_t(-128)),
+ "multiply that populates the sign bit produces minval");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int8_t(8), int8_t(16)), int8_t(-128)),
+ "multiply that populates the sign bit produces minval");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int8_t(127), int8_t(127)), int8_t(1)),
+ "multiplying maxvals overflows all the way to 1");
+}
+
+static void TestWrappingMultiply16() {
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(uint16_t(0), uint16_t(32768)), uint16_t(0)),
+ "zero times anything is zero");
+ MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint16_t(32768), uint16_t(1)),
+ uint16_t(32768)),
+ "1 times anything is anything");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(uint16_t(2), uint16_t(32768)), uint16_t(0)),
+ "2 times high bit overflows, produces zero");
+ MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint16_t(3), uint16_t(32768)),
+ uint16_t(-32768)),
+ "3 * 32768 - 65536 is 32768");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(uint16_t(64), uint16_t(512)), uint16_t(32768)),
+ "multiply that populates the high bit produces that value");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(uint16_t(32767), uint16_t(32767)),
+ uint16_t(1)),
+ "multiplying signed maxvals overflows all the way to 1");
+
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int16_t(0), int16_t(-32768)), int16_t(0)),
+ "zero times anything is zero");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int16_t(-32768), int16_t(1)), int16_t(-32768)),
+ "1 times anything is anything");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int16_t(-456), int16_t(123)), int16_t(9448)),
+ "multiply opposite signs, then add 2**16 for the result");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int16_t(2), int16_t(-32768)), int16_t(0)),
+ "2 times min overflows, produces zero");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int16_t(64), int16_t(512)), int16_t(-32768)),
+ "multiply that populates the sign bit produces minval");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int16_t(32767), int16_t(32767)), int16_t(1)),
+ "multiplying maxvals overflows all the way to 1");
+}
+
+static void TestWrappingMultiply32() {
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(uint32_t(0), uint32_t(2147483648)),
+ uint32_t(0)),
+ "zero times anything is zero");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(uint32_t(42), uint32_t(17)), uint32_t(714)),
+ "42 * 17 is 714 without wraparound");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(uint32_t(2147483648), uint32_t(1)),
+ uint32_t(2147483648)),
+ "1 times anything is anything");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(uint32_t(2), uint32_t(2147483648)),
+ uint32_t(0)),
+ "2 times high bit overflows, produces zero");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(uint32_t(8192), uint32_t(262144)),
+ uint32_t(2147483648)),
+ "multiply that populates the high bit produces that value");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(uint32_t(2147483647), uint32_t(2147483647)),
+ uint32_t(1)),
+ "multiplying signed maxvals overflows all the way to 1");
+
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int32_t(0), int32_t(-2147483647 - 1)),
+ int32_t(0)),
+ "zero times anything is zero");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int32_t(-2147483647 - 1), int32_t(1)),
+ int32_t(-2147483647 - 1)),
+ "1 times anything is anything");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int32_t(2), int32_t(-2147483647 - 1)),
+ int32_t(0)),
+ "2 times min overflows, produces zero");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int32_t(-7), int32_t(-9)), int32_t(63)),
+ "-7 * -9 is 63, no wraparound needed");
+ MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int32_t(8192), int32_t(262144)),
+ int32_t(-2147483647 - 1)),
+ "multiply that populates the sign bit produces minval");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int32_t(2147483647), int32_t(2147483647)),
+ int32_t(1)),
+ "multiplying maxvals overflows all the way to 1");
+}
+
+static void TestWrappingMultiply64() {
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(uint64_t(0), uint64_t(9223372036854775808ULL)),
+ uint64_t(0)),
+ "zero times anything is zero");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(uint64_t(9223372036854775808ULL), uint64_t(1)),
+ uint64_t(9223372036854775808ULL)),
+ "1 times anything is anything");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(uint64_t(2), uint64_t(9223372036854775808ULL)),
+ uint64_t(0)),
+ "2 times high bit overflows, produces zero");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(uint64_t(131072), uint64_t(70368744177664)),
+ uint64_t(9223372036854775808ULL)),
+ "multiply that populates the high bit produces that value");
+ MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint64_t(9223372036854775807),
+ uint64_t(9223372036854775807)),
+ uint64_t(1)),
+ "multiplying signed maxvals overflows all the way to 1");
+
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int64_t(0), int64_t(-9223372036854775807 - 1)),
+ int64_t(0)),
+ "zero times anything is zero");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int64_t(-9223372036854775807 - 1), int64_t(1)),
+ int64_t(-9223372036854775807 - 1)),
+ "1 times anything is anything");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int64_t(2), int64_t(-9223372036854775807 - 1)),
+ int64_t(0)),
+ "2 times min overflows, produces zero");
+ MOZ_RELEASE_ASSERT(
+ TestEqual(WrappingMultiply(int64_t(131072), int64_t(70368744177664)),
+ int64_t(-9223372036854775807 - 1)),
+ "multiply that populates the sign bit produces minval");
+ MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int64_t(9223372036854775807),
+ int64_t(9223372036854775807)),
+ int64_t(1)),
+ "multiplying maxvals overflows all the way to 1");
+}
+
+static void TestWrappingMultiply() {
+ TestWrappingMultiply8();
+ TestWrappingMultiply16();
+ TestWrappingMultiply32();
+ TestWrappingMultiply64();
+}
+
+int main() {
+ TestWrappingAdd();
+ TestWrappingSubtract();
+ TestWrappingMultiply();
+ return 0;
+}
diff --git a/mfbt/tests/TestXorShift128PlusRNG.cpp b/mfbt/tests/TestXorShift128PlusRNG.cpp
new file mode 100644
index 0000000000..12b3c547ac
--- /dev/null
+++ b/mfbt/tests/TestXorShift128PlusRNG.cpp
@@ -0,0 +1,101 @@
+/* -*- 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/gtest/TestAlgorithm.cpp b/mfbt/tests/gtest/TestAlgorithm.cpp
new file mode 100644
index 0000000000..a01531fa77
--- /dev/null
+++ b/mfbt/tests/gtest/TestAlgorithm.cpp
@@ -0,0 +1,191 @@
+/* -*- 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 "gtest/gtest.h"
+
+#include "mozilla/Algorithm.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/ResultVariant.h"
+
+#include <iterator>
+#include <vector>
+
+using namespace mozilla;
+using std::begin;
+using std::end;
+
+namespace {
+struct MoveOnly {
+ explicit MoveOnly(int32_t aValue) : mValue{Some(aValue)} {}
+
+ MoveOnly(MoveOnly&&) = default;
+ MoveOnly& operator=(MoveOnly&&) = default;
+
+ Maybe<int32_t> mValue;
+};
+
+struct TestError {};
+
+constexpr static int32_t arr1[3] = {1, 2, 3};
+} // namespace
+
+TEST(MFBT_Algorithm_TransformAbortOnErr, NoError)
+{
+ std::vector<int64_t> out;
+ auto res = TransformAbortOnErr(
+ begin(arr1), end(arr1), std::back_inserter(out),
+ [](const int32_t value) -> Result<int64_t, TestError> {
+ return value * 10;
+ });
+ ASSERT_TRUE(res.isOk());
+
+ const std::vector<int64_t> expected = {10, 20, 30};
+ ASSERT_EQ(expected, out);
+}
+
+TEST(MFBT_Algorithm_TransformAbortOnErr, NoError_Range)
+{
+ std::vector<int64_t> out;
+ auto res = TransformAbortOnErr(
+ arr1, std::back_inserter(out),
+ [](const int32_t value) -> Result<int64_t, TestError> {
+ return value * 10;
+ });
+ ASSERT_TRUE(res.isOk());
+
+ const std::vector<int64_t> expected = {10, 20, 30};
+ ASSERT_EQ(expected, out);
+}
+
+TEST(MFBT_Algorithm_TransformAbortOnErr, ErrorOnFirst)
+{
+ std::vector<int64_t> out;
+ auto res = TransformAbortOnErr(
+ begin(arr1), end(arr1), std::back_inserter(out),
+ [](const int32_t value) -> Result<int64_t, TestError> {
+ return Err(TestError{});
+ });
+ ASSERT_TRUE(res.isErr());
+ ASSERT_TRUE(out.empty());
+}
+
+TEST(MFBT_Algorithm_TransformAbortOnErr, ErrorOnOther)
+{
+ std::vector<int64_t> out;
+ auto res = TransformAbortOnErr(
+ begin(arr1), end(arr1), std::back_inserter(out),
+ [](const int32_t value) -> Result<int64_t, TestError> {
+ if (value > 2) {
+ return Err(TestError{});
+ }
+ return value * 10;
+ });
+ ASSERT_TRUE(res.isErr());
+
+ // XXX Should we assert on this, or is the content of out an implementation
+ // detail?
+ const std::vector<int64_t> expected = {10, 20};
+ ASSERT_EQ(expected, out);
+}
+
+TEST(MFBT_Algorithm_TransformAbortOnErr, ErrorOnOther_Move)
+{
+ MoveOnly in[3] = {MoveOnly{1}, MoveOnly{2}, MoveOnly{3}};
+ std::vector<int64_t> out;
+ auto res = TransformAbortOnErr(
+ std::make_move_iterator(begin(in)), std::make_move_iterator(end(in)),
+ std::back_inserter(out),
+ [](MoveOnly value) -> Result<int64_t, TestError> {
+ if (*value.mValue > 1) {
+ return Err(TestError{});
+ }
+ return *value.mValue * 10;
+ });
+ ASSERT_TRUE(res.isErr());
+
+ ASSERT_FALSE(in[0].mValue);
+ ASSERT_FALSE(in[1].mValue);
+ ASSERT_TRUE(in[2].mValue);
+
+ // XXX Should we assert on this, or is the content of out an implementation
+ // detail?
+ const std::vector<int64_t> expected = {10};
+ ASSERT_EQ(expected, out);
+}
+
+TEST(MFBT_Algorithm_TransformIfAbortOnErr, NoError)
+{
+ std::vector<int64_t> out;
+ auto res = TransformIfAbortOnErr(
+ begin(arr1), end(arr1), std::back_inserter(out),
+ [](const int32_t value) { return value % 2 == 1; },
+ [](const int32_t value) -> Result<int64_t, TestError> {
+ return value * 10;
+ });
+ ASSERT_TRUE(res.isOk());
+
+ const std::vector<int64_t> expected = {10, 30};
+ ASSERT_EQ(expected, out);
+}
+
+TEST(MFBT_Algorithm_TransformIfAbortOnErr, NoError_Range)
+{
+ std::vector<int64_t> out;
+ auto res = TransformIfAbortOnErr(
+ arr1, std::back_inserter(out),
+ [](const int32_t value) { return value % 2 == 1; },
+ [](const int32_t value) -> Result<int64_t, TestError> {
+ return value * 10;
+ });
+ ASSERT_TRUE(res.isOk());
+
+ const std::vector<int64_t> expected = {10, 30};
+ ASSERT_EQ(expected, out);
+}
+
+TEST(MFBT_Algorithm_TransformIfAbortOnErr, ErrorOnOther)
+{
+ std::vector<int64_t> out;
+ auto res = TransformIfAbortOnErr(
+ begin(arr1), end(arr1), std::back_inserter(out),
+ [](const int32_t value) { return value % 2 == 1; },
+ [](const int32_t value) -> Result<int64_t, TestError> {
+ if (value > 2) {
+ return Err(TestError{});
+ }
+ return value * 10;
+ });
+ ASSERT_TRUE(res.isErr());
+
+ const std::vector<int64_t> expected = {10};
+ ASSERT_EQ(expected, out);
+}
+
+TEST(MFBT_Algorithm_TransformIfAbortOnErr, ErrorOnOther_Move)
+{
+ MoveOnly in[3] = {MoveOnly{1}, MoveOnly{2}, MoveOnly{3}};
+ std::vector<int64_t> out;
+ auto res = TransformIfAbortOnErr(
+ std::make_move_iterator(begin(in)), std::make_move_iterator(end(in)),
+ std::back_inserter(out),
+ [](const MoveOnly& value) { return *value.mValue % 2 == 1; },
+ [](MoveOnly value) -> Result<int64_t, TestError> {
+ if (*value.mValue > 1) {
+ return Err(TestError{});
+ }
+ return *value.mValue * 10;
+ });
+ ASSERT_TRUE(res.isErr());
+
+ ASSERT_FALSE(in[0].mValue);
+ ASSERT_TRUE(in[1].mValue);
+ ASSERT_FALSE(in[2].mValue);
+
+ // XXX Should we assert on this, or is the content of out an implementation
+ // detail?
+ const std::vector<int64_t> expected = {10};
+ ASSERT_EQ(expected, out);
+}
diff --git a/mfbt/tests/gtest/TestBuffer.cpp b/mfbt/tests/gtest/TestBuffer.cpp
new file mode 100644
index 0000000000..df36282be1
--- /dev/null
+++ b/mfbt/tests/gtest/TestBuffer.cpp
@@ -0,0 +1,96 @@
+/* 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 "gtest/gtest.h"
+
+#include "mozilla/Buffer.h"
+#include "mozilla/Array.h"
+
+using namespace mozilla;
+
+TEST(Buffer, TestBufferInfallible)
+{
+ const size_t LEN = 8;
+ Array<int32_t, LEN> arr = {1, 2, 3, 4, 5, 6, 7, 8};
+ Buffer<int32_t> buf(arr);
+
+ for (size_t i = 0; i < LEN; i++) {
+ ASSERT_EQ(buf[i], arr[i]);
+ }
+
+ auto iter = buf.begin();
+ auto end = buf.end();
+ for (size_t i = 0; i < LEN; i++) {
+ ASSERT_EQ(*iter, arr[i]);
+ iter++;
+ }
+ ASSERT_EQ(iter, end);
+
+ Span<int32_t> span = buf;
+ for (size_t i = 0; i < LEN; i++) {
+ ASSERT_EQ(span[i], arr[i]);
+ }
+
+ auto spanIter = span.begin();
+ auto spanEnd = span.end();
+ for (size_t i = 0; i < LEN; i++) {
+ ASSERT_EQ(*spanIter, arr[i]);
+ spanIter++;
+ }
+ ASSERT_EQ(spanIter, spanEnd);
+
+ span[3] = 42;
+ ASSERT_EQ(buf[3], 42);
+
+ Buffer<int32_t> another(std::move(buf));
+ ASSERT_EQ(another[3], 42);
+ ASSERT_EQ(buf.Length(), 0U);
+}
+
+TEST(Buffer, TestBufferFallible)
+{
+ const size_t LEN = 8;
+ Array<int32_t, LEN> arr = {1, 2, 3, 4, 5, 6, 7, 8};
+ auto maybe = Buffer<int32_t>::CopyFrom(arr);
+ ASSERT_TRUE(maybe.isSome());
+ Buffer<int32_t> buf(std::move(*maybe));
+
+ for (size_t i = 0; i < LEN; i++) {
+ ASSERT_EQ(buf[i], arr[i]);
+ }
+
+ auto iter = buf.begin();
+ auto end = buf.end();
+ for (size_t i = 0; i < LEN; i++) {
+ ASSERT_EQ(*iter, arr[i]);
+ iter++;
+ }
+ ASSERT_EQ(iter, end);
+
+ Span<int32_t> span = buf;
+ for (size_t i = 0; i < LEN; i++) {
+ ASSERT_EQ(span[i], arr[i]);
+ }
+
+ auto spanIter = span.begin();
+ auto spanEnd = span.end();
+ for (size_t i = 0; i < LEN; i++) {
+ ASSERT_EQ(*spanIter, arr[i]);
+ spanIter++;
+ }
+ ASSERT_EQ(spanIter, spanEnd);
+
+ span[3] = 42;
+ ASSERT_EQ(buf[3], 42);
+
+ Buffer<int32_t> another(std::move(buf));
+ ASSERT_EQ(another[3], 42);
+ ASSERT_EQ(buf.Length(), 0U);
+}
+
+TEST(Buffer, TestBufferElements)
+{
+ ASSERT_EQ(Buffer<int32_t>().Elements(),
+ reinterpret_cast<int32_t*>(alignof(int32_t)));
+}
diff --git a/mfbt/tests/gtest/TestInitializedOnce.cpp b/mfbt/tests/gtest/TestInitializedOnce.cpp
new file mode 100644
index 0000000000..a043013451
--- /dev/null
+++ b/mfbt/tests/gtest/TestInitializedOnce.cpp
@@ -0,0 +1,200 @@
+/* -*- 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 "gtest/gtest.h"
+
+#include "mozilla/InitializedOnce.h"
+
+#include <type_traits>
+
+using namespace mozilla;
+
+namespace {
+template <typename T>
+void AssertIsSome(const T& aVal) {
+ ASSERT_TRUE(aVal);
+ ASSERT_TRUE(aVal.isSome());
+ ASSERT_FALSE(aVal.isNothing());
+}
+
+template <typename T>
+void AssertIsNothing(const T& aVal) {
+ ASSERT_FALSE(aVal);
+ ASSERT_FALSE(aVal.isSome());
+ ASSERT_TRUE(aVal.isNothing());
+}
+
+static_assert(std::is_trivially_destructible_v<InitializedOnce<const int>>);
+static_assert(std::is_trivially_destructible_v<LazyInitializedOnce<const int>>);
+
+static_assert(!std::is_copy_constructible_v<InitializedOnce<const int>>);
+static_assert(!std::is_copy_assignable_v<InitializedOnce<const int>>);
+
+static_assert(!std::is_default_constructible_v<InitializedOnce<const int>>);
+static_assert(std::is_default_constructible_v<LazyInitializedOnce<const int>>);
+static_assert(std::is_default_constructible_v<
+ LazyInitializedOnceEarlyDestructible<const int>>);
+
+// XXX We cannot test for move-constructability/move-assignability at the
+// moment, since the operations are always defined, but trigger static_assert's
+// if they should not be used. This is not too bad, since we are never copyable.
+
+constexpr InitializedOnce<const int>* kPtrInitializedOnceIntLazyInitForbid =
+ nullptr;
+constexpr LazyInitializedOnce<const int>* kPtrInitializedOnceIntLazyInitAllow =
+ nullptr;
+constexpr LazyInitializedOnceEarlyDestructible<const int>*
+ kPtrInitializedOnceIntLazyInitAllowResettable = nullptr;
+
+template <class T, typename = decltype(std::declval<T*>()->destroy())>
+constexpr bool test_has_destroy_method(const T*) {
+ return true;
+}
+constexpr bool test_has_destroy_method(...) { return false; }
+
+static_assert(test_has_destroy_method(kPtrInitializedOnceIntLazyInitForbid));
+static_assert(!test_has_destroy_method(kPtrInitializedOnceIntLazyInitAllow));
+static_assert(
+ test_has_destroy_method(kPtrInitializedOnceIntLazyInitAllowResettable));
+
+template <class T,
+ typename = decltype(std::declval<T*>()->init(std::declval<int>()))>
+constexpr bool test_has_init_method(const T*) {
+ return true;
+}
+constexpr bool test_has_init_method(...) { return false; }
+
+static_assert(!test_has_init_method(kPtrInitializedOnceIntLazyInitForbid));
+static_assert(test_has_init_method(kPtrInitializedOnceIntLazyInitAllow));
+static_assert(
+ test_has_init_method(kPtrInitializedOnceIntLazyInitAllowResettable));
+
+struct MoveOnly {
+ explicit constexpr MoveOnly(int aValue) : mValue{aValue} {}
+
+ MoveOnly(MoveOnly&&) = default;
+ MoveOnly& operator=(MoveOnly&&) = default;
+
+ int mValue;
+};
+
+} // namespace
+
+constexpr int testValue = 32;
+
+TEST(InitializedOnce, ImmediateInit)
+{
+ constexpr InitializedOnce<const MoveOnly> val{testValue};
+
+ // compile-time assertions
+ static_assert(val);
+ static_assert(val.isSome());
+ static_assert(!val.isNothing());
+ static_assert(testValue == (*val).mValue);
+ static_assert(testValue == val->mValue);
+ static_assert(testValue == val.ref().mValue);
+
+ // run-time assertions
+ AssertIsSome(val);
+ ASSERT_EQ(testValue, (*val).mValue);
+ ASSERT_EQ(testValue, val->mValue);
+ ASSERT_EQ(testValue, val.ref().mValue);
+}
+
+TEST(InitializedOnce, ImmediateInitReset)
+{
+ InitializedOnce<const MoveOnly> val{testValue};
+ val.destroy();
+
+ AssertIsNothing(val);
+}
+
+TEST(InitializedOnce, MoveConstruct)
+{
+ InitializedOnce<const MoveOnly> oldVal{testValue};
+ InitializedOnce<const MoveOnly> val{std::move(oldVal)};
+
+ AssertIsNothing(oldVal);
+ AssertIsSome(val);
+}
+
+TEST(InitializedOnceAllowLazy, DefaultCtor)
+{
+ LazyInitializedOnce<const MoveOnly> val;
+
+ AssertIsNothing(val);
+}
+
+TEST(InitializedOnceAllowLazy, Init)
+{
+ LazyInitializedOnce<const MoveOnly> val;
+ val.init(testValue);
+
+ AssertIsSome(val);
+ ASSERT_EQ(testValue, (*val).mValue);
+ ASSERT_EQ(testValue, val->mValue);
+ ASSERT_EQ(testValue, val.ref().mValue);
+}
+
+TEST(InitializedOnceAllowLazy, do_Init)
+{
+ LazyInitializedOnce<const MoveOnly> val;
+ do_Init(val) = MoveOnly{testValue};
+
+ AssertIsSome(val);
+ ASSERT_EQ(testValue, (*val).mValue);
+ ASSERT_EQ(testValue, val->mValue);
+ ASSERT_EQ(testValue, val.ref().mValue);
+}
+
+TEST(InitializedOnceAllowLazyResettable, DefaultCtor)
+{
+ LazyInitializedOnceEarlyDestructible<const MoveOnly> val;
+
+ AssertIsNothing(val);
+}
+
+TEST(InitializedOnceAllowLazyResettable, Init)
+{
+ LazyInitializedOnceEarlyDestructible<const MoveOnly> val;
+ val.init(testValue);
+
+ AssertIsSome(val);
+ ASSERT_EQ(testValue, (*val).mValue);
+ ASSERT_EQ(testValue, val->mValue);
+ ASSERT_EQ(testValue, val.ref().mValue);
+}
+
+TEST(InitializedOnceAllowLazyResettable, InitReset)
+{
+ LazyInitializedOnceEarlyDestructible<const MoveOnly> val;
+ val.init(testValue);
+ val.destroy();
+
+ AssertIsNothing(val);
+}
+
+TEST(InitializedOnceAllowLazyResettable, MoveConstruct)
+{
+ LazyInitializedOnceEarlyDestructible<const MoveOnly> oldVal{testValue};
+ LazyInitializedOnceEarlyDestructible<const MoveOnly> val{std::move(oldVal)};
+
+ AssertIsNothing(oldVal);
+ AssertIsSome(val);
+}
+
+TEST(InitializedOnceAllowLazyResettable, MoveAssign)
+{
+ LazyInitializedOnceEarlyDestructible<const MoveOnly> oldVal{testValue};
+ LazyInitializedOnceEarlyDestructible<const MoveOnly> val;
+
+ val = std::move(oldVal);
+
+ AssertIsNothing(oldVal);
+ AssertIsSome(val);
+}
+
+// XXX How do we test for assertions to be hit?
diff --git a/mfbt/tests/gtest/TestLinkedList.cpp b/mfbt/tests/gtest/TestLinkedList.cpp
new file mode 100644
index 0000000000..d53cba5920
--- /dev/null
+++ b/mfbt/tests/gtest/TestLinkedList.cpp
@@ -0,0 +1,78 @@
+/* -*- 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 "gtest/gtest.h"
+
+#include "mozilla/LinkedList.h"
+#include "mozilla/RefPtr.h"
+
+using mozilla::AutoCleanLinkedList;
+using mozilla::LinkedList;
+using mozilla::LinkedListElement;
+
+class PtrClass : public LinkedListElement<PtrClass> {
+ public:
+ bool* mResult;
+
+ explicit PtrClass(bool* result) : mResult(result) { EXPECT_TRUE(!*mResult); }
+
+ virtual ~PtrClass() { *mResult = true; }
+};
+
+class InheritedPtrClass : public PtrClass {
+ public:
+ bool* mInheritedResult;
+
+ InheritedPtrClass(bool* result, bool* inheritedResult)
+ : PtrClass(result), mInheritedResult(inheritedResult) {
+ EXPECT_TRUE(!*mInheritedResult);
+ }
+
+ virtual ~InheritedPtrClass() { *mInheritedResult = true; }
+};
+
+TEST(LinkedList, AutoCleanLinkedList)
+{
+ bool rv1 = false;
+ bool rv2 = false;
+ bool rv3 = false;
+ {
+ AutoCleanLinkedList<PtrClass> list;
+ list.insertBack(new PtrClass(&rv1));
+ list.insertBack(new InheritedPtrClass(&rv2, &rv3));
+ }
+
+ EXPECT_TRUE(rv1);
+ EXPECT_TRUE(rv2);
+ EXPECT_TRUE(rv3);
+}
+
+class CountedClass final : public LinkedListElement<RefPtr<CountedClass>> {
+ public:
+ int mCount;
+ void AddRef() { mCount++; }
+ void Release() { mCount--; }
+
+ CountedClass() : mCount(0) {}
+ ~CountedClass() { EXPECT_TRUE(mCount == 0); }
+};
+
+TEST(LinkedList, AutoCleanLinkedListRefPtr)
+{
+ RefPtr<CountedClass> elt1 = new CountedClass;
+ CountedClass* elt2 = new CountedClass;
+ {
+ AutoCleanLinkedList<RefPtr<CountedClass>> list;
+ list.insertBack(elt1);
+ list.insertBack(elt2);
+
+ EXPECT_TRUE(elt1->mCount == 2);
+ EXPECT_TRUE(elt2->mCount == 1);
+ }
+
+ EXPECT_TRUE(elt1->mCount == 1);
+ EXPECT_TRUE(elt2->mCount == 0);
+}
diff --git a/mfbt/tests/gtest/TestMainThreadWeakPtr.cpp b/mfbt/tests/gtest/TestMainThreadWeakPtr.cpp
new file mode 100644
index 0000000000..1722ade8c1
--- /dev/null
+++ b/mfbt/tests/gtest/TestMainThreadWeakPtr.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 "gtest/gtest.h"
+
+#include "mozilla/WeakPtr.h"
+#include "mozilla/UniquePtr.h"
+#include <thread>
+
+using namespace mozilla;
+
+struct C : public SupportsWeakPtr {
+ int mNum{0};
+};
+
+struct HasWeakPtrToC {
+ explicit HasWeakPtrToC(C* c) : mPtr(c) {}
+
+ MainThreadWeakPtr<C> mPtr;
+
+ ~HasWeakPtrToC() {
+ MOZ_RELEASE_ASSERT(!NS_IsMainThread(), "Should be released OMT");
+ }
+};
+
+TEST(MFBT_MainThreadWeakPtr, Basic)
+{
+ auto c = MakeUnique<C>();
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ auto weakRef = MakeUnique<HasWeakPtrToC>(c.get());
+
+ std::thread t([weakRef = std::move(weakRef)] {});
+
+ MOZ_RELEASE_ASSERT(!weakRef);
+ c = nullptr;
+
+ t.join();
+}
diff --git a/mfbt/tests/gtest/TestMozDbg.cpp b/mfbt/tests/gtest/TestMozDbg.cpp
new file mode 100644
index 0000000000..24ccd8ed37
--- /dev/null
+++ b/mfbt/tests/gtest/TestMozDbg.cpp
@@ -0,0 +1,170 @@
+/* -*- 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 <iostream>
+#include <type_traits>
+
+#include "gtest/gtest.h"
+#include "mozilla/DbgMacro.h"
+#include "mozilla/Unused.h"
+
+using namespace mozilla;
+
+#define TEST_MOZ_DBG_TYPE_IS(type_, expression_...) \
+ static_assert(std::is_same_v<type_, decltype(MOZ_DBG(expression_))>, \
+ "MOZ_DBG should return the indicated type")
+
+#define TEST_MOZ_DBG_TYPE_SAME(expression_...) \
+ static_assert( \
+ std::is_same_v<decltype((expression_)), decltype(MOZ_DBG(expression_))>, \
+ "MOZ_DBG should return the same type")
+
+struct Number {
+ explicit Number(int aValue) : mValue(aValue) {}
+
+ Number(const Number& aOther) = default;
+
+ Number(Number&& aOther) : mValue(aOther.mValue) { aOther.mValue = 0; }
+
+ Number& operator=(const Number& aOther) = default;
+
+ Number& operator=(Number&& aOther) {
+ mValue = aOther.mValue;
+ aOther.mValue = 0;
+ return *this;
+ }
+
+ ~Number() { mValue = -999; }
+
+ int mValue;
+
+ MOZ_DEFINE_DBG(Number, mValue)
+};
+
+struct MoveOnly {
+ explicit MoveOnly(int aValue) : mValue(aValue) {}
+
+ MoveOnly(const MoveOnly& aOther) = delete;
+
+ MoveOnly(MoveOnly&& aOther) : mValue(aOther.mValue) { aOther.mValue = 0; }
+
+ MoveOnly& operator=(MoveOnly& aOther) = default;
+
+ MoveOnly& operator=(MoveOnly&& aOther) {
+ mValue = aOther.mValue;
+ aOther.mValue = 0;
+ return *this;
+ }
+
+ int mValue;
+
+ MOZ_DEFINE_DBG(MoveOnly)
+};
+
+void StaticAssertions() {
+ int x = 123;
+ Number y(123);
+ Number z(234);
+ MoveOnly w(456);
+
+ // Static assertions.
+
+ // lvalues
+ TEST_MOZ_DBG_TYPE_SAME(x); // int&
+ TEST_MOZ_DBG_TYPE_SAME(y); // Number&
+ TEST_MOZ_DBG_TYPE_SAME(x = 234); // int&
+ TEST_MOZ_DBG_TYPE_SAME(y = z); // Number&
+ TEST_MOZ_DBG_TYPE_SAME(w); // MoveOnly&
+
+ // prvalues (which MOZ_DBG turns into xvalues by creating objects for them)
+ TEST_MOZ_DBG_TYPE_IS(int&&, 123);
+ TEST_MOZ_DBG_TYPE_IS(int&&, 1 + 2);
+ TEST_MOZ_DBG_TYPE_IS(int*&&, &x);
+ TEST_MOZ_DBG_TYPE_IS(int&&, x++);
+ TEST_MOZ_DBG_TYPE_IS(Number&&, Number(123));
+ TEST_MOZ_DBG_TYPE_IS(MoveOnly&&, MoveOnly(123));
+
+ // xvalues
+ TEST_MOZ_DBG_TYPE_SAME(std::move(y)); // int&&
+ TEST_MOZ_DBG_TYPE_SAME(std::move(y)); // Number&&
+ TEST_MOZ_DBG_TYPE_SAME(std::move(w)); // MoveOnly&
+
+ Unused << x;
+ Unused << y;
+ Unused << z;
+}
+
+TEST(MozDbg, ObjectValues)
+{
+ // Test that moves and assignments all operate correctly with MOZ_DBG wrapped
+ // around various parts of the expression.
+
+ Number a(1);
+ Number b(4);
+
+ ASSERT_EQ(a.mValue, 1);
+
+ MOZ_DBG(a.mValue);
+ ASSERT_EQ(a.mValue, 1);
+
+ MOZ_DBG(a.mValue + 1);
+ ASSERT_EQ(a.mValue, 1);
+
+ MOZ_DBG(a.mValue = 2);
+ ASSERT_EQ(a.mValue, 2);
+
+ MOZ_DBG(a).mValue = 3;
+ ASSERT_EQ(a.mValue, 3);
+
+ MOZ_DBG(a = b);
+ ASSERT_EQ(a.mValue, 4);
+ ASSERT_EQ(b.mValue, 4);
+
+ b.mValue = 5;
+ MOZ_DBG(a) = b;
+ ASSERT_EQ(a.mValue, 5);
+ ASSERT_EQ(b.mValue, 5);
+
+ b.mValue = 6;
+ MOZ_DBG(a = std::move(b));
+ ASSERT_EQ(a.mValue, 6);
+ ASSERT_EQ(b.mValue, 0);
+
+ b.mValue = 7;
+ MOZ_DBG(a) = std::move(b);
+ ASSERT_EQ(a.mValue, 7);
+ ASSERT_EQ(b.mValue, 0);
+
+ b.mValue = 8;
+ a = std::move(MOZ_DBG(b));
+ ASSERT_EQ(a.mValue, 8);
+ ASSERT_EQ(b.mValue, 0);
+
+ a = MOZ_DBG(Number(9));
+ ASSERT_EQ(a.mValue, 9);
+
+ MoveOnly c(1);
+ MoveOnly d(2);
+
+ c = std::move(MOZ_DBG(d));
+ ASSERT_EQ(c.mValue, 2);
+ ASSERT_EQ(d.mValue, 0);
+
+ c.mValue = 3;
+ d.mValue = 4;
+ c = MOZ_DBG(std::move(d));
+ ASSERT_EQ(c.mValue, 4);
+ ASSERT_EQ(d.mValue, 0);
+
+ c.mValue = 5;
+ d.mValue = 6;
+ MOZ_DBG(c = std::move(d));
+ ASSERT_EQ(c.mValue, 6);
+ ASSERT_EQ(d.mValue, 0);
+
+ c = MOZ_DBG(MoveOnly(7));
+ ASSERT_EQ(c.mValue, 7);
+}
diff --git a/mfbt/tests/gtest/TestResultExtensions.cpp b/mfbt/tests/gtest/TestResultExtensions.cpp
new file mode 100644
index 0000000000..711e4f33e4
--- /dev/null
+++ b/mfbt/tests/gtest/TestResultExtensions.cpp
@@ -0,0 +1,579 @@
+/* -*- 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 "gtest/gtest.h"
+
+#include "mozilla/ResultExtensions.h"
+#include "nsLocalFile.h"
+
+#include <functional>
+
+using namespace mozilla;
+
+namespace {
+class TestClass {
+ public:
+ static constexpr int kTestValue = 42;
+
+ nsresult NonOverloadedNoInput(int* aOut) {
+ *aOut = kTestValue;
+ return NS_OK;
+ }
+ nsresult NonOverloadedNoInputFails(int* aOut) { return NS_ERROR_FAILURE; }
+
+ nsresult NonOverloadedNoInputConst(int* aOut) const {
+ *aOut = kTestValue;
+ return NS_OK;
+ }
+ nsresult NonOverloadedNoInputFailsConst(int* aOut) const {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult NonOverloadedNoInputRef(int& aOut) {
+ aOut = kTestValue;
+ return NS_OK;
+ }
+ nsresult NonOverloadedNoInputFailsRef(int& aOut) { return NS_ERROR_FAILURE; }
+
+ nsresult NonOverloadedNoInputComplex(std::pair<int, int>* aOut) {
+ *aOut = std::pair{kTestValue, kTestValue};
+ return NS_OK;
+ }
+ nsresult NonOverloadedNoInputFailsComplex(std::pair<int, int>* aOut) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult NonOverloadedWithInput(int aIn, int* aOut) {
+ *aOut = aIn;
+ return NS_OK;
+ }
+ nsresult NonOverloadedWithInputFails(int aIn, int* aOut) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult NonOverloadedNoOutput(int aIn) { return NS_OK; }
+ nsresult NonOverloadedNoOutputFails(int aIn) { return NS_ERROR_FAILURE; }
+
+ nsresult PolymorphicNoInput(nsIFile** aOut) {
+ *aOut = MakeAndAddRef<nsLocalFile>().take();
+ return NS_OK;
+ }
+ nsresult PolymorphicNoInputFails(nsIFile** aOut) { return NS_ERROR_FAILURE; }
+};
+
+class RefCountedTestClass {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(RefCountedTestClass);
+
+ static constexpr int kTestValue = 42;
+
+ nsresult NonOverloadedNoInput(int* aOut) {
+ *aOut = kTestValue;
+ return NS_OK;
+ }
+ nsresult NonOverloadedNoInputFails(int* aOut) { return NS_ERROR_FAILURE; }
+
+ private:
+ ~RefCountedTestClass() = default;
+};
+
+// Check that DerefedType deduces the types as expected
+static_assert(std::is_same_v<mozilla::detail::DerefedType<RefCountedTestClass&>,
+ RefCountedTestClass>);
+static_assert(std::is_same_v<mozilla::detail::DerefedType<RefCountedTestClass*>,
+ RefCountedTestClass>);
+static_assert(
+ std::is_same_v<mozilla::detail::DerefedType<RefPtr<RefCountedTestClass>>,
+ RefCountedTestClass>);
+
+static_assert(std::is_same_v<mozilla::detail::DerefedType<nsIFile&>, nsIFile>);
+static_assert(std::is_same_v<mozilla::detail::DerefedType<nsIFile*>, nsIFile>);
+static_assert(
+ std::is_same_v<mozilla::detail::DerefedType<nsCOMPtr<nsIFile>>, nsIFile>);
+} // namespace
+
+TEST(ResultExtensions_ToResultInvoke, Lambda_NoInput)
+{
+ TestClass foo;
+
+ // success
+ {
+ auto valOrErr = ToResultInvoke<int>(
+ [&foo](int* out) { return foo.NonOverloadedNoInput(out); });
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap());
+ }
+
+ // failure
+ {
+ auto valOrErr = ToResultInvoke<int>(
+ [&foo](int* out) { return foo.NonOverloadedNoInputFails(out); });
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isErr());
+ ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr());
+ }
+}
+
+TEST(ResultExtensions_ToResultInvoke, MemFn_NoInput)
+{
+ TestClass foo;
+
+ // success
+ {
+ auto valOrErr =
+ ToResultInvoke<int>(std::mem_fn(&TestClass::NonOverloadedNoInput), foo);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap());
+ }
+
+ // failure
+ {
+ auto valOrErr = ToResultInvoke<int>(
+ std::mem_fn(&TestClass::NonOverloadedNoInputFails), foo);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isErr());
+ ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr());
+ }
+}
+
+TEST(ResultExtensions_ToResultInvoke, MemFn_Polymorphic_NoInput)
+{
+ TestClass foo;
+
+ // success
+ {
+ auto valOrErr = ToResultInvoke<nsCOMPtr<nsIFile>>(
+ std::mem_fn(&TestClass::PolymorphicNoInput), foo);
+ static_assert(std::is_same_v<decltype(valOrErr),
+ Result<nsCOMPtr<nsIFile>, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ ASSERT_NE(nullptr, valOrErr.inspect());
+
+ ASSERT_EQ(ToResultInvoke<nsString>(std::mem_fn(&nsIFile::GetPath),
+ *MakeRefPtr<nsLocalFile>())
+ .inspect(),
+ ToResultInvoke<nsString>(std::mem_fn(&nsIFile::GetPath),
+ valOrErr.inspect())
+ .inspect());
+ }
+
+ // failure
+ {
+ auto valOrErr = ToResultInvoke<nsCOMPtr<nsIFile>>(
+ std::mem_fn(&TestClass::PolymorphicNoInputFails), foo);
+ static_assert(std::is_same_v<decltype(valOrErr),
+ Result<nsCOMPtr<nsIFile>, nsresult>>);
+ ASSERT_TRUE(valOrErr.isErr());
+ ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr());
+ }
+}
+
+TEST(ResultExtensions_ToResultInvokeMember, NoInput)
+{
+ TestClass foo;
+
+ // success
+ {
+ auto valOrErr = ToResultInvokeMember(foo, &TestClass::NonOverloadedNoInput);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap());
+ }
+
+ // failure
+ {
+ auto valOrErr =
+ ToResultInvokeMember(foo, &TestClass::NonOverloadedNoInputFails);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isErr());
+ ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr());
+ }
+}
+
+TEST(ResultExtensions_ToResultInvokeMember, NoInput_Const)
+{
+ const TestClass foo;
+
+ // success
+ {
+ auto valOrErr =
+ ToResultInvokeMember(foo, &TestClass::NonOverloadedNoInputConst);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap());
+ }
+
+ // failure
+ {
+ auto valOrErr =
+ ToResultInvokeMember(foo, &TestClass::NonOverloadedNoInputFailsConst);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isErr());
+ ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr());
+ }
+}
+
+TEST(ResultExtensions_ToResultInvokeMember, NoInput_Ref)
+{
+ TestClass foo;
+
+ // success
+ {
+ auto valOrErr =
+ ToResultInvokeMember(foo, &TestClass::NonOverloadedNoInputRef);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap());
+ }
+
+ // failure
+ {
+ auto valOrErr =
+ ToResultInvokeMember(foo, &TestClass::NonOverloadedNoInputFailsRef);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isErr());
+ ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr());
+ }
+}
+
+TEST(ResultExtensions_ToResultInvokeMember, NoInput_Complex)
+{
+ TestClass foo;
+
+ // success
+ {
+ auto valOrErr =
+ ToResultInvokeMember(foo, &TestClass::NonOverloadedNoInputComplex);
+ static_assert(std::is_same_v<decltype(valOrErr),
+ Result<std::pair<int, int>, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ ASSERT_EQ((std::pair{TestClass::kTestValue, TestClass::kTestValue}),
+ valOrErr.unwrap());
+ }
+
+ // failure
+ {
+ auto valOrErr =
+ ToResultInvokeMember(foo, &TestClass::NonOverloadedNoInputFailsComplex);
+ static_assert(std::is_same_v<decltype(valOrErr),
+ Result<std::pair<int, int>, nsresult>>);
+ ASSERT_TRUE(valOrErr.isErr());
+ ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr());
+ }
+}
+
+TEST(ResultExtensions_ToResultInvokeMember, WithInput)
+{
+ TestClass foo;
+
+ // success
+ {
+ auto valOrErr = ToResultInvokeMember(
+ foo, &TestClass::NonOverloadedWithInput, -TestClass::kTestValue);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ ASSERT_EQ(-TestClass::kTestValue, valOrErr.unwrap());
+ }
+
+ // failure
+ {
+ auto valOrErr = ToResultInvokeMember(
+ foo, &TestClass::NonOverloadedWithInputFails, -TestClass::kTestValue);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isErr());
+ ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr());
+ }
+}
+
+TEST(ResultExtensions_ToResultInvokeMember, NoOutput)
+{
+ TestClass foo;
+
+ // success
+ {
+ auto valOrErr = ToResultInvokeMember(foo, &TestClass::NonOverloadedNoOutput,
+ -TestClass::kTestValue);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<Ok, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ }
+
+ // failure
+ {
+ auto valOrErr = ToResultInvokeMember(
+ foo, &TestClass::NonOverloadedNoOutputFails, -TestClass::kTestValue);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<Ok, nsresult>>);
+ ASSERT_TRUE(valOrErr.isErr());
+ ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr());
+ }
+}
+
+TEST(ResultExtensions_ToResultInvokeMember, NoInput_Macro)
+{
+ TestClass foo;
+
+ // success
+ {
+ auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInput);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap());
+ }
+
+ // failure
+ {
+ auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInputFails);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isErr());
+ ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr());
+ }
+}
+
+TEST(ResultExtensions_ToResultInvokeMember, NoInput_Const_Macro)
+{
+ const TestClass foo;
+
+ // success
+ {
+ auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInputConst);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap());
+ }
+
+ // failure
+ {
+ auto valOrErr =
+ MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInputFailsConst);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isErr());
+ ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr());
+ }
+}
+
+TEST(ResultExtensions_ToResultInvokeMember, NoInput_Ref_Macro)
+{
+ TestClass foo;
+
+ // success
+ {
+ auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInputRef);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap());
+ }
+
+ // failure
+ {
+ auto valOrErr =
+ MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInputFailsRef);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isErr());
+ ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr());
+ }
+}
+
+TEST(ResultExtensions_ToResultInvokeMember, NoInput_Complex_Macro)
+{
+ TestClass foo;
+
+ // success
+ {
+ auto valOrErr =
+ MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInputComplex);
+ static_assert(std::is_same_v<decltype(valOrErr),
+ Result<std::pair<int, int>, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ ASSERT_EQ((std::pair{TestClass::kTestValue, TestClass::kTestValue}),
+ valOrErr.unwrap());
+ }
+
+ // failure
+ {
+ auto valOrErr =
+ MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInputFailsComplex);
+
+ static_assert(std::is_same_v<decltype(valOrErr),
+ Result<std::pair<int, int>, nsresult>>);
+ ASSERT_TRUE(valOrErr.isErr());
+ ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr());
+ }
+}
+
+TEST(ResultExtensions_ToResultInvokeMember, WithInput_Macro)
+{
+ TestClass foo;
+
+ // success
+ {
+ auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedWithInput,
+ -TestClass::kTestValue);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ ASSERT_EQ(-TestClass::kTestValue, valOrErr.unwrap());
+ }
+
+ // failure
+ {
+ auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(
+ foo, NonOverloadedWithInputFails, -TestClass::kTestValue);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isErr());
+ ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr());
+ }
+}
+
+TEST(ResultExtensions_ToResultInvokeMember, NoOutput_Macro)
+{
+ TestClass foo;
+
+ // success
+ {
+ auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoOutput,
+ -TestClass::kTestValue);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<Ok, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ }
+
+ // failure
+ {
+ auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoOutputFails,
+ -TestClass::kTestValue);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<Ok, nsresult>>);
+ ASSERT_TRUE(valOrErr.isErr());
+ ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr());
+ }
+}
+
+TEST(ResultExtensions_ToResultInvokeMember, NoInput_Complex_Macro_Typed)
+{
+ TestClass foo;
+
+ // success
+ {
+ auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
+ (std::pair<int, int>), foo, NonOverloadedNoInputComplex);
+ static_assert(std::is_same_v<decltype(valOrErr),
+ Result<std::pair<int, int>, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ ASSERT_EQ((std::pair{TestClass::kTestValue, TestClass::kTestValue}),
+ valOrErr.unwrap());
+ }
+
+ // failure
+ {
+ auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
+ (std::pair<int, int>), foo, NonOverloadedNoInputFailsComplex);
+ static_assert(std::is_same_v<decltype(valOrErr),
+ Result<std::pair<int, int>, nsresult>>);
+ ASSERT_TRUE(valOrErr.isErr());
+ ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr());
+ }
+}
+
+TEST(ResultExtensions_ToResultInvokeMember, RefPtr_NoInput)
+{
+ auto foo = MakeRefPtr<RefCountedTestClass>();
+
+ // success
+ {
+ auto valOrErr =
+ ToResultInvokeMember(foo, &RefCountedTestClass::NonOverloadedNoInput);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap());
+ }
+
+ // failure
+ {
+ auto valOrErr = ToResultInvokeMember(
+ foo, &RefCountedTestClass::NonOverloadedNoInputFails);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isErr());
+ ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr());
+ }
+}
+
+TEST(ResultExtensions_ToResultInvokeMember, RefPtr_NoInput_Macro)
+{
+ auto foo = MakeRefPtr<RefCountedTestClass>();
+
+ // success
+ {
+ auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInput);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap());
+ }
+
+ // failure
+ {
+ auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInputFails);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isErr());
+ ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr());
+ }
+}
+
+TEST(ResultExtensions_ToResultInvokeMember, RawPtr_NoInput_Macro)
+{
+ auto foo = MakeRefPtr<RefCountedTestClass>();
+ auto* fooPtr = foo.get();
+
+ // success
+ {
+ auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(fooPtr, NonOverloadedNoInput);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap());
+ }
+
+ // failure
+ {
+ auto valOrErr =
+ MOZ_TO_RESULT_INVOKE_MEMBER(fooPtr, NonOverloadedNoInputFails);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>);
+ ASSERT_TRUE(valOrErr.isErr());
+ ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr());
+ }
+}
+
+TEST(ResultExtensions_ToResultInvokeMember, nsCOMPtr_AbstractClass_WithInput)
+{
+ nsCOMPtr<nsIFile> file = MakeAndAddRef<nsLocalFile>();
+
+ auto valOrErr = ToResultInvokeMember(file, &nsIFile::Equals, file);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<bool, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ ASSERT_EQ(true, valOrErr.unwrap());
+}
+
+TEST(ResultExtensions_ToResultInvokeMember,
+ RawPtr_AbstractClass_WithInput_Macro)
+{
+ nsCOMPtr<nsIFile> file = MakeAndAddRef<nsLocalFile>();
+ auto* filePtr = file.get();
+
+ auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(filePtr, Equals, file);
+ static_assert(std::is_same_v<decltype(valOrErr), Result<bool, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ ASSERT_EQ(true, valOrErr.unwrap());
+}
+
+TEST(ResultExtensions_ToResultInvokeMember,
+ RawPtr_AbstractClass_NoInput_Macro_Typed)
+{
+ nsCOMPtr<nsIFile> file = MakeAndAddRef<nsLocalFile>();
+ auto* filePtr = file.get();
+
+ auto valOrErr =
+ MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCOMPtr<nsIFile>, filePtr, Clone);
+ static_assert(
+ std::is_same_v<decltype(valOrErr), Result<nsCOMPtr<nsIFile>, nsresult>>);
+ ASSERT_TRUE(valOrErr.isOk());
+ ASSERT_NE(nullptr, valOrErr.unwrap());
+}
diff --git a/mfbt/tests/gtest/TestReverseIterator.cpp b/mfbt/tests/gtest/TestReverseIterator.cpp
new file mode 100644
index 0000000000..a1ba019aa1
--- /dev/null
+++ b/mfbt/tests/gtest/TestReverseIterator.cpp
@@ -0,0 +1,104 @@
+/* -*- 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 "gtest/gtest.h"
+
+#include "mozilla/ReverseIterator.h"
+
+using namespace mozilla;
+
+TEST(ReverseIterator, Const_RangeBasedFor)
+{
+ const std::vector<int> in = {1, 2, 3, 4};
+ const auto reversedRange =
+ detail::IteratorRange<ReverseIterator<std::vector<int>::const_iterator>>{
+ ReverseIterator{in.end()}, ReverseIterator{in.begin()}};
+
+ const std::vector<int> expected = {4, 3, 2, 1};
+ std::vector<int> out;
+ for (auto i : reversedRange) {
+ out.emplace_back(i);
+ }
+
+ EXPECT_EQ(expected, out);
+}
+
+TEST(ReverseIterator, NonConst_RangeBasedFor)
+{
+ std::vector<int> in = {1, 2, 3, 4};
+ auto reversedRange =
+ detail::IteratorRange<ReverseIterator<std::vector<int>::iterator>>{
+ ReverseIterator{in.end()}, ReverseIterator{in.begin()}};
+
+ const std::vector<int> expected = {-1, -2, -3, -4};
+ for (auto& i : reversedRange) {
+ i = -i;
+ }
+
+ EXPECT_EQ(expected, in);
+}
+
+TEST(ReverseIterator, Difference)
+{
+ const std::vector<int> in = {1, 2, 3, 4};
+ using reverse_iterator = ReverseIterator<std::vector<int>::const_iterator>;
+
+ reverse_iterator rbegin = reverse_iterator{in.end()},
+ rend = reverse_iterator{in.begin()};
+ EXPECT_EQ(4, rend - rbegin);
+ EXPECT_EQ(0, rend - rend);
+ EXPECT_EQ(0, rbegin - rbegin);
+
+ --rend;
+ EXPECT_EQ(3, rend - rbegin);
+
+ ++rbegin;
+ EXPECT_EQ(2, rend - rbegin);
+
+ rend--;
+ EXPECT_EQ(1, rend - rbegin);
+
+ rbegin++;
+ EXPECT_EQ(0, rend - rbegin);
+}
+
+TEST(ReverseIterator, Comparison)
+{
+ const std::vector<int> in = {1, 2, 3, 4};
+ using reverse_iterator = ReverseIterator<std::vector<int>::const_iterator>;
+
+ reverse_iterator rbegin = reverse_iterator{in.end()},
+ rend = reverse_iterator{in.begin()};
+ EXPECT_TRUE(rbegin < rend);
+ EXPECT_FALSE(rend < rbegin);
+ EXPECT_FALSE(rend < rend);
+ EXPECT_FALSE(rbegin < rbegin);
+
+ EXPECT_TRUE(rend > rbegin);
+ EXPECT_FALSE(rbegin > rend);
+ EXPECT_FALSE(rend > rend);
+ EXPECT_FALSE(rbegin > rbegin);
+
+ EXPECT_TRUE(rbegin <= rend);
+ EXPECT_FALSE(rend <= rbegin);
+ EXPECT_TRUE(rend <= rend);
+ EXPECT_TRUE(rbegin <= rbegin);
+
+ EXPECT_TRUE(rend >= rbegin);
+ EXPECT_FALSE(rbegin >= rend);
+ EXPECT_TRUE(rend >= rend);
+ EXPECT_TRUE(rbegin >= rbegin);
+
+ EXPECT_FALSE(rend == rbegin);
+ EXPECT_FALSE(rbegin == rend);
+ EXPECT_TRUE(rend == rend);
+ EXPECT_TRUE(rbegin == rbegin);
+
+ EXPECT_TRUE(rend != rbegin);
+ EXPECT_TRUE(rbegin != rend);
+ EXPECT_FALSE(rend != rend);
+ EXPECT_FALSE(rbegin != rbegin);
+}
diff --git a/mfbt/tests/gtest/TestSpan.cpp b/mfbt/tests/gtest/TestSpan.cpp
new file mode 100644
index 0000000000..fb7db0d158
--- /dev/null
+++ b/mfbt/tests/gtest/TestSpan.cpp
@@ -0,0 +1,2355 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+// Adapted from
+// https://github.com/Microsoft/GSL/blob/3819df6e378ffccf0e29465afe99c3b324c2aa70/tests/Span_tests.cpp
+
+#include "gtest/gtest.h"
+
+#include "mozilla/Array.h"
+#include "mozilla/Span.h"
+
+#include "nsString.h"
+#include "nsTArray.h"
+#include "mozilla/Range.h"
+
+#include <type_traits>
+
+#define SPAN_TEST(name) TEST(SpanTest, name)
+#define CHECK_THROW(a, b)
+
+using namespace mozilla;
+
+static_assert(std::is_convertible_v<Range<int>, Span<const int>>,
+ "Range should convert into const");
+static_assert(std::is_convertible_v<Range<const int>, Span<const int>>,
+ "const Range should convert into const");
+static_assert(!std::is_convertible_v<Range<const int>, Span<int>>,
+ "Range should not drop const in conversion");
+static_assert(std::is_convertible_v<Span<int>, Range<const int>>,
+ "Span should convert into const");
+static_assert(std::is_convertible_v<Span<const int>, Range<const int>>,
+ "const Span should convert into const");
+static_assert(!std::is_convertible_v<Span<const int>, Range<int>>,
+ "Span should not drop const in conversion");
+static_assert(std::is_convertible_v<Span<const int>, Span<const int>>,
+ "const Span should convert into const");
+static_assert(std::is_convertible_v<Span<int>, Span<const int>>,
+ "Span should convert into const");
+static_assert(!std::is_convertible_v<Span<const int>, Span<int>>,
+ "Span should not drop const in conversion");
+static_assert(std::is_convertible_v<const nsTArray<int>, Span<const int>>,
+ "const nsTArray should convert into const");
+static_assert(std::is_convertible_v<nsTArray<int>, Span<const int>>,
+ "nsTArray should convert into const");
+static_assert(!std::is_convertible_v<const nsTArray<int>, Span<int>>,
+ "nsTArray should not drop const in conversion");
+static_assert(std::is_convertible_v<nsTArray<const int>, Span<const int>>,
+ "nsTArray should convert into const");
+static_assert(!std::is_convertible_v<nsTArray<const int>, Span<int>>,
+ "nsTArray should not drop const in conversion");
+
+static_assert(std::is_convertible_v<const std::vector<int>, Span<const int>>,
+ "const std::vector should convert into const");
+static_assert(std::is_convertible_v<std::vector<int>, Span<const int>>,
+ "std::vector should convert into const");
+static_assert(!std::is_convertible_v<const std::vector<int>, Span<int>>,
+ "std::vector should not drop const in conversion");
+
+/**
+ * Rust slice-compatible nullptr replacement value.
+ */
+#define SLICE_CONST_INT_PTR reinterpret_cast<const int*>(alignof(const int))
+
+/**
+ * Rust slice-compatible nullptr replacement value.
+ */
+#define SLICE_INT_PTR reinterpret_cast<int*>(alignof(int))
+
+/**
+ * Rust slice-compatible nullptr replacement value.
+ */
+#define SLICE_CONST_INT_PTR_PTR \
+ reinterpret_cast<const int**>(alignof(const int*))
+
+/**
+ * Rust slice-compatible nullptr replacement value.
+ */
+#define SLICE_INT_PTR_PTR reinterpret_cast<int**>(alignof(int*))
+
+namespace {
+struct BaseClass {};
+struct DerivedClass : BaseClass {};
+} // namespace
+
+void AssertSpanOfThreeInts(Span<const int> s) {
+ ASSERT_EQ(s.size(), 3U);
+ ASSERT_EQ(s[0], 1);
+ ASSERT_EQ(s[1], 2);
+ ASSERT_EQ(s[2], 3);
+}
+
+void AssertSpanOfThreeChars(Span<const char> s) {
+ ASSERT_EQ(s.size(), 3U);
+ ASSERT_EQ(s[0], 'a');
+ ASSERT_EQ(s[1], 'b');
+ ASSERT_EQ(s[2], 'c');
+}
+
+void AssertSpanOfThreeChar16s(Span<const char16_t> s) {
+ ASSERT_EQ(s.size(), 3U);
+ ASSERT_EQ(s[0], 'a');
+ ASSERT_EQ(s[1], 'b');
+ ASSERT_EQ(s[2], 'c');
+}
+
+void AssertSpanOfThreeCharsViaString(const nsACString& aStr) {
+ AssertSpanOfThreeChars(aStr);
+}
+
+void AssertSpanOfThreeChar16sViaString(const nsAString& aStr) {
+ AssertSpanOfThreeChar16s(aStr);
+}
+
+SPAN_TEST(default_constructor) {
+ {
+ Span<int> s;
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), SLICE_INT_PTR);
+
+ Span<const int> cs;
+ ASSERT_EQ(cs.Length(), 0U);
+ ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR);
+ }
+
+ {
+ Span<int, 0> s;
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), SLICE_INT_PTR);
+
+ Span<const int, 0> cs;
+ ASSERT_EQ(cs.Length(), 0U);
+ ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR);
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ Span<int, 1> s;
+ ASSERT_EQ(s.Length(), 1U);
+ ASSERT_EQ(s.data(), SLICE_INT_PTR); // explains why it can't compile
+#endif
+ }
+
+ {
+ Span<int> s{};
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), SLICE_INT_PTR);
+
+ Span<const int> cs{};
+ ASSERT_EQ(cs.Length(), 0U);
+ ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR);
+ }
+}
+
+SPAN_TEST(size_optimization) {
+ {
+ Span<int> s;
+ ASSERT_EQ(sizeof(s), sizeof(int*) + sizeof(size_t));
+ }
+
+ {
+ Span<int, 0> s;
+ ASSERT_EQ(sizeof(s), sizeof(int*));
+ }
+}
+
+SPAN_TEST(from_nullptr_constructor) {
+ {
+ Span<int> s = nullptr;
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), SLICE_INT_PTR);
+
+ Span<const int> cs = nullptr;
+ ASSERT_EQ(cs.Length(), 0U);
+ ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR);
+ }
+
+ {
+ Span<int, 0> s = nullptr;
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), SLICE_INT_PTR);
+
+ Span<const int, 0> cs = nullptr;
+ ASSERT_EQ(cs.Length(), 0U);
+ ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR);
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ Span<int, 1> s = nullptr;
+ ASSERT_EQ(s.Length(), 1U);
+ ASSERT_EQ(s.data(), SLICE_INT_PTR); // explains why it can't compile
+#endif
+ }
+
+ {
+ Span<int> s{nullptr};
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), SLICE_INT_PTR);
+
+ Span<const int> cs{nullptr};
+ ASSERT_EQ(cs.Length(), 0U);
+ ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR);
+ }
+
+ {
+ Span<int*> s{nullptr};
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), SLICE_INT_PTR_PTR);
+
+ Span<const int*> cs{nullptr};
+ ASSERT_EQ(cs.Length(), 0U);
+ ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR_PTR);
+ }
+}
+
+SPAN_TEST(from_nullptr_length_constructor) {
+ {
+ Span<int> s{nullptr, static_cast<Span<int>::index_type>(0)};
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), SLICE_INT_PTR);
+
+ Span<const int> cs{nullptr, static_cast<Span<int>::index_type>(0)};
+ ASSERT_EQ(cs.Length(), 0U);
+ ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR);
+ }
+
+ {
+ Span<int, 0> s{nullptr, static_cast<Span<int>::index_type>(0)};
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), SLICE_INT_PTR);
+
+ Span<const int, 0> cs{nullptr, static_cast<Span<int>::index_type>(0)};
+ ASSERT_EQ(cs.Length(), 0U);
+ ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR);
+ }
+
+#if 0
+ {
+ auto workaround_macro = []() { Span<int, 1> s{ nullptr, static_cast<Span<int>::index_type>(0) }; };
+ CHECK_THROW(workaround_macro(), fail_fast);
+ }
+
+ {
+ auto workaround_macro = []() { Span<int> s{nullptr, 1}; };
+ CHECK_THROW(workaround_macro(), fail_fast);
+
+ auto const_workaround_macro = []() { Span<const int> cs{nullptr, 1}; };
+ CHECK_THROW(const_workaround_macro(), fail_fast);
+ }
+
+ {
+ auto workaround_macro = []() { Span<int, 0> s{nullptr, 1}; };
+ CHECK_THROW(workaround_macro(), fail_fast);
+
+ auto const_workaround_macro = []() { Span<const int, 0> s{nullptr, 1}; };
+ CHECK_THROW(const_workaround_macro(), fail_fast);
+ }
+#endif
+ {
+ Span<int*> s{nullptr, static_cast<Span<int>::index_type>(0)};
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), SLICE_INT_PTR_PTR);
+
+ Span<const int*> cs{nullptr, static_cast<Span<int>::index_type>(0)};
+ ASSERT_EQ(cs.Length(), 0U);
+ ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR_PTR);
+ }
+}
+
+SPAN_TEST(from_pointer_length_constructor) {
+ int arr[4] = {1, 2, 3, 4};
+
+ {
+ Span<int> s{&arr[0], 2};
+ ASSERT_EQ(s.Length(), 2U);
+ ASSERT_EQ(s.data(), &arr[0]);
+ ASSERT_EQ(s[0], 1);
+ ASSERT_EQ(s[1], 2);
+ }
+
+ {
+ Span<int, 2> s{&arr[0], 2};
+ ASSERT_EQ(s.Length(), 2U);
+ ASSERT_EQ(s.data(), &arr[0]);
+ ASSERT_EQ(s[0], 1);
+ ASSERT_EQ(s[1], 2);
+ }
+
+ {
+ int* p = nullptr;
+ Span<int> s{p, static_cast<Span<int>::index_type>(0)};
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), SLICE_INT_PTR);
+ }
+
+#if 0
+ {
+ int* p = nullptr;
+ auto workaround_macro = [=]() { Span<int> s{p, 2}; };
+ CHECK_THROW(workaround_macro(), fail_fast);
+ }
+#endif
+
+ {
+ auto s = Span(&arr[0], 2);
+ ASSERT_EQ(s.Length(), 2U);
+ ASSERT_EQ(s.data(), &arr[0]);
+ ASSERT_EQ(s[0], 1);
+ ASSERT_EQ(s[1], 2);
+ }
+
+ {
+ int* p = nullptr;
+ auto s = Span(p, static_cast<Span<int>::index_type>(0));
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), SLICE_INT_PTR);
+ }
+
+#if 0
+ {
+ int* p = nullptr;
+ auto workaround_macro = [=]() { Span(p, 2); };
+ CHECK_THROW(workaround_macro(), fail_fast);
+ }
+#endif
+}
+
+SPAN_TEST(from_pointer_pointer_constructor) {
+ int arr[4] = {1, 2, 3, 4};
+
+ {
+ Span<int> s{&arr[0], &arr[2]};
+ ASSERT_EQ(s.Length(), 2U);
+ ASSERT_EQ(s.data(), &arr[0]);
+ ASSERT_EQ(s[0], 1);
+ ASSERT_EQ(s[1], 2);
+ }
+
+ {
+ Span<int, 2> s{&arr[0], &arr[2]};
+ ASSERT_EQ(s.Length(), 2U);
+ ASSERT_EQ(s.data(), &arr[0]);
+ ASSERT_EQ(s[0], 1);
+ ASSERT_EQ(s[1], 2);
+ }
+
+ {
+ Span<int> s{&arr[0], &arr[0]};
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), &arr[0]);
+ }
+
+ {
+ Span<int, 0> s{&arr[0], &arr[0]};
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), &arr[0]);
+ }
+
+ // this will fail the std::distance() precondition, which asserts on MSVC
+ // debug builds
+ //{
+ // auto workaround_macro = [&]() { Span<int> s{&arr[1], &arr[0]}; };
+ // CHECK_THROW(workaround_macro(), fail_fast);
+ //}
+
+ // this will fail the std::distance() precondition, which asserts on MSVC
+ // debug builds
+ //{
+ // int* p = nullptr;
+ // auto workaround_macro = [&]() { Span<int> s{&arr[0], p}; };
+ // CHECK_THROW(workaround_macro(), fail_fast);
+ //}
+
+ {
+ int* p = nullptr;
+ Span<int> s{p, p};
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), SLICE_INT_PTR);
+ }
+
+ {
+ int* p = nullptr;
+ Span<int, 0> s{p, p};
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), SLICE_INT_PTR);
+ }
+
+ // this will fail the std::distance() precondition, which asserts on MSVC
+ // debug builds
+ //{
+ // int* p = nullptr;
+ // auto workaround_macro = [&]() { Span<int> s{&arr[0], p}; };
+ // CHECK_THROW(workaround_macro(), fail_fast);
+ //}
+
+ {
+ auto s = Span(&arr[0], &arr[2]);
+ ASSERT_EQ(s.Length(), 2U);
+ ASSERT_EQ(s.data(), &arr[0]);
+ ASSERT_EQ(s[0], 1);
+ ASSERT_EQ(s[1], 2);
+ }
+
+ {
+ auto s = Span(&arr[0], &arr[0]);
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), &arr[0]);
+ }
+
+ {
+ int* p = nullptr;
+ auto s = Span(p, p);
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), SLICE_INT_PTR);
+ }
+}
+
+SPAN_TEST(from_array_constructor) {
+ int arr[5] = {1, 2, 3, 4, 5};
+
+ {
+ Span<int> s{arr};
+ ASSERT_EQ(s.Length(), 5U);
+ ASSERT_EQ(s.data(), &arr[0]);
+ }
+
+ {
+ Span<int, 5> s{arr};
+ ASSERT_EQ(s.Length(), 5U);
+ ASSERT_EQ(s.data(), &arr[0]);
+ }
+
+ int arr2d[2][3] = {{1, 2, 3}, {4, 5, 6}};
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ { Span<int, 6> s{arr}; }
+
+ {
+ Span<int, 0> s{arr};
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), &arr[0]);
+ }
+
+ {
+ Span<int> s{arr2d};
+ ASSERT_EQ(s.Length(), 6U);
+ ASSERT_EQ(s.data(), &arr2d[0][0]);
+ ASSERT_EQ(s[0], 1);
+ ASSERT_EQ(s[5], 6);
+ }
+
+ {
+ Span<int, 0> s{arr2d};
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), &arr2d[0][0]);
+ }
+
+ { Span<int, 6> s{arr2d}; }
+#endif
+ {
+ Span<int[3]> s{&(arr2d[0]), 1};
+ ASSERT_EQ(s.Length(), 1U);
+ ASSERT_EQ(s.data(), &arr2d[0]);
+ }
+
+ int arr3d[2][3][2] = {{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}};
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ Span<int> s{arr3d};
+ ASSERT_EQ(s.Length(), 12U);
+ ASSERT_EQ(s.data(), &arr3d[0][0][0]);
+ ASSERT_EQ(s[0], 1);
+ ASSERT_EQ(s[11], 12);
+ }
+
+ {
+ Span<int, 0> s{arr3d};
+ ASSERT_EQ(s.Length(), 0U);
+ ASSERT_EQ(s.data(), &arr3d[0][0][0]);
+ }
+
+ { Span<int, 11> s{arr3d}; }
+
+ {
+ Span<int, 12> s{arr3d};
+ ASSERT_EQ(s.Length(), 12U);
+ ASSERT_EQ(s.data(), &arr3d[0][0][0]);
+ ASSERT_EQ(s[0], 1);
+ ASSERT_EQ(s[5], 6);
+ }
+#endif
+ {
+ Span<int[3][2]> s{&arr3d[0], 1};
+ ASSERT_EQ(s.Length(), 1U);
+ ASSERT_EQ(s.data(), &arr3d[0]);
+ }
+
+ {
+ auto s = Span(arr);
+ ASSERT_EQ(s.Length(), 5U);
+ ASSERT_EQ(s.data(), &arr[0]);
+ }
+
+ {
+ auto s = Span(&(arr2d[0]), 1);
+ ASSERT_EQ(s.Length(), 1U);
+ ASSERT_EQ(s.data(), &arr2d[0]);
+ }
+
+ {
+ auto s = Span(&arr3d[0], 1);
+ ASSERT_EQ(s.Length(), 1U);
+ ASSERT_EQ(s.data(), &arr3d[0]);
+ }
+}
+
+SPAN_TEST(from_dynamic_array_constructor) {
+ double(*arr)[3][4] = new double[100][3][4];
+
+ {
+ Span<double> s(&arr[0][0][0], 10);
+ ASSERT_EQ(s.Length(), 10U);
+ ASSERT_EQ(s.data(), &arr[0][0][0]);
+ }
+
+ {
+ auto s = Span(&arr[0][0][0], 10);
+ ASSERT_EQ(s.Length(), 10U);
+ ASSERT_EQ(s.data(), &arr[0][0][0]);
+ }
+
+ delete[] arr;
+}
+
+SPAN_TEST(from_std_array_constructor) {
+ std::array<int, 4> arr = {{1, 2, 3, 4}};
+
+ {
+ Span<int> s{arr};
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+ ASSERT_EQ(s.data(), arr.data());
+
+ Span<const int> cs{arr};
+ ASSERT_EQ(cs.size(), narrow_cast<size_t>(arr.size()));
+ ASSERT_EQ(cs.data(), arr.data());
+ }
+
+ {
+ Span<int, 4> s{arr};
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+ ASSERT_EQ(s.data(), arr.data());
+
+ Span<const int, 4> cs{arr};
+ ASSERT_EQ(cs.size(), narrow_cast<size_t>(arr.size()));
+ ASSERT_EQ(cs.data(), arr.data());
+ }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ Span<int, 2> s{arr};
+ ASSERT_EQ(s.size(), 2U);
+ ASSERT_EQ(s.data(), arr.data());
+
+ Span<const int, 2> cs{arr};
+ ASSERT_EQ(cs.size(), 2U);
+ ASSERT_EQ(cs.data(), arr.data());
+ }
+
+ {
+ Span<int, 0> s{arr};
+ ASSERT_EQ(s.size(), 0U);
+ ASSERT_EQ(s.data(), arr.data());
+
+ Span<const int, 0> cs{arr};
+ ASSERT_EQ(cs.size(), 0U);
+ ASSERT_EQ(cs.data(), arr.data());
+ }
+
+ { Span<int, 5> s{arr}; }
+
+ {
+ auto get_an_array = []() -> std::array<int, 4> { return {1, 2, 3, 4}; };
+ auto take_a_Span = [](Span<int> s) { static_cast<void>(s); };
+ // try to take a temporary std::array
+ take_a_Span(get_an_array());
+ }
+#endif
+
+ {
+ auto get_an_array = []() -> std::array<int, 4> { return {{1, 2, 3, 4}}; };
+ auto take_a_Span = [](Span<const int> s) { static_cast<void>(s); };
+ // try to take a temporary std::array
+ take_a_Span(get_an_array());
+ }
+
+ {
+ auto s = Span(arr);
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+ ASSERT_EQ(s.data(), arr.data());
+ }
+}
+
+SPAN_TEST(from_const_std_array_constructor) {
+ const std::array<int, 4> arr = {{1, 2, 3, 4}};
+
+ {
+ Span<const int> s{arr};
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+ ASSERT_EQ(s.data(), arr.data());
+ }
+
+ {
+ Span<const int, 4> s{arr};
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+ ASSERT_EQ(s.data(), arr.data());
+ }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ Span<const int, 2> s{arr};
+ ASSERT_EQ(s.size(), 2U);
+ ASSERT_EQ(s.data(), arr.data());
+ }
+
+ {
+ Span<const int, 0> s{arr};
+ ASSERT_EQ(s.size(), 0U);
+ ASSERT_EQ(s.data(), arr.data());
+ }
+
+ { Span<const int, 5> s{arr}; }
+#endif
+
+ {
+ auto get_an_array = []() -> const std::array<int, 4> {
+ return {{1, 2, 3, 4}};
+ };
+ auto take_a_Span = [](Span<const int> s) { static_cast<void>(s); };
+ // try to take a temporary std::array
+ take_a_Span(get_an_array());
+ }
+
+ {
+ auto s = Span(arr);
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+ ASSERT_EQ(s.data(), arr.data());
+ }
+}
+
+SPAN_TEST(from_std_array_const_constructor) {
+ std::array<const int, 4> arr = {{1, 2, 3, 4}};
+
+ {
+ Span<const int> s{arr};
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+ ASSERT_EQ(s.data(), arr.data());
+ }
+
+ {
+ Span<const int, 4> s{arr};
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+ ASSERT_EQ(s.data(), arr.data());
+ }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ Span<const int, 2> s{arr};
+ ASSERT_EQ(s.size(), 2U);
+ ASSERT_EQ(s.data(), arr.data());
+ }
+
+ {
+ Span<const int, 0> s{arr};
+ ASSERT_EQ(s.size(), 0U);
+ ASSERT_EQ(s.data(), arr.data());
+ }
+
+ { Span<const int, 5> s{arr}; }
+
+ { Span<int, 4> s{arr}; }
+#endif
+
+ {
+ auto s = Span(arr);
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+ ASSERT_EQ(s.data(), arr.data());
+ }
+}
+
+SPAN_TEST(from_mozilla_array_constructor) {
+ mozilla::Array<int, 4> arr(1, 2, 3, 4);
+
+ {
+ Span<int> s{arr};
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+ ASSERT_EQ(s.data(), &arr[0]);
+
+ Span<const int> cs{arr};
+ ASSERT_EQ(cs.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+ ASSERT_EQ(cs.data(), &arr[0]);
+ }
+
+ {
+ Span<int, 4> s{arr};
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+ ASSERT_EQ(s.data(), &arr[0]);
+
+ Span<const int, 4> cs{arr};
+ ASSERT_EQ(cs.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+ ASSERT_EQ(cs.data(), &arr[0]);
+ }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ Span<int, 2> s{arr};
+ ASSERT_EQ(s.size(), 2U);
+ ASSERT_EQ(s.data(), &arr[0]);
+
+ Span<const int, 2> cs{arr};
+ ASSERT_EQ(cs.size(), 2U);
+ ASSERT_EQ(cs.data(), &arr[0]);
+ }
+
+ {
+ Span<int, 0> s{arr};
+ ASSERT_EQ(s.size(), 0U);
+ ASSERT_EQ(s.data(), &arr[0]);
+
+ Span<const int, 0> cs{arr};
+ ASSERT_EQ(cs.size(), 0U);
+ ASSERT_EQ(cs.data(), &arr[0]);
+ }
+
+ { Span<int, 5> s{arr}; }
+
+ {
+ auto get_an_array = []() -> mozilla::Array<int, 4> { return {1, 2, 3, 4}; };
+ auto take_a_Span = [](Span<int> s) { static_cast<void>(s); };
+ // try to take a temporary mozilla::Array
+ take_a_Span(get_an_array());
+ }
+#endif
+
+ {
+ auto get_an_array = []() -> mozilla::Array<int, 4> { return {1, 2, 3, 4}; };
+ auto take_a_Span = [](Span<const int> s) { static_cast<void>(s); };
+ // try to take a temporary mozilla::Array
+ take_a_Span(get_an_array());
+ }
+
+ {
+ auto s = Span(arr);
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+ ASSERT_EQ(s.data(), &arr[0]);
+ }
+}
+
+SPAN_TEST(from_const_mozilla_array_constructor) {
+ const mozilla::Array<int, 4> arr(1, 2, 3, 4);
+
+ {
+ Span<const int> s{arr};
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+ ASSERT_EQ(s.data(), &arr[0]);
+ }
+
+ {
+ Span<const int, 4> s{arr};
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+ ASSERT_EQ(s.data(), &arr[0]);
+ }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ Span<const int, 2> s{arr};
+ ASSERT_EQ(s.size(), 2U);
+ ASSERT_EQ(s.data(), &arr[0]);
+ }
+
+ {
+ Span<const int, 0> s{arr};
+ ASSERT_EQ(s.size(), 0U);
+ ASSERT_EQ(s.data(), &arr[0]);
+ }
+
+ { Span<const int, 5> s{arr}; }
+#endif
+
+#if 0
+ {
+ auto get_an_array = []() -> const mozilla::Array<int, 4> {
+ return { 1, 2, 3, 4 };
+ };
+ auto take_a_Span = [](Span<const int> s) { static_cast<void>(s); };
+ // try to take a temporary mozilla::Array
+ take_a_Span(get_an_array());
+ }
+#endif
+
+ {
+ auto s = Span(arr);
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+ ASSERT_EQ(s.data(), &arr[0]);
+ }
+}
+
+SPAN_TEST(from_mozilla_array_const_constructor) {
+ mozilla::Array<const int, 4> arr(1, 2, 3, 4);
+
+ {
+ Span<const int> s{arr};
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+ ASSERT_EQ(s.data(), &arr[0]);
+ }
+
+ {
+ Span<const int, 4> s{arr};
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+ ASSERT_EQ(s.data(), &arr[0]);
+ }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ Span<const int, 2> s{arr};
+ ASSERT_EQ(s.size(), 2U);
+ ASSERT_EQ(s.data(), &arr[0]);
+ }
+
+ {
+ Span<const int, 0> s{arr};
+ ASSERT_EQ(s.size(), 0U);
+ ASSERT_EQ(s.data(), &arr[0]);
+ }
+
+ { Span<const int, 5> s{arr}; }
+
+ { Span<int, 4> s{arr}; }
+#endif
+
+ {
+ auto s = Span(arr);
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+ ASSERT_EQ(s.data(), &arr[0]);
+ }
+}
+
+SPAN_TEST(from_container_constructor) {
+ std::vector<int> v = {1, 2, 3};
+ const std::vector<int> cv = v;
+
+ {
+ AssertSpanOfThreeInts(v);
+
+ Span<int> s{v};
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(v.size()));
+ ASSERT_EQ(s.data(), v.data());
+
+ Span<const int> cs{v};
+ ASSERT_EQ(cs.size(), narrow_cast<size_t>(v.size()));
+ ASSERT_EQ(cs.data(), v.data());
+ }
+
+ std::string str = "hello";
+ const std::string cstr = "hello";
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ Span<char> s{str};
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(str.size()));
+ ASSERT_EQ(s.data(), str.data());
+#endif
+ Span<const char> cs{str};
+ ASSERT_EQ(cs.size(), narrow_cast<size_t>(str.size()));
+ ASSERT_EQ(cs.data(), str.data());
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ Span<char> s{cstr};
+#endif
+ Span<const char> cs{cstr};
+ ASSERT_EQ(cs.size(), narrow_cast<size_t>(cstr.size()));
+ ASSERT_EQ(cs.data(), cstr.data());
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ auto get_temp_vector = []() -> std::vector<int> { return {}; };
+ auto use_Span = [](Span<int> s) { static_cast<void>(s); };
+ use_Span(get_temp_vector());
+#endif
+ }
+
+ {
+ auto get_temp_vector = []() -> std::vector<int> { return {}; };
+ auto use_Span = [](Span<const int> s) { static_cast<void>(s); };
+ use_Span(get_temp_vector());
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ auto get_temp_string = []() -> std::string { return {}; };
+ auto use_Span = [](Span<char> s) { static_cast<void>(s); };
+ use_Span(get_temp_string());
+#endif
+ }
+
+ {
+ auto get_temp_string = []() -> std::string { return {}; };
+ auto use_Span = [](Span<const char> s) { static_cast<void>(s); };
+ use_Span(get_temp_string());
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ auto get_temp_vector = []() -> const std::vector<int> { return {}; };
+ auto use_Span = [](Span<const char> s) { static_cast<void>(s); };
+ use_Span(get_temp_vector());
+#endif
+ }
+
+ {
+ auto get_temp_string = []() -> const std::string { return {}; };
+ auto use_Span = [](Span<const char> s) { static_cast<void>(s); };
+ use_Span(get_temp_string());
+ }
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ std::map<int, int> m;
+ Span<int> s{m};
+#endif
+ }
+
+ {
+ auto s = Span(v);
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(v.size()));
+ ASSERT_EQ(s.data(), v.data());
+
+ auto cs = Span(cv);
+ ASSERT_EQ(cs.size(), narrow_cast<size_t>(cv.size()));
+ ASSERT_EQ(cs.data(), cv.data());
+ }
+}
+
+SPAN_TEST(from_xpcom_collections) {
+ {
+ nsTArray<int> v;
+ v.AppendElement(1);
+ v.AppendElement(2);
+ v.AppendElement(3);
+
+ AssertSpanOfThreeInts(v);
+
+ Span<int> s{v};
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+ ASSERT_EQ(s.data(), v.Elements());
+ ASSERT_EQ(s[2], 3);
+
+ Span<const int> cs{v};
+ ASSERT_EQ(cs.size(), narrow_cast<size_t>(v.Length()));
+ ASSERT_EQ(cs.data(), v.Elements());
+ ASSERT_EQ(cs[2], 3);
+ }
+ {
+ nsTArray<int> v;
+ v.AppendElement(1);
+ v.AppendElement(2);
+ v.AppendElement(3);
+
+ AssertSpanOfThreeInts(v);
+
+ auto s = Span(v);
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+ ASSERT_EQ(s.data(), v.Elements());
+ ASSERT_EQ(s[2], 3);
+ }
+ {
+ AutoTArray<int, 5> v;
+ v.AppendElement(1);
+ v.AppendElement(2);
+ v.AppendElement(3);
+
+ AssertSpanOfThreeInts(v);
+
+ Span<int> s{v};
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+ ASSERT_EQ(s.data(), v.Elements());
+ ASSERT_EQ(s[2], 3);
+
+ Span<const int> cs{v};
+ ASSERT_EQ(cs.size(), narrow_cast<size_t>(v.Length()));
+ ASSERT_EQ(cs.data(), v.Elements());
+ ASSERT_EQ(cs[2], 3);
+ }
+ {
+ AutoTArray<int, 5> v;
+ v.AppendElement(1);
+ v.AppendElement(2);
+ v.AppendElement(3);
+
+ AssertSpanOfThreeInts(v);
+
+ auto s = Span(v);
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+ ASSERT_EQ(s.data(), v.Elements());
+ ASSERT_EQ(s[2], 3);
+ }
+ {
+ FallibleTArray<int> v;
+ *(v.AppendElement(fallible)) = 1;
+ *(v.AppendElement(fallible)) = 2;
+ *(v.AppendElement(fallible)) = 3;
+
+ AssertSpanOfThreeInts(v);
+
+ Span<int> s{v};
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+ ASSERT_EQ(s.data(), v.Elements());
+ ASSERT_EQ(s[2], 3);
+
+ Span<const int> cs{v};
+ ASSERT_EQ(cs.size(), narrow_cast<size_t>(v.Length()));
+ ASSERT_EQ(cs.data(), v.Elements());
+ ASSERT_EQ(cs[2], 3);
+ }
+ {
+ FallibleTArray<int> v;
+ *(v.AppendElement(fallible)) = 1;
+ *(v.AppendElement(fallible)) = 2;
+ *(v.AppendElement(fallible)) = 3;
+
+ AssertSpanOfThreeInts(v);
+
+ auto s = Span(v);
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+ ASSERT_EQ(s.data(), v.Elements());
+ ASSERT_EQ(s[2], 3);
+ }
+ {
+ nsAutoString str;
+ str.AssignLiteral(u"abc");
+
+ AssertSpanOfThreeChar16s(str);
+ AssertSpanOfThreeChar16sViaString(str);
+
+ Span<char16_t> s{str.GetMutableData()};
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(str.Length()));
+ ASSERT_EQ(s.data(), str.BeginWriting());
+ ASSERT_EQ(s[2], 'c');
+
+ Span<const char16_t> cs{str};
+ ASSERT_EQ(cs.size(), narrow_cast<size_t>(str.Length()));
+ ASSERT_EQ(cs.data(), str.BeginReading());
+ ASSERT_EQ(cs[2], 'c');
+ }
+ {
+ nsAutoString str;
+ str.AssignLiteral(u"abc");
+
+ AssertSpanOfThreeChar16s(str);
+ AssertSpanOfThreeChar16sViaString(str);
+
+ auto s = Span(str);
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(str.Length()));
+ ASSERT_EQ(s.data(), str.BeginReading());
+ ASSERT_EQ(s[2], 'c');
+ }
+ {
+ nsAutoCString str;
+ str.AssignLiteral("abc");
+
+ AssertSpanOfThreeChars(str);
+ AssertSpanOfThreeCharsViaString(str);
+
+ Span<const uint8_t> cs{str};
+ ASSERT_EQ(cs.size(), narrow_cast<size_t>(str.Length()));
+ ASSERT_EQ(cs.data(), reinterpret_cast<const uint8_t*>(str.BeginReading()));
+ ASSERT_EQ(cs[2], 'c');
+ }
+ {
+ nsAutoCString str;
+ str.AssignLiteral("abc");
+
+ AssertSpanOfThreeChars(str);
+ AssertSpanOfThreeCharsViaString(str);
+
+ auto s = Span(str);
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(str.Length()));
+ ASSERT_EQ(s.data(), str.BeginReading());
+ ASSERT_EQ(s[2], 'c');
+ }
+ {
+ nsTArray<int> v;
+ v.AppendElement(1);
+ v.AppendElement(2);
+ v.AppendElement(3);
+
+ Range<int> r(v.Elements(), v.Length());
+
+ AssertSpanOfThreeInts(r);
+
+ Span<int> s{r};
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+ ASSERT_EQ(s.data(), v.Elements());
+ ASSERT_EQ(s[2], 3);
+
+ Span<const int> cs{r};
+ ASSERT_EQ(cs.size(), narrow_cast<size_t>(v.Length()));
+ ASSERT_EQ(cs.data(), v.Elements());
+ ASSERT_EQ(cs[2], 3);
+ }
+ {
+ nsTArray<int> v;
+ v.AppendElement(1);
+ v.AppendElement(2);
+ v.AppendElement(3);
+
+ Range<int> r(v.Elements(), v.Length());
+
+ AssertSpanOfThreeInts(r);
+
+ auto s = Span(r);
+ ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+ ASSERT_EQ(s.data(), v.Elements());
+ ASSERT_EQ(s[2], 3);
+ }
+}
+
+SPAN_TEST(from_cstring) {
+ {
+ const char* str = nullptr;
+ auto cs = MakeStringSpan(str);
+ ASSERT_EQ(cs.size(), 0U);
+ }
+ {
+ const char* str = "abc";
+
+ auto cs = MakeStringSpan(str);
+ ASSERT_EQ(cs.size(), 3U);
+ ASSERT_EQ(cs.data(), str);
+ ASSERT_EQ(cs[2], 'c');
+
+ static_assert(MakeStringSpan("abc").size() == 3U);
+ static_assert(MakeStringSpan("abc")[2] == 'c');
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ Span<const char> scccl("literal"); // error
+
+ Span<const char> sccel;
+ sccel = "literal"; // error
+
+ cs = Span("literal"); // error
+#endif
+ }
+ {
+ char arr[4] = {'a', 'b', 'c', 0};
+
+ auto cs = MakeStringSpan(arr);
+ ASSERT_EQ(cs.size(), 3U);
+ ASSERT_EQ(cs.data(), arr);
+ ASSERT_EQ(cs[2], 'c');
+
+ cs = Span(arr);
+ ASSERT_EQ(cs.size(), 4U); // zero terminator is part of the array span.
+ ASSERT_EQ(cs.data(), arr);
+ ASSERT_EQ(cs[2], 'c');
+ ASSERT_EQ(cs[3], '\0'); // zero terminator is part of the array span.
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ Span<char> scca(arr); // error
+ Span<const char> sccca(arr); // error
+
+ Span<const char> scccea;
+ scccea = arr; // error
+#endif
+ }
+ {
+ const char16_t* str = nullptr;
+ auto cs = MakeStringSpan(str);
+ ASSERT_EQ(cs.size(), 0U);
+ }
+ {
+ char16_t arr[4] = {'a', 'b', 'c', 0};
+ const char16_t* str = arr;
+
+ auto cs = MakeStringSpan(str);
+ ASSERT_EQ(cs.size(), 3U);
+ ASSERT_EQ(cs.data(), str);
+ ASSERT_EQ(cs[2], 'c');
+
+ static_assert(MakeStringSpan(u"abc").size() == 3U);
+ static_assert(MakeStringSpan(u"abc")[2] == u'c');
+
+ cs = MakeStringSpan(arr);
+ ASSERT_EQ(cs.size(), 3U);
+ ASSERT_EQ(cs.data(), str);
+ ASSERT_EQ(cs[2], 'c');
+
+ cs = Span(arr);
+ ASSERT_EQ(cs.size(), 4U); // zero terminator is part of the array span.
+ ASSERT_EQ(cs.data(), str);
+ ASSERT_EQ(cs[2], 'c');
+ ASSERT_EQ(cs[3], '\0'); // zero terminator is part of the array span.
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ Span<char16_t> scca(arr); // error
+
+ Span<const char16_t> scccea;
+ scccea = arr; // error
+
+ Span<const char16_t> scccl(u"literal"); // error
+
+ Span<const char16_t>* sccel;
+ *sccel = u"literal"; // error
+
+ cs = Span(u"literal"); // error
+#endif
+ }
+}
+
+SPAN_TEST(from_convertible_Span_constructor){{Span<DerivedClass> avd;
+Span<const DerivedClass> avcd = avd;
+static_cast<void>(avcd);
+}
+
+{
+#ifdef CONFIRM_COMPILATION_ERRORS
+ Span<DerivedClass> avd;
+ Span<BaseClass> avb = avd;
+ static_cast<void>(avb);
+#endif
+}
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+{
+ Span<int> s;
+ Span<unsigned int> s2 = s;
+ static_cast<void>(s2);
+}
+
+{
+ Span<int> s;
+ Span<const unsigned int> s2 = s;
+ static_cast<void>(s2);
+}
+
+{
+ Span<int> s;
+ Span<short> s2 = s;
+ static_cast<void>(s2);
+}
+#endif
+}
+
+SPAN_TEST(copy_move_and_assignment) {
+ Span<int> s1;
+ ASSERT_TRUE(s1.empty());
+
+ int arr[] = {3, 4, 5};
+
+ Span<const int> s2 = arr;
+ ASSERT_EQ(s2.Length(), 3U);
+ ASSERT_EQ(s2.data(), &arr[0]);
+
+ s2 = s1;
+ ASSERT_TRUE(s2.empty());
+
+ auto get_temp_Span = [&]() -> Span<int> { return {&arr[1], 2}; };
+ auto use_Span = [&](Span<const int> s) {
+ ASSERT_EQ(s.Length(), 2U);
+ ASSERT_EQ(s.data(), &arr[1]);
+ };
+ use_Span(get_temp_Span());
+
+ s1 = get_temp_Span();
+ ASSERT_EQ(s1.Length(), 2U);
+ ASSERT_EQ(s1.data(), &arr[1]);
+}
+
+SPAN_TEST(first) {
+ int arr[5] = {1, 2, 3, 4, 5};
+
+ {
+ Span<int, 5> av = arr;
+ ASSERT_EQ(av.First<2>().Length(), 2U);
+ ASSERT_EQ(av.First(2).Length(), 2U);
+ }
+
+ {
+ Span<int, 5> av = arr;
+ ASSERT_EQ(av.First<0>().Length(), 0U);
+ ASSERT_EQ(av.First(0).Length(), 0U);
+ }
+
+ {
+ Span<int, 5> av = arr;
+ ASSERT_EQ(av.First<5>().Length(), 5U);
+ ASSERT_EQ(av.First(5).Length(), 5U);
+ }
+
+#if 0
+ {
+ Span<int, 5> av = arr;
+# ifdef CONFIRM_COMPILATION_ERRORS
+ ASSERT_EQ(av.First<6>().Length() , 6U);
+ ASSERT_EQ(av.First<-1>().Length() , -1);
+# endif
+ CHECK_THROW(av.First(6).Length(), fail_fast);
+ }
+#endif
+
+ {
+ Span<int> av;
+ ASSERT_EQ(av.First<0>().Length(), 0U);
+ ASSERT_EQ(av.First(0).Length(), 0U);
+ }
+}
+
+SPAN_TEST(last) {
+ int arr[5] = {1, 2, 3, 4, 5};
+
+ {
+ Span<int, 5> av = arr;
+ ASSERT_EQ(av.Last<2>().Length(), 2U);
+ ASSERT_EQ(av.Last(2).Length(), 2U);
+ }
+
+ {
+ Span<int, 5> av = arr;
+ ASSERT_EQ(av.Last<0>().Length(), 0U);
+ ASSERT_EQ(av.Last(0).Length(), 0U);
+ }
+
+ {
+ Span<int, 5> av = arr;
+ ASSERT_EQ(av.Last<5>().Length(), 5U);
+ ASSERT_EQ(av.Last(5).Length(), 5U);
+ }
+
+#if 0
+ {
+ Span<int, 5> av = arr;
+# ifdef CONFIRM_COMPILATION_ERRORS
+ ASSERT_EQ(av.Last<6>().Length() , 6U);
+# endif
+ CHECK_THROW(av.Last(6).Length(), fail_fast);
+ }
+#endif
+
+ {
+ Span<int> av;
+ ASSERT_EQ(av.Last<0>().Length(), 0U);
+ ASSERT_EQ(av.Last(0).Length(), 0U);
+ }
+}
+
+SPAN_TEST(from_to) {
+ int arr[5] = {1, 2, 3, 4, 5};
+
+ {
+ Span<int, 5> av = arr;
+ ASSERT_EQ(av.From(3).Length(), 2U);
+ ASSERT_EQ(av.From(2)[1], 4);
+ }
+
+ {
+ Span<int, 5> av = arr;
+ ASSERT_EQ(av.From(5).Length(), 0U);
+ }
+
+ {
+ Span<int, 5> av = arr;
+ ASSERT_EQ(av.From(0).Length(), 5U);
+ }
+
+ {
+ Span<int, 5> av = arr;
+ ASSERT_EQ(av.To(3).Length(), 3U);
+ ASSERT_EQ(av.To(3)[1], 2);
+ }
+
+ {
+ Span<int, 5> av = arr;
+ ASSERT_EQ(av.To(0).Length(), 0U);
+ }
+
+ {
+ Span<int, 5> av = arr;
+ ASSERT_EQ(av.To(5).Length(), 5U);
+ }
+
+ {
+ Span<int, 5> av = arr;
+ ASSERT_EQ(av.FromTo(1, 4).Length(), 3U);
+ ASSERT_EQ(av.FromTo(1, 4)[1], 3);
+ }
+
+ {
+ Span<int, 5> av = arr;
+ ASSERT_EQ(av.FromTo(2, 2).Length(), 0U);
+ }
+
+ {
+ Span<int, 5> av = arr;
+ ASSERT_EQ(av.FromTo(0, 5).Length(), 5U);
+ }
+}
+
+SPAN_TEST(Subspan) {
+ int arr[5] = {1, 2, 3, 4, 5};
+
+ {
+ Span<int, 5> av = arr;
+ ASSERT_EQ((av.Subspan<2, 2>().Length()), 2U);
+ ASSERT_EQ(av.Subspan(2, 2).Length(), 2U);
+ ASSERT_EQ(av.Subspan(2, 3).Length(), 3U);
+ }
+
+ {
+ Span<int, 5> av = arr;
+ ASSERT_EQ((av.Subspan<0, 0>().Length()), 0U);
+ ASSERT_EQ(av.Subspan(0, 0).Length(), 0U);
+ }
+
+ {
+ Span<int, 5> av = arr;
+ ASSERT_EQ((av.Subspan<0, 5>().Length()), 5U);
+ ASSERT_EQ(av.Subspan(0, 5).Length(), 5U);
+ CHECK_THROW(av.Subspan(0, 6).Length(), fail_fast);
+ CHECK_THROW(av.Subspan(1, 5).Length(), fail_fast);
+ }
+
+ {
+ Span<int, 5> av = arr;
+ ASSERT_EQ((av.Subspan<4, 0>().Length()), 0U);
+ ASSERT_EQ(av.Subspan(4, 0).Length(), 0U);
+ ASSERT_EQ(av.Subspan(5, 0).Length(), 0U);
+ CHECK_THROW(av.Subspan(6, 0).Length(), fail_fast);
+ }
+
+ {
+ Span<int> av;
+ ASSERT_EQ((av.Subspan<0, 0>().Length()), 0U);
+ ASSERT_EQ(av.Subspan(0, 0).Length(), 0U);
+ CHECK_THROW((av.Subspan<1, 0>().Length()), fail_fast);
+ }
+
+ {
+ Span<int> av;
+ ASSERT_EQ(av.Subspan(0).Length(), 0U);
+ CHECK_THROW(av.Subspan(1).Length(), fail_fast);
+ }
+
+ {
+ Span<int> av = arr;
+ ASSERT_EQ(av.Subspan(0).Length(), 5U);
+ ASSERT_EQ(av.Subspan(1).Length(), 4U);
+ ASSERT_EQ(av.Subspan(4).Length(), 1U);
+ ASSERT_EQ(av.Subspan(5).Length(), 0U);
+ CHECK_THROW(av.Subspan(6).Length(), fail_fast);
+ auto av2 = av.Subspan(1);
+ for (int i = 0; i < 4; ++i) ASSERT_EQ(av2[i], i + 2);
+ }
+
+ {
+ Span<int, 5> av = arr;
+ ASSERT_EQ(av.Subspan(0).Length(), 5U);
+ ASSERT_EQ(av.Subspan(1).Length(), 4U);
+ ASSERT_EQ(av.Subspan(4).Length(), 1U);
+ ASSERT_EQ(av.Subspan(5).Length(), 0U);
+ CHECK_THROW(av.Subspan(6).Length(), fail_fast);
+ auto av2 = av.Subspan(1);
+ for (int i = 0; i < 4; ++i) ASSERT_EQ(av2[i], i + 2);
+ }
+}
+
+SPAN_TEST(at_call) {
+ int arr[4] = {1, 2, 3, 4};
+
+ {
+ Span<int> s = arr;
+ ASSERT_EQ(s.at(0), 1);
+ CHECK_THROW(s.at(5), fail_fast);
+ }
+
+ {
+ int arr2d[2] = {1, 6};
+ Span<int, 2> s = arr2d;
+ ASSERT_EQ(s.at(0), 1);
+ ASSERT_EQ(s.at(1), 6);
+ CHECK_THROW(s.at(2), fail_fast);
+ }
+}
+
+SPAN_TEST(operator_function_call) {
+ int arr[4] = {1, 2, 3, 4};
+
+ {
+ Span<int> s = arr;
+ ASSERT_EQ(s(0), 1);
+ CHECK_THROW(s(5), fail_fast);
+ }
+
+ {
+ int arr2d[2] = {1, 6};
+ Span<int, 2> s = arr2d;
+ ASSERT_EQ(s(0), 1);
+ ASSERT_EQ(s(1), 6);
+ CHECK_THROW(s(2), fail_fast);
+ }
+}
+
+SPAN_TEST(iterator_default_init) {
+ Span<int>::iterator it1;
+ Span<int>::iterator it2;
+ ASSERT_EQ(it1, it2);
+}
+
+SPAN_TEST(const_iterator_default_init) {
+ Span<int>::const_iterator it1;
+ Span<int>::const_iterator it2;
+ ASSERT_EQ(it1, it2);
+}
+
+SPAN_TEST(iterator_conversions) {
+ Span<int>::iterator badIt;
+ Span<int>::const_iterator badConstIt;
+ ASSERT_EQ(badIt, badConstIt);
+
+ int a[] = {1, 2, 3, 4};
+ Span<int> s = a;
+
+ auto it = s.begin();
+ auto cit = s.cbegin();
+
+ ASSERT_EQ(it, cit);
+ ASSERT_EQ(cit, it);
+
+ Span<int>::const_iterator cit2 = it;
+ ASSERT_EQ(cit2, cit);
+
+ Span<int>::const_iterator cit3 = it + 4;
+ ASSERT_EQ(cit3, s.cend());
+}
+
+SPAN_TEST(iterator_comparisons) {
+ int a[] = {1, 2, 3, 4};
+ {
+ Span<int> s = a;
+ Span<int>::iterator it = s.begin();
+ auto it2 = it + 1;
+ Span<int>::const_iterator cit = s.cbegin();
+
+ ASSERT_EQ(it, cit);
+ ASSERT_EQ(cit, it);
+ ASSERT_EQ(it, it);
+ ASSERT_EQ(cit, cit);
+ ASSERT_EQ(cit, s.begin());
+ ASSERT_EQ(s.begin(), cit);
+ ASSERT_EQ(s.cbegin(), cit);
+ ASSERT_EQ(it, s.begin());
+ ASSERT_EQ(s.begin(), it);
+
+ ASSERT_NE(it, it2);
+ ASSERT_NE(it2, it);
+ ASSERT_NE(it, s.end());
+ ASSERT_NE(it2, s.end());
+ ASSERT_NE(s.end(), it);
+ ASSERT_NE(it2, cit);
+ ASSERT_NE(cit, it2);
+
+ ASSERT_LT(it, it2);
+ ASSERT_LE(it, it2);
+ ASSERT_LE(it2, s.end());
+ ASSERT_LT(it, s.end());
+ ASSERT_LE(it, cit);
+ ASSERT_LE(cit, it);
+ ASSERT_LT(cit, it2);
+ ASSERT_LE(cit, it2);
+ ASSERT_LT(cit, s.end());
+ ASSERT_LE(cit, s.end());
+
+ ASSERT_GT(it2, it);
+ ASSERT_GE(it2, it);
+ ASSERT_GT(s.end(), it2);
+ ASSERT_GE(s.end(), it2);
+ ASSERT_GT(it2, cit);
+ ASSERT_GE(it2, cit);
+ }
+}
+
+SPAN_TEST(begin_end) {
+ {
+ int a[] = {1, 2, 3, 4};
+ Span<int> s = a;
+
+ Span<int>::iterator it = s.begin();
+ Span<int>::iterator it2 = std::begin(s);
+ ASSERT_EQ(it, it2);
+
+ it = s.end();
+ it2 = std::end(s);
+ ASSERT_EQ(it, it2);
+ }
+
+ {
+ int a[] = {1, 2, 3, 4};
+ Span<int> s = a;
+
+ auto it = s.begin();
+ auto first = it;
+ ASSERT_EQ(it, first);
+ ASSERT_EQ(*it, 1);
+
+ auto beyond = s.end();
+ ASSERT_NE(it, beyond);
+ CHECK_THROW(*beyond, fail_fast);
+
+ ASSERT_EQ(beyond - first, 4);
+ ASSERT_EQ(first - first, 0);
+ ASSERT_EQ(beyond - beyond, 0);
+
+ ++it;
+ ASSERT_EQ(it - first, 1);
+ ASSERT_EQ(*it, 2);
+ *it = 22;
+ ASSERT_EQ(*it, 22);
+ ASSERT_EQ(beyond - it, 3);
+
+ it = first;
+ ASSERT_EQ(it, first);
+ while (it != s.end()) {
+ *it = 5;
+ ++it;
+ }
+
+ ASSERT_EQ(it, beyond);
+ ASSERT_EQ(it - beyond, 0);
+
+ for (auto& n : s) {
+ ASSERT_EQ(n, 5);
+ }
+ }
+}
+
+SPAN_TEST(cbegin_cend) {
+#if 0
+ {
+ int a[] = { 1, 2, 3, 4 };
+ Span<int> s = a;
+
+ Span<int>::const_iterator cit = s.cbegin();
+ Span<int>::const_iterator cit2 = std::cbegin(s);
+ ASSERT_EQ(cit , cit2);
+
+ cit = s.cend();
+ cit2 = std::cend(s);
+ ASSERT_EQ(cit , cit2);
+ }
+#endif
+ {
+ int a[] = {1, 2, 3, 4};
+ Span<int> s = a;
+
+ auto it = s.cbegin();
+ auto first = it;
+ ASSERT_EQ(it, first);
+ ASSERT_EQ(*it, 1);
+
+ auto beyond = s.cend();
+ ASSERT_NE(it, beyond);
+ CHECK_THROW(*beyond, fail_fast);
+
+ ASSERT_EQ(beyond - first, 4);
+ ASSERT_EQ(first - first, 0);
+ ASSERT_EQ(beyond - beyond, 0);
+
+ ++it;
+ ASSERT_EQ(it - first, 1);
+ ASSERT_EQ(*it, 2);
+ ASSERT_EQ(beyond - it, 3);
+
+ int last = 0;
+ it = first;
+ ASSERT_EQ(it, first);
+ while (it != s.cend()) {
+ ASSERT_EQ(*it, last + 1);
+
+ last = *it;
+ ++it;
+ }
+
+ ASSERT_EQ(it, beyond);
+ ASSERT_EQ(it - beyond, 0);
+ }
+}
+
+SPAN_TEST(rbegin_rend) {
+ {
+ int a[] = {1, 2, 3, 4};
+ Span<int> s = a;
+
+ auto it = s.rbegin();
+ auto first = it;
+ ASSERT_EQ(it, first);
+ ASSERT_EQ(*it, 4);
+
+ auto beyond = s.rend();
+ ASSERT_NE(it, beyond);
+ CHECK_THROW(*beyond, fail_fast);
+
+ ASSERT_EQ(beyond - first, 4);
+ ASSERT_EQ(first - first, 0);
+ ASSERT_EQ(beyond - beyond, 0);
+
+ ++it;
+ ASSERT_EQ(it - first, 1);
+ ASSERT_EQ(*it, 3);
+ *it = 22;
+ ASSERT_EQ(*it, 22);
+ ASSERT_EQ(beyond - it, 3);
+
+ it = first;
+ ASSERT_EQ(it, first);
+ while (it != s.rend()) {
+ *it = 5;
+ ++it;
+ }
+
+ ASSERT_EQ(it, beyond);
+ ASSERT_EQ(it - beyond, 0);
+
+ for (auto& n : s) {
+ ASSERT_EQ(n, 5);
+ }
+ }
+}
+
+SPAN_TEST(crbegin_crend) {
+ {
+ int a[] = {1, 2, 3, 4};
+ Span<int> s = a;
+
+ auto it = s.crbegin();
+ auto first = it;
+ ASSERT_EQ(it, first);
+ ASSERT_EQ(*it, 4);
+
+ auto beyond = s.crend();
+ ASSERT_NE(it, beyond);
+ CHECK_THROW(*beyond, fail_fast);
+
+ ASSERT_EQ(beyond - first, 4);
+ ASSERT_EQ(first - first, 0);
+ ASSERT_EQ(beyond - beyond, 0);
+
+ ++it;
+ ASSERT_EQ(it - first, 1);
+ ASSERT_EQ(*it, 3);
+ ASSERT_EQ(beyond - it, 3);
+
+ it = first;
+ ASSERT_EQ(it, first);
+ int last = 5;
+ while (it != s.crend()) {
+ ASSERT_EQ(*it, last - 1);
+ last = *it;
+
+ ++it;
+ }
+
+ ASSERT_EQ(it, beyond);
+ ASSERT_EQ(it - beyond, 0);
+ }
+}
+
+SPAN_TEST(comparison_operators) {
+ {
+ Span<int> s1 = nullptr;
+ Span<int> s2 = nullptr;
+ ASSERT_EQ(s1, s2);
+ ASSERT_FALSE(s1 != s2);
+ ASSERT_FALSE(s1 < s2);
+ ASSERT_LE(s1, s2);
+ ASSERT_FALSE(s1 > s2);
+ ASSERT_GE(s1, s2);
+ ASSERT_EQ(s2, s1);
+ ASSERT_FALSE(s2 != s1);
+ ASSERT_FALSE(s2 < s1);
+ ASSERT_LE(s2, s1);
+ ASSERT_FALSE(s2 > s1);
+ ASSERT_GE(s2, s1);
+ }
+
+ {
+ int arr[] = {2, 1};
+ Span<int> s1 = arr;
+ Span<int> s2 = arr;
+
+ ASSERT_EQ(s1, s2);
+ ASSERT_FALSE(s1 != s2);
+ ASSERT_FALSE(s1 < s2);
+ ASSERT_LE(s1, s2);
+ ASSERT_FALSE(s1 > s2);
+ ASSERT_GE(s1, s2);
+ ASSERT_EQ(s2, s1);
+ ASSERT_FALSE(s2 != s1);
+ ASSERT_FALSE(s2 < s1);
+ ASSERT_LE(s2, s1);
+ ASSERT_FALSE(s2 > s1);
+ ASSERT_GE(s2, s1);
+ }
+
+ {
+ int arr[] = {2, 1}; // bigger
+
+ Span<int> s1 = nullptr;
+ Span<int> s2 = arr;
+
+ ASSERT_NE(s1, s2);
+ ASSERT_NE(s2, s1);
+ ASSERT_NE(s1, s2);
+ ASSERT_NE(s2, s1);
+ ASSERT_LT(s1, s2);
+ ASSERT_FALSE(s2 < s1);
+ ASSERT_LE(s1, s2);
+ ASSERT_FALSE(s2 <= s1);
+ ASSERT_GT(s2, s1);
+ ASSERT_FALSE(s1 > s2);
+ ASSERT_GE(s2, s1);
+ ASSERT_FALSE(s1 >= s2);
+ }
+
+ {
+ int arr1[] = {1, 2};
+ int arr2[] = {1, 2};
+ Span<int> s1 = arr1;
+ Span<int> s2 = arr2;
+
+ ASSERT_EQ(s1, s2);
+ ASSERT_FALSE(s1 != s2);
+ ASSERT_FALSE(s1 < s2);
+ ASSERT_LE(s1, s2);
+ ASSERT_FALSE(s1 > s2);
+ ASSERT_GE(s1, s2);
+ ASSERT_EQ(s2, s1);
+ ASSERT_FALSE(s2 != s1);
+ ASSERT_FALSE(s2 < s1);
+ ASSERT_LE(s2, s1);
+ ASSERT_FALSE(s2 > s1);
+ ASSERT_GE(s2, s1);
+ }
+
+ {
+ int arr[] = {1, 2, 3};
+
+ AssertSpanOfThreeInts(arr);
+
+ Span<int> s1 = {&arr[0], 2}; // shorter
+ Span<int> s2 = arr; // longer
+
+ ASSERT_NE(s1, s2);
+ ASSERT_NE(s2, s1);
+ ASSERT_NE(s1, s2);
+ ASSERT_NE(s2, s1);
+ ASSERT_LT(s1, s2);
+ ASSERT_FALSE(s2 < s1);
+ ASSERT_LE(s1, s2);
+ ASSERT_FALSE(s2 <= s1);
+ ASSERT_GT(s2, s1);
+ ASSERT_FALSE(s1 > s2);
+ ASSERT_GE(s2, s1);
+ ASSERT_FALSE(s1 >= s2);
+ }
+
+ {
+ int arr1[] = {1, 2}; // smaller
+ int arr2[] = {2, 1}; // bigger
+
+ Span<int> s1 = arr1;
+ Span<int> s2 = arr2;
+
+ ASSERT_NE(s1, s2);
+ ASSERT_NE(s2, s1);
+ ASSERT_NE(s1, s2);
+ ASSERT_NE(s2, s1);
+ ASSERT_LT(s1, s2);
+ ASSERT_FALSE(s2 < s1);
+ ASSERT_LE(s1, s2);
+ ASSERT_FALSE(s2 <= s1);
+ ASSERT_GT(s2, s1);
+ ASSERT_FALSE(s1 > s2);
+ ASSERT_GE(s2, s1);
+ ASSERT_FALSE(s1 >= s2);
+ }
+}
+
+SPAN_TEST(as_bytes) {
+ int a[] = {1, 2, 3, 4};
+
+ {
+ Span<const int> s = a;
+ ASSERT_EQ(s.Length(), 4U);
+ Span<const uint8_t> bs = AsBytes(s);
+ ASSERT_EQ(static_cast<const void*>(bs.data()),
+ static_cast<const void*>(s.data()));
+ ASSERT_EQ(bs.Length(), s.LengthBytes());
+ }
+
+ {
+ Span<int> s;
+ auto bs = AsBytes(s);
+ ASSERT_EQ(bs.Length(), s.Length());
+ ASSERT_EQ(bs.Length(), 0U);
+ ASSERT_EQ(bs.size_bytes(), 0U);
+ ASSERT_EQ(static_cast<const void*>(bs.data()),
+ static_cast<const void*>(s.data()));
+ ASSERT_EQ(bs.data(), reinterpret_cast<const uint8_t*>(SLICE_INT_PTR));
+ }
+
+ {
+ Span<int> s = a;
+ auto bs = AsBytes(s);
+ ASSERT_EQ(static_cast<const void*>(bs.data()),
+ static_cast<const void*>(s.data()));
+ ASSERT_EQ(bs.Length(), s.LengthBytes());
+ }
+}
+
+SPAN_TEST(as_writable_bytes) {
+ int a[] = {1, 2, 3, 4};
+
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ // you should not be able to get writeable bytes for const objects
+ Span<const int> s = a;
+ ASSERT_EQ(s.Length(), 4U);
+ Span<const byte> bs = AsWritableBytes(s);
+ ASSERT_EQ(static_cast<void*>(bs.data()), static_cast<void*>(s.data()));
+ ASSERT_EQ(bs.Length(), s.LengthBytes());
+#endif
+ }
+
+ {
+ Span<int> s;
+ auto bs = AsWritableBytes(s);
+ ASSERT_EQ(bs.Length(), s.Length());
+ ASSERT_EQ(bs.Length(), 0U);
+ ASSERT_EQ(bs.size_bytes(), 0U);
+ ASSERT_EQ(static_cast<void*>(bs.data()), static_cast<void*>(s.data()));
+ ASSERT_EQ(bs.data(), reinterpret_cast<uint8_t*>(SLICE_INT_PTR));
+ }
+
+ {
+ Span<int> s = a;
+ auto bs = AsWritableBytes(s);
+ ASSERT_EQ(static_cast<void*>(bs.data()), static_cast<void*>(s.data()));
+ ASSERT_EQ(bs.Length(), s.LengthBytes());
+ }
+}
+
+SPAN_TEST(as_chars) {
+ const uint8_t a[] = {1, 2, 3, 4};
+ Span<const uint8_t> u = Span(a);
+ Span<const char> c = AsChars(u);
+ ASSERT_EQ(static_cast<const void*>(u.data()),
+ static_cast<const void*>(c.data()));
+ ASSERT_EQ(u.size(), c.size());
+}
+
+SPAN_TEST(as_writable_chars) {
+ uint8_t a[] = {1, 2, 3, 4};
+ Span<uint8_t> u = Span(a);
+ Span<char> c = AsWritableChars(u);
+ ASSERT_EQ(static_cast<void*>(u.data()), static_cast<void*>(c.data()));
+ ASSERT_EQ(u.size(), c.size());
+}
+
+SPAN_TEST(fixed_size_conversions) {
+ int arr[] = {1, 2, 3, 4};
+
+ // converting to an Span from an equal size array is ok
+ Span<int, 4> s4 = arr;
+ ASSERT_EQ(s4.Length(), 4U);
+
+ // converting to dynamic_range is always ok
+ {
+ Span<int> s = s4;
+ ASSERT_EQ(s.Length(), s4.Length());
+ static_cast<void>(s);
+ }
+
+// initialization or assignment to static Span that REDUCES size is NOT ok
+#ifdef CONFIRM_COMPILATION_ERRORS
+ { Span<int, 2> s = arr; }
+ {
+ Span<int, 2> s2 = s4;
+ static_cast<void>(s2);
+ }
+#endif
+
+#if 0
+ // even when done dynamically
+ {
+ Span<int> s = arr;
+ auto f = [&]() {
+ Span<int, 2> s2 = s;
+ static_cast<void>(s2);
+ };
+ CHECK_THROW(f(), fail_fast);
+ }
+#endif
+
+ // but doing so explicitly is ok
+
+ // you can convert statically
+ {
+ Span<int, 2> s2 = {arr, 2};
+ static_cast<void>(s2);
+ }
+ {
+ Span<int, 1> s1 = s4.First<1>();
+ static_cast<void>(s1);
+ }
+
+ // ...or dynamically
+ {
+ // NB: implicit conversion to Span<int,1> from Span<int>
+ Span<int, 1> s1 = s4.First(1);
+ static_cast<void>(s1);
+ }
+
+#if 0
+ // initialization or assignment to static Span that requires size INCREASE is not ok.
+ int arr2[2] = {1, 2};
+#endif
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ { Span<int, 4> s3 = arr2; }
+ {
+ Span<int, 2> s2 = arr2;
+ Span<int, 4> s4a = s2;
+ }
+#endif
+
+#if 0
+ {
+ auto f = [&]() {
+ Span<int, 4> _s4 = {arr2, 2};
+ static_cast<void>(_s4);
+ };
+ CHECK_THROW(f(), fail_fast);
+ }
+
+ // this should fail - we are trying to assign a small dynamic Span to a fixed_size larger one
+ Span<int> av = arr2;
+ auto f = [&]() {
+ Span<int, 4> _s4 = av;
+ static_cast<void>(_s4);
+ };
+ CHECK_THROW(f(), fail_fast);
+#endif
+}
+
+#if 0
+ SPAN_TEST(interop_with_std_regex)
+ {
+ char lat[] = { '1', '2', '3', '4', '5', '6', 'E', 'F', 'G' };
+ Span<char> s = lat;
+ auto f_it = s.begin() + 7;
+
+ std::match_results<Span<char>::iterator> match;
+
+ std::regex_match(s.begin(), s.end(), match, std::regex(".*"));
+ ASSERT_EQ(match.ready());
+ ASSERT_TRUE(!match.empty());
+ ASSERT_TRUE(match[0].matched);
+ ASSERT_TRUE(match[0].first , s.begin());
+ ASSERT_EQ(match[0].second , s.end());
+
+ std::regex_search(s.begin(), s.end(), match, std::regex("F"));
+ ASSERT_TRUE(match.ready());
+ ASSERT_TRUE(!match.empty());
+ ASSERT_TRUE(match[0].matched);
+ ASSERT_EQ(match[0].first , f_it);
+ ASSERT_EQ(match[0].second , (f_it + 1));
+ }
+
+SPAN_TEST(interop_with_gsl_at)
+{
+ int arr[5] = { 1, 2, 3, 4, 5 };
+ Span<int> s{ arr };
+ ASSERT_EQ(at(s, 0) , 1 );
+ASSERT_EQ(at(s, 1) , 2U);
+}
+#endif
+
+SPAN_TEST(default_constructible) {
+ ASSERT_TRUE((std::is_default_constructible<Span<int>>::value));
+ ASSERT_TRUE((std::is_default_constructible<Span<int, 0>>::value));
+ ASSERT_TRUE((!std::is_default_constructible<Span<int, 42>>::value));
+}
+
+SPAN_TEST(type_inference) {
+ static constexpr int arr[5] = {1, 2, 3, 4, 5};
+ constexpr auto s = Span{arr};
+ static_assert(std::is_same_v<const Span<const int, 5>, decltype(s)>);
+ static_assert(arr == s.Elements());
+}
+
+SPAN_TEST(split_at_dynamic_with_dynamic_extent) {
+ static constexpr int arr[5] = {1, 2, 3, 4, 5};
+ constexpr Span<const int> s = Span{arr};
+
+ { // Split at begin.
+ constexpr auto splitAt0Result = s.SplitAt(0);
+ static_assert(
+ std::is_same_v<Span<const int>, decltype(splitAt0Result.first)>);
+ static_assert(
+ std::is_same_v<Span<const int>, decltype(splitAt0Result.second)>);
+ ASSERT_EQ(s.Elements(), splitAt0Result.second.Elements());
+ ASSERT_EQ(0u, splitAt0Result.first.Length());
+ ASSERT_EQ(5u, splitAt0Result.second.Length());
+ }
+
+ { // Split at end.
+ constexpr auto splitAt5Result = s.SplitAt(s.Length());
+ static_assert(
+ std::is_same_v<Span<const int>, decltype(splitAt5Result.first)>);
+ static_assert(
+ std::is_same_v<Span<const int>, decltype(splitAt5Result.second)>);
+ ASSERT_EQ(s.Elements(), splitAt5Result.first.Elements());
+ ASSERT_EQ(5u, splitAt5Result.first.Length());
+ ASSERT_EQ(0u, splitAt5Result.second.Length());
+ }
+
+ {
+ // Split inside.
+ constexpr auto splitAt3Result = s.SplitAt(3);
+ static_assert(
+ std::is_same_v<Span<const int>, decltype(splitAt3Result.first)>);
+ static_assert(
+ std::is_same_v<Span<const int>, decltype(splitAt3Result.second)>);
+ ASSERT_EQ(s.Elements(), splitAt3Result.first.Elements());
+ ASSERT_EQ(s.Elements() + 3, splitAt3Result.second.Elements());
+ ASSERT_EQ(3u, splitAt3Result.first.Length());
+ ASSERT_EQ(2u, splitAt3Result.second.Length());
+ }
+}
+
+SPAN_TEST(split_at_dynamic_with_static_extent) {
+ static constexpr int arr[5] = {1, 2, 3, 4, 5};
+ constexpr auto s = Span{arr};
+
+ {
+ // Split at begin.
+ constexpr auto splitAt0Result = s.SplitAt(0);
+ static_assert(
+ std::is_same_v<Span<const int>, decltype(splitAt0Result.first)>);
+ static_assert(
+ std::is_same_v<Span<const int>, decltype(splitAt0Result.second)>);
+ ASSERT_EQ(s.Elements(), splitAt0Result.second.Elements());
+ }
+
+ {
+ // Split at end.
+ constexpr auto splitAt5Result = s.SplitAt(s.Length());
+ static_assert(
+ std::is_same_v<Span<const int>, decltype(splitAt5Result.first)>);
+ static_assert(
+ std::is_same_v<Span<const int>, decltype(splitAt5Result.second)>);
+ ASSERT_EQ(s.Elements(), splitAt5Result.first.Elements());
+ }
+
+ {
+ // Split inside.
+ constexpr auto splitAt3Result = s.SplitAt(3);
+ static_assert(
+ std::is_same_v<Span<const int>, decltype(splitAt3Result.first)>);
+ static_assert(
+ std::is_same_v<Span<const int>, decltype(splitAt3Result.second)>);
+ ASSERT_EQ(s.Elements(), splitAt3Result.first.Elements());
+ ASSERT_EQ(s.Elements() + 3, splitAt3Result.second.Elements());
+ }
+}
+
+SPAN_TEST(split_at_static) {
+ static constexpr int arr[5] = {1, 2, 3, 4, 5};
+ constexpr auto s = Span{arr};
+
+ // Split at begin.
+ constexpr auto splitAt0Result = s.SplitAt<0>();
+ static_assert(
+ std::is_same_v<Span<const int, 0>, decltype(splitAt0Result.first)>);
+ static_assert(
+ std::is_same_v<Span<const int, 5>, decltype(splitAt0Result.second)>);
+ static_assert(splitAt0Result.second.Elements() == s.Elements());
+
+ // Split at end.
+ constexpr auto splitAt5Result = s.SplitAt<s.Length()>();
+ static_assert(std::is_same_v<Span<const int, s.Length()>,
+ decltype(splitAt5Result.first)>);
+ static_assert(
+ std::is_same_v<Span<const int, 0>, decltype(splitAt5Result.second)>);
+ static_assert(splitAt5Result.first.Elements() == s.Elements());
+
+ // Split inside.
+ constexpr auto splitAt3Result = s.SplitAt<3>();
+ static_assert(
+ std::is_same_v<Span<const int, 3>, decltype(splitAt3Result.first)>);
+ static_assert(
+ std::is_same_v<Span<const int, 2>, decltype(splitAt3Result.second)>);
+ static_assert(splitAt3Result.first.Elements() == s.Elements());
+ static_assert(splitAt3Result.second.Elements() == s.Elements() + 3);
+}
+
+SPAN_TEST(as_const_dynamic) {
+ static int arr[5] = {1, 2, 3, 4, 5};
+ auto span = Span{arr, 5};
+ auto constSpan = span.AsConst();
+ static_assert(std::is_same_v<Span<const int>, decltype(constSpan)>);
+}
+
+SPAN_TEST(as_const_static) {
+ {
+ static constexpr int constArr[5] = {1, 2, 3, 4, 5};
+ constexpr auto span = Span{constArr}; // is already a Span<const int>
+ constexpr auto constSpan = span.AsConst();
+
+ static_assert(
+ std::is_same_v<const Span<const int, 5>, decltype(constSpan)>);
+ }
+
+ {
+ static int arr[5] = {1, 2, 3, 4, 5};
+ auto span = Span{arr};
+ auto constSpan = span.AsConst();
+ static_assert(std::is_same_v<Span<const int, 5>, decltype(constSpan)>);
+ }
+}
+
+SPAN_TEST(construct_from_iterators_dynamic) {
+ const int constArr[5] = {1, 2, 3, 4, 5};
+ auto constSpan = Span{constArr};
+
+ // const from const
+ {
+ const auto wholeSpan = Span{constSpan.cbegin(), constSpan.cend()};
+ static_assert(std::is_same_v<decltype(wholeSpan), const Span<const int>>);
+ ASSERT_TRUE(constSpan == wholeSpan);
+
+ const auto emptyBeginSpan = Span{constSpan.cbegin(), constSpan.cbegin()};
+ ASSERT_TRUE(emptyBeginSpan.IsEmpty());
+
+ const auto emptyEndSpan = Span{constSpan.cend(), constSpan.cend()};
+ ASSERT_TRUE(emptyEndSpan.IsEmpty());
+
+ const auto subSpan = Span{constSpan.cbegin() + 1, constSpan.cend() - 1};
+ ASSERT_EQ(constSpan.Length() - 2, subSpan.Length());
+ ASSERT_EQ(constSpan.Elements() + 1, subSpan.Elements());
+ }
+
+ int arr[5] = {1, 2, 3, 4, 5};
+ auto span = Span{arr};
+
+ // const from non-const
+ {
+ const auto wholeSpan = Span{span.cbegin(), span.cend()};
+ static_assert(std::is_same_v<decltype(wholeSpan), const Span<const int>>);
+ // XXX Can't use span == wholeSpan because of difference in constness.
+ ASSERT_EQ(span.Elements(), wholeSpan.Elements());
+ ASSERT_EQ(span.Length(), wholeSpan.Length());
+
+ const auto emptyBeginSpan = Span{span.cbegin(), span.cbegin()};
+ ASSERT_TRUE(emptyBeginSpan.IsEmpty());
+
+ const auto emptyEndSpan = Span{span.cend(), span.cend()};
+ ASSERT_TRUE(emptyEndSpan.IsEmpty());
+
+ const auto subSpan = Span{span.cbegin() + 1, span.cend() - 1};
+ ASSERT_EQ(span.Length() - 2, subSpan.Length());
+ ASSERT_EQ(span.Elements() + 1, subSpan.Elements());
+ }
+
+ // non-const from non-const
+ {
+ const auto wholeSpan = Span{span.begin(), span.end()};
+ static_assert(std::is_same_v<decltype(wholeSpan), const Span<int>>);
+ ASSERT_TRUE(span == wholeSpan);
+
+ const auto emptyBeginSpan = Span{span.begin(), span.begin()};
+ ASSERT_TRUE(emptyBeginSpan.IsEmpty());
+
+ const auto emptyEndSpan = Span{span.end(), span.end()};
+ ASSERT_TRUE(emptyEndSpan.IsEmpty());
+
+ const auto subSpan = Span{span.begin() + 1, span.end() - 1};
+ ASSERT_EQ(span.Length() - 2, subSpan.Length());
+ }
+}
+
+SPAN_TEST(construct_from_iterators_static) {
+ static constexpr int arr[5] = {1, 2, 3, 4, 5};
+ constexpr auto constSpan = Span{arr};
+
+ // const
+ {
+ const auto wholeSpan = Span{constSpan.cbegin(), constSpan.cend()};
+ static_assert(std::is_same_v<decltype(wholeSpan), const Span<const int>>);
+ ASSERT_TRUE(constSpan == wholeSpan);
+
+ const auto emptyBeginSpan = Span{constSpan.cbegin(), constSpan.cbegin()};
+ ASSERT_TRUE(emptyBeginSpan.IsEmpty());
+
+ const auto emptyEndSpan = Span{constSpan.cend(), constSpan.cend()};
+ ASSERT_TRUE(emptyEndSpan.IsEmpty());
+
+ const auto subSpan = Span{constSpan.cbegin() + 1, constSpan.cend() - 1};
+ ASSERT_EQ(constSpan.Length() - 2, subSpan.Length());
+ ASSERT_EQ(constSpan.Elements() + 1, subSpan.Elements());
+ }
+}
+
+SPAN_TEST(construct_from_container_with_type_deduction) {
+ std::vector<int> vec = {1, 2, 3, 4, 5};
+
+ // from const
+ {
+ const auto& constVecRef = vec;
+
+ auto span = Span{constVecRef};
+ static_assert(std::is_same_v<decltype(span), Span<const int>>);
+ }
+
+ // from non-const
+ {
+ auto span = Span{vec};
+ static_assert(std::is_same_v<decltype(span), Span<int>>);
+ }
+}
diff --git a/mfbt/tests/gtest/TestTainting.cpp b/mfbt/tests/gtest/TestTainting.cpp
new file mode 100644
index 0000000000..0025819c06
--- /dev/null
+++ b/mfbt/tests/gtest/TestTainting.cpp
@@ -0,0 +1,485 @@
+/* -*- 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 "gtest/gtest.h"
+#include <math.h>
+
+#include "mozilla/Array.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Range.h"
+#include "mozilla/Tainting.h"
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
+#include "nsTArray.h"
+#include <array>
+#include <deque>
+#include <forward_list>
+#include <list>
+#include <map>
+#include <set>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+using mozilla::Tainted;
+
+#define EXPECTED_INT 10
+#define EXPECTED_CHAR 'z'
+
+static bool externalFunction(int arg) { return arg > 2; }
+
+// ==================================================================
+// MOZ_VALIDATE_AND_GET =============================================
+TEST(Tainting, moz_validate_and_get)
+{
+ int bar;
+ int comparisonVariable = 20;
+ Tainted<int> foo = Tainted<int>(EXPECTED_INT);
+
+ bar = MOZ_VALIDATE_AND_GET(foo, foo < 20);
+ ASSERT_EQ(bar, EXPECTED_INT);
+
+ // This test is for comparison to an external variable, testing the
+ // default capture mode of the lambda used inside the macro.
+ bar = MOZ_VALIDATE_AND_GET(foo, foo < comparisonVariable);
+ ASSERT_EQ(bar, EXPECTED_INT);
+
+ bar = MOZ_VALIDATE_AND_GET(
+ foo, foo < 20,
+ "foo must be less than 20 because higher values represent decibel"
+ "levels greater than a a jet engine inside your ear.");
+ ASSERT_EQ(bar, EXPECTED_INT);
+
+ // Test an external variable with a comment.
+ bar = MOZ_VALIDATE_AND_GET(foo, foo < comparisonVariable, "Test comment");
+ ASSERT_EQ(bar, EXPECTED_INT);
+
+ // Test an external function with a comment.
+ bar = MOZ_VALIDATE_AND_GET(foo, externalFunction(foo), "Test comment");
+ ASSERT_EQ(bar, EXPECTED_INT);
+
+ // Lambda Tests
+ bar =
+ MOZ_VALIDATE_AND_GET(foo, ([&foo]() { return externalFunction(foo); }()));
+ ASSERT_EQ(bar, EXPECTED_INT);
+
+ // This test is for the lambda variant with a supplied assertion
+ // string.
+ bar =
+ MOZ_VALIDATE_AND_GET(foo, ([&foo]() { return externalFunction(foo); }()),
+ "This tests a comment");
+ ASSERT_EQ(bar, EXPECTED_INT);
+
+ // This test is for the lambda variant with a captured variable
+ bar = MOZ_VALIDATE_AND_GET(foo, ([&foo, &comparisonVariable] {
+ bool intermediateResult = externalFunction(foo);
+ return intermediateResult ||
+ comparisonVariable < 4;
+ }()),
+ "This tests a comment");
+ ASSERT_EQ(bar, EXPECTED_INT);
+
+ // This test is for the lambda variant with full capture mode
+ bar = MOZ_VALIDATE_AND_GET(foo, ([&] {
+ bool intermediateResult = externalFunction(foo);
+ return intermediateResult ||
+ comparisonVariable < 4;
+ }()),
+ "This tests a comment");
+ ASSERT_EQ(bar, EXPECTED_INT);
+
+ // External lambdas
+ auto lambda1 = [](int foo) { return externalFunction(foo); };
+
+ auto lambda2 = [&](int foo) {
+ bool intermediateResult = externalFunction(foo);
+ return intermediateResult || comparisonVariable < 4;
+ };
+
+ // Test with an explicit capture
+ auto lambda3 = [&comparisonVariable](int foo) {
+ bool intermediateResult = externalFunction(foo);
+ return intermediateResult || comparisonVariable < 4;
+ };
+
+ bar = MOZ_VALIDATE_AND_GET(foo, lambda1(foo));
+ ASSERT_EQ(bar, EXPECTED_INT);
+
+ // Test with a comment
+ bar = MOZ_VALIDATE_AND_GET(foo, lambda1(foo), "Test comment.");
+ ASSERT_EQ(bar, EXPECTED_INT);
+
+ // Test with a default capture mode
+ bar = MOZ_VALIDATE_AND_GET(foo, lambda2(foo), "Test comment.");
+ ASSERT_EQ(bar, EXPECTED_INT);
+
+ bar = MOZ_VALIDATE_AND_GET(foo, lambda3(foo), "Test comment.");
+ ASSERT_EQ(bar, EXPECTED_INT);
+
+ // We can't test MOZ_VALIDATE_AND_GET failing, because that triggers
+ // a release assert.
+}
+
+// ==================================================================
+// MOZ_IS_VALID =====================================================
+TEST(Tainting, moz_is_valid)
+{
+ int comparisonVariable = 20;
+ Tainted<int> foo = Tainted<int>(EXPECTED_INT);
+
+ ASSERT_TRUE(MOZ_IS_VALID(foo, foo < 20));
+
+ ASSERT_FALSE(MOZ_IS_VALID(foo, foo > 20));
+
+ ASSERT_TRUE(MOZ_IS_VALID(foo, foo < comparisonVariable));
+
+ ASSERT_TRUE(
+ MOZ_IS_VALID(foo, ([&foo]() { return externalFunction(foo); }())));
+
+ ASSERT_TRUE(MOZ_IS_VALID(foo, ([&foo, &comparisonVariable]() {
+ bool intermediateResult = externalFunction(foo);
+ return intermediateResult ||
+ comparisonVariable < 4;
+ }())));
+
+ // External lambdas
+ auto lambda1 = [](int foo) { return externalFunction(foo); };
+
+ auto lambda2 = [&](int foo) {
+ bool intermediateResult = externalFunction(foo);
+ return intermediateResult || comparisonVariable < 4;
+ };
+
+ // Test with an explicit capture
+ auto lambda3 = [&comparisonVariable](int foo) {
+ bool intermediateResult = externalFunction(foo);
+ return intermediateResult || comparisonVariable < 4;
+ };
+
+ ASSERT_TRUE(MOZ_IS_VALID(foo, lambda1(foo)));
+
+ ASSERT_TRUE(MOZ_IS_VALID(foo, lambda2(foo)));
+
+ ASSERT_TRUE(MOZ_IS_VALID(foo, lambda3(foo)));
+}
+
+// ==================================================================
+// MOZ_VALIDATE_OR ==================================================
+TEST(Tainting, moz_validate_or)
+{
+ int result;
+ int comparisonVariable = 20;
+ Tainted<int> foo = Tainted<int>(EXPECTED_INT);
+
+ result = MOZ_VALIDATE_OR(foo, foo < 20, 100);
+ ASSERT_EQ(result, EXPECTED_INT);
+
+ result = MOZ_VALIDATE_OR(foo, foo > 20, 100);
+ ASSERT_EQ(result, 100);
+
+ result = MOZ_VALIDATE_OR(foo, foo < comparisonVariable, 100);
+ ASSERT_EQ(result, EXPECTED_INT);
+
+ // External lambdas
+ auto lambda1 = [](int foo) { return externalFunction(foo); };
+
+ auto lambda2 = [&](int foo) {
+ bool intermediateResult = externalFunction(foo);
+ return intermediateResult || comparisonVariable < 4;
+ };
+
+ // Test with an explicit capture
+ auto lambda3 = [&comparisonVariable](int foo) {
+ bool intermediateResult = externalFunction(foo);
+ return intermediateResult || comparisonVariable < 4;
+ };
+
+ result = MOZ_VALIDATE_OR(foo, lambda1(foo), 100);
+ ASSERT_EQ(result, EXPECTED_INT);
+
+ result = MOZ_VALIDATE_OR(foo, lambda2(foo), 100);
+ ASSERT_EQ(result, EXPECTED_INT);
+
+ result = MOZ_VALIDATE_OR(foo, lambda3(foo), 100);
+ ASSERT_EQ(result, EXPECTED_INT);
+
+ result =
+ MOZ_VALIDATE_OR(foo, ([&foo]() { return externalFunction(foo); }()), 100);
+ ASSERT_EQ(result, EXPECTED_INT);
+
+ // This test is for the lambda variant with a supplied assertion
+ // string.
+ result =
+ MOZ_VALIDATE_OR(foo, ([&foo] { return externalFunction(foo); }()), 100);
+ ASSERT_EQ(result, EXPECTED_INT);
+
+ // This test is for the lambda variant with a captured variable
+ result =
+ MOZ_VALIDATE_OR(foo, ([&foo, &comparisonVariable] {
+ bool intermediateResult = externalFunction(foo);
+ return intermediateResult || comparisonVariable < 4;
+ }()),
+ 100);
+ ASSERT_EQ(result, EXPECTED_INT);
+
+ // This test is for the lambda variant with full capture mode
+ result =
+ MOZ_VALIDATE_OR(foo, ([&] {
+ bool intermediateResult = externalFunction(foo);
+ return intermediateResult || comparisonVariable < 4;
+ }()),
+ 100);
+ ASSERT_EQ(result, EXPECTED_INT);
+}
+
+// ==================================================================
+// MOZ_FIND_AND_VALIDATE ============================================
+TEST(Tainting, moz_find_and_validate)
+{
+ Tainted<int> foo = Tainted<int>(EXPECTED_INT);
+ Tainted<char> baz = Tainted<char>(EXPECTED_CHAR);
+
+ //-------------------------------
+ const mozilla::Array<int, 6> mozarrayWithFoo(0, 5, EXPECTED_INT, 15, 20, 25);
+ const mozilla::Array<int, 5> mozarrayWithoutFoo(0, 5, 15, 20, 25);
+
+ ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, mozarrayWithFoo) ==
+ mozarrayWithFoo[2]);
+
+ ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo,
+ mozarrayWithoutFoo) == nullptr);
+
+ //-------------------------------
+ class TestClass {
+ public:
+ int a;
+ int b;
+
+ TestClass(int a, int b) {
+ this->a = a;
+ this->b = b;
+ }
+
+ bool operator==(const TestClass& other) const {
+ return this->a == other.a && this->b == other.b;
+ }
+ };
+
+ const mozilla::Array<TestClass, 5> mozarrayOfClassesWithFoo(
+ TestClass(0, 1), TestClass(2, 3), TestClass(EXPECTED_INT, EXPECTED_INT),
+ TestClass(4, 5), TestClass(6, 7));
+
+ ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(
+ foo, foo == list_item.a && foo == list_item.b,
+ mozarrayOfClassesWithFoo) == mozarrayOfClassesWithFoo[2]);
+
+ ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(
+ foo, (foo == list_item.a && foo == list_item.b),
+ mozarrayOfClassesWithFoo) == mozarrayOfClassesWithFoo[2]);
+
+ ASSERT_TRUE(
+ *MOZ_FIND_AND_VALIDATE(
+ foo,
+ (foo == list_item.a && foo == list_item.b && externalFunction(foo)),
+ mozarrayOfClassesWithFoo) == mozarrayOfClassesWithFoo[2]);
+
+ ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(
+ foo, ([](int tainted_val, TestClass list_item) {
+ return tainted_val == list_item.a &&
+ tainted_val == list_item.b;
+ }(foo, list_item)),
+ mozarrayOfClassesWithFoo) == mozarrayOfClassesWithFoo[2]);
+
+ auto lambda4 = [](int tainted_val, TestClass list_item) {
+ return tainted_val == list_item.a && tainted_val == list_item.b;
+ };
+
+ ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, lambda4(foo, list_item),
+ mozarrayOfClassesWithFoo) ==
+ mozarrayOfClassesWithFoo[2]);
+
+ //-------------------------------
+ const char m[] = "m";
+ const char o[] = "o";
+ const char z[] = {EXPECTED_CHAR, '\0'};
+ const char l[] = "l";
+ const char a[] = "a";
+
+ nsTHashtable<nsCharPtrHashKey> hashtableWithBaz;
+ hashtableWithBaz.PutEntry(m);
+ hashtableWithBaz.PutEntry(o);
+ hashtableWithBaz.PutEntry(z);
+ hashtableWithBaz.PutEntry(l);
+ hashtableWithBaz.PutEntry(a);
+ nsTHashtable<nsCharPtrHashKey> hashtableWithoutBaz;
+ hashtableWithoutBaz.PutEntry(m);
+ hashtableWithoutBaz.PutEntry(o);
+ hashtableWithoutBaz.PutEntry(l);
+ hashtableWithoutBaz.PutEntry(a);
+
+ ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(baz, *list_item.GetKey() == baz,
+ hashtableWithBaz) ==
+ hashtableWithBaz.GetEntry(z));
+
+ ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(baz, *list_item.GetKey() == baz,
+ hashtableWithoutBaz) == nullptr);
+
+ //-------------------------------
+ const nsTArray<int> nsTArrayWithFoo = {0, 5, EXPECTED_INT, 15, 20, 25};
+ const nsTArray<int> nsTArrayWithoutFoo = {0, 5, 15, 20, 25};
+
+ ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, nsTArrayWithFoo) ==
+ nsTArrayWithFoo[2]);
+
+ ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo,
+ nsTArrayWithoutFoo) == nullptr);
+
+ //-------------------------------
+ const std::array<int, 6> arrayWithFoo{0, 5, EXPECTED_INT, 15, 20, 25};
+ const std::array<int, 5> arrayWithoutFoo{0, 5, 15, 20, 25};
+
+ ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, arrayWithFoo) ==
+ arrayWithFoo[2]);
+
+ ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, arrayWithoutFoo) ==
+ nullptr);
+
+ //-------------------------------
+ const std::deque<int> dequeWithFoo{0, 5, EXPECTED_INT, 15, 20, 25};
+ const std::deque<int> dequeWithoutFoo{0, 5, 15, 20, 25};
+
+ ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, dequeWithFoo) ==
+ dequeWithFoo[2]);
+
+ ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, dequeWithoutFoo) ==
+ nullptr);
+
+ //-------------------------------
+ const std::forward_list<int> forwardWithFoo{0, 5, EXPECTED_INT, 15, 20, 25};
+ const std::forward_list<int> forwardWithoutFoo{0, 5, 15, 20, 25};
+
+ auto forwardListIt = forwardWithFoo.begin();
+ std::advance(forwardListIt, 2);
+
+ ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, forwardWithFoo) ==
+ *forwardListIt);
+
+ ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, forwardWithoutFoo) ==
+ nullptr);
+
+ //-------------------------------
+ const std::list<int> listWithFoo{0, 5, EXPECTED_INT, 15, 20, 25};
+ const std::list<int> listWithoutFoo{0, 5, 15, 20, 25};
+
+ auto listIt = listWithFoo.begin();
+ std::advance(listIt, 2);
+
+ ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, listWithFoo) ==
+ *listIt);
+
+ ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, listWithoutFoo) ==
+ nullptr);
+
+ //-------------------------------
+ const std::map<std::string, int> mapWithFoo{{
+ {"zero", 0},
+ {"five", 5},
+ {"ten", EXPECTED_INT},
+ {"fifteen", 15},
+ {"twenty", 20},
+ {"twenty-five", 25},
+ }};
+ const std::map<std::string, int> mapWithoutFoo{{
+ {"zero", 0},
+ {"five", 5},
+ {"fifteen", 15},
+ {"twenty", 20},
+ {"twenty-five", 25},
+ }};
+
+ const auto map_it = mapWithFoo.find("ten");
+
+ ASSERT_TRUE(
+ MOZ_FIND_AND_VALIDATE(foo, list_item.second == foo, mapWithFoo)->second ==
+ map_it->second);
+
+ ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item.second == foo,
+ mapWithoutFoo) == nullptr);
+
+ //-------------------------------
+ const std::set<int> setWithFoo{0, 5, EXPECTED_INT, 15, 20, 25};
+ const std::set<int> setWithoutFoo{0, 5, 15, 20, 25};
+
+ auto setIt = setWithFoo.find(EXPECTED_INT);
+
+ ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, setWithFoo) ==
+ *setIt);
+
+ ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, setWithoutFoo) ==
+ nullptr);
+
+ //-------------------------------
+ const std::unordered_map<std::string, int> unordermapWithFoo = {
+ {"zero", 0}, {"five", 5}, {"ten", EXPECTED_INT},
+ {"fifteen", 15}, {"twenty", 20}, {"twenty-five", 25},
+ };
+ const std::unordered_map<std::string, int> unordermapWithoutFoo{{
+ {"zero", 0},
+ {"five", 5},
+ {"fifteen", 15},
+ {"twenty", 20},
+ {"twenty-five", 25},
+ }};
+
+ auto unorderedMapIt = unordermapWithFoo.find("ten");
+
+ ASSERT_TRUE(
+ MOZ_FIND_AND_VALIDATE(foo, list_item.second == foo, unordermapWithFoo)
+ ->second == unorderedMapIt->second);
+
+ ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item.second == foo,
+ unordermapWithoutFoo) == nullptr);
+
+ //-------------------------------
+ const std::unordered_set<int> unorderedsetWithFoo{0, 5, EXPECTED_INT,
+ 15, 20, 25};
+ const std::unordered_set<int> unorderedsetWithoutFoo{0, 5, 15, 20, 25};
+
+ auto unorderedSetIt = unorderedsetWithFoo.find(EXPECTED_INT);
+
+ ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo,
+ unorderedsetWithFoo) == *unorderedSetIt);
+
+ ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo,
+ unorderedsetWithoutFoo) == nullptr);
+
+ //-------------------------------
+ const std::vector<int> vectorWithFoo{0, 5, EXPECTED_INT, 15, 20, 25};
+ const std::vector<int> vectorWithoutFoo{0, 5, 15, 20, 25};
+
+ ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, vectorWithFoo) ==
+ vectorWithFoo[2]);
+
+ ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, vectorWithoutFoo) ==
+ nullptr);
+}
+
+// ==================================================================
+// MOZ_NO_VALIDATE ==================================================
+TEST(Tainting, moz_no_validate)
+{
+ int result;
+ Tainted<int> foo = Tainted<int>(EXPECTED_INT);
+
+ result = MOZ_NO_VALIDATE(
+ foo,
+ "Value is used to match against a dictionary key in the parent."
+ "If there's no key present, there won't be a match."
+ "There is no risk of grabbing a cross-origin value from the dictionary,"
+ "because the IPC actor is instatiated per-content-process and the "
+ "dictionary is not shared between actors.");
+ ASSERT_TRUE(result == EXPECTED_INT);
+}
diff --git a/mfbt/tests/gtest/moz.build b/mfbt/tests/gtest/moz.build
new file mode 100644
index 0000000000..0af8d1ea75
--- /dev/null
+++ b/mfbt/tests/gtest/moz.build
@@ -0,0 +1,32 @@
+# -*- 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/.
+
+UNIFIED_SOURCES += [
+ "TestBuffer.cpp",
+ "TestLinkedList.cpp",
+ "TestReverseIterator.cpp",
+ "TestSpan.cpp",
+ "TestTainting.cpp",
+]
+
+SOURCES += [
+ "TestAlgorithm.cpp",
+ "TestInitializedOnce.cpp",
+ "TestMainThreadWeakPtr.cpp",
+ "TestResultExtensions.cpp",
+]
+
+if not CONFIG["MOZILLA_OFFICIAL"]:
+ UNIFIED_SOURCES += [
+ # MOZ_DBG is not defined in MOZILLA_OFFICIAL builds.
+ "TestMozDbg.cpp",
+ ]
+
+# LOCAL_INCLUDES += [
+# "../../base",
+# ]
+
+FINAL_LIBRARY = "xul-gtest"
diff --git a/mfbt/tests/moz.build b/mfbt/tests/moz.build
new file mode 100644
index 0000000000..231bec84a3
--- /dev/null
+++ b/mfbt/tests/moz.build
@@ -0,0 +1,117 @@
+# -*- 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/.
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"]:
+ TEST_DIRS += [
+ "gtest",
+ ]
+
+# Important: for these tests to be run, they also need to be added
+# to testing/cppunittest.toml.
+CppUnitTests(
+ [
+ "TestAlgorithm",
+ "TestArray",
+ "TestArrayUtils",
+ "TestAtomicBitfields",
+ "TestAtomics",
+ "TestBinarySearch",
+ "TestBitSet",
+ "TestBloomFilter",
+ "TestBufferList",
+ "TestCasting",
+ "TestCeilingFloor",
+ "TestCheckedInt",
+ "TestCompactPair",
+ "TestCountPopulation",
+ "TestCountZeroes",
+ "TestDefineEnum",
+ "TestDoublyLinkedList",
+ "TestEndian",
+ "TestEnumeratedArray",
+ "TestEnumSet",
+ "TestEnumTypeTraits",
+ "TestFastBernoulliTrial",
+ "TestFloatingPoint",
+ "TestFunctionRef",
+ "TestFunctionTypeTraits",
+ "TestHashTable",
+ "TestIntegerRange",
+ "TestJSONWriter",
+ "TestLinkedList",
+ "TestMacroArgs",
+ "TestMacroForEach",
+ "TestMathAlgorithms",
+ "TestMaybe",
+ "TestNonDereferenceable",
+ "TestNotNull",
+ "TestRandomNum",
+ "TestRange",
+ "TestRefPtr",
+ "TestResult",
+ "TestRollingMean",
+ "TestSaturate",
+ "TestScopeExit",
+ "TestSegmentedVector",
+ "TestSHA1",
+ "TestSIMD",
+ "TestSmallPointerArray",
+ "TestSplayTree",
+ "TestTextUtils",
+ "TestTypedEnum",
+ "TestUniquePtr",
+ "TestVariant",
+ "TestVector",
+ "TestWeakPtr",
+ "TestWrappingOperations",
+ "TestXorShift128PlusRNG",
+ ]
+)
+
+# We don't support these tests yet because of the lack of thread support for wasi.
+if CONFIG["OS_ARCH"] != "WASI":
+ CppUnitTests(
+ [
+ "TestSPSCQueue",
+ "TestThreadSafeWeakPtr",
+ ]
+ )
+
+if CONFIG["OS_ARCH"] == "WINNT":
+ CppUnitTests(
+ [
+ "TestWinArchDefs",
+ ]
+ )
+
+# Not to be unified with the rest, because this test
+# sets MOZ_PRETEND_NO_JSRUST, which changes the behavior
+# of the included headers.
+CppUnitTests(
+ [
+ "TestUtf8",
+ ]
+)
+
+# Wasi doesn't support <signal> yet so skip this test.
+if not CONFIG["MOZ_ASAN"] and not CONFIG["MOZ_TSAN"] and CONFIG["OS_ARCH"] != "WASI":
+ CppUnitTests(
+ [
+ "TestPoisonArea",
+ ]
+ )
+
+DisableStlWrapping()
+
+if CONFIG["CC_TYPE"] == "clang-cl":
+ 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 += [
+ "mozglue",
+]