diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:44:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:44:51 +0000 |
commit | 9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /mfbt/tests | |
parent | Initial commit. (diff) | |
download | thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.tar.xz thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mfbt/tests')
71 files changed, 23440 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..a81574b5c5 --- /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..714c6e8340 --- /dev/null +++ b/mfbt/tests/TestFunctionRef.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/FunctionRef.h" +#include "mozilla/UniquePtr.h" + +#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; } +}; + +static void TestNonmemberFunction() { + mozilla::FunctionRef<int(int)> f(&increment); + CHECK(f(42) == 43); +} + +static void TestStaticMemberFunction() { + mozilla::FunctionRef<int(int)> f(&S::increment); + CHECK(f(42) == 43); +} + +static void TestFunctionObject() { + auto incrementor = Incrementor(); + mozilla::FunctionRef<int(int)> f(incrementor); + CHECK(f(42) == 43); +} + +static void TestLambda() { + // Test non-capturing lambda + auto lambda1 = [](int arg) { return arg + 1; }; + mozilla::FunctionRef<int(int)> f(lambda1); + CHECK(f(42) == 43); + + // Test capturing lambda + int one = 1; + auto lambda2 = [one](int arg) { return arg + one; }; + mozilla::FunctionRef<int(int)> g(lambda2); + CHECK(g(42) == 43); + + mozilla::FunctionRef<int(int)> h([](int arg) { return arg + 1; }); + CHECK(h(42) == 43); +} + +static void TestOperatorBool() { + mozilla::FunctionRef<int(int)> f1; + CHECK(!static_cast<bool>(f1)); + + mozilla::FunctionRef<int(int)> f2 = increment; + CHECK(static_cast<bool>(f2)); + + mozilla::FunctionRef<int(int)> f3 = nullptr; + CHECK(!static_cast<bool>(f3)); +} + +static void TestReferenceParameters() { + mozilla::FunctionRef<int(const int&, const int&)> f = &addConstRefs; + int x = 1; + int y = 2; + CHECK(f(x, y) == 3); +} + +static void TestVoidNoParameters() { + mozilla::FunctionRef<void()> f = &helloWorld; + CHECK(!helloWorldCalled); + f(); + CHECK(helloWorldCalled); +} + +static void TestPointerParameters() { + mozilla::FunctionRef<void(int*)> f = &incrementPointer; + int x = 1; + f(&x); + CHECK(x == 2); +} + +static void TestImplicitFunctorTypeConversion() { + auto incrementor = Incrementor(); + mozilla::FunctionRef<long(short)> f = incrementor; + short x = 1; + CHECK(f(x) == 2); +} + +static void TestImplicitLambdaTypeConversion() { + mozilla::FunctionRef<long(short)> f = [](short arg) { return arg + 1; }; + short x = 1; + CHECK(f(x) == 2); +} + +static void TestImplicitFunctionPointerTypeConversion() { + mozilla::FunctionRef<long(short)> f = &increment; + short x = 1; + CHECK(f(x) == 2); +} + +static void TestMoveOnlyArguments() { + mozilla::FunctionRef<int(mozilla::UniquePtr<int>)> f(&incrementUnique); + + CHECK(f(mozilla::MakeUnique<int>(5)) == 6); +} + +int main() { + TestNonmemberFunction(); + TestStaticMemberFunction(); + TestFunctionObject(); + 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(¬hing == &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..20680e41c8 --- /dev/null +++ b/mfbt/tests/TestResult.cpp @@ -0,0 +1,671 @@ +/* -*- 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 <string.h> +#include "mozilla/ResultVariant.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()); + } +} + +/* * */ + +int main() { + BasicTests(); + InPlaceConstructionTests(); + TypeConversionTests(); + EmptyValueTest(); + MapTest(); + MapErrTest(); + OrElseTest(); + AndThenTest(); + UniquePtrTest(); + 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..d114fe72a9 --- /dev/null +++ b/mfbt/tests/TestSPSCQueue.cpp @@ -0,0 +1,248 @@ +/* -*- 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 t([&ring] { + std::unique_ptr<float[]> inBuffer(new float[ENQUEUE_SIZE]); + int rv = ring.Enqueue(inBuffer.get(), ENQUEUE_SIZE); + MOZ_RELEASE_ASSERT(rv > 0); + }); + + t.join(); + + ring.ResetThreadIds(); + + // Enqueue with a different thread. We have reset the thread ID + // in the ring buffer, this should work. + std::thread t2([&ring] { + std::unique_ptr<float[]> inBuffer(new float[ENQUEUE_SIZE]); + int rv = ring.Enqueue(inBuffer.get(), ENQUEUE_SIZE); + MOZ_RELEASE_ASSERT(rv > 0); + }); + + t2.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..4cfa19c09a --- /dev/null +++ b/mfbt/tests/TestSegmentedVector.cpp @@ -0,0 +1,369 @@ +/* -*- 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); + } +}; + +// 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, n; + + 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()); + MOZ_RELEASE_ASSERT(v.Length() == 100); + + n = 0; + for (auto iter = v.Iter(); !iter.Done(); iter.Next()) { + MOZ_RELEASE_ASSERT(iter.Get() == n); + n++; + } + MOZ_RELEASE_ASSERT(n == 100); + + // Add another 900 elements, then re-check. + for (; i < 1000; i++) { + v.InfallibleAppend(std::move(i)); + } + MOZ_RELEASE_ASSERT(!v.IsEmpty()); + MOZ_RELEASE_ASSERT(v.Length() == 1000); + + n = 0; + for (auto iter = v.Iter(); !iter.Done(); iter.Next()) { + MOZ_RELEASE_ASSERT(iter.Get() == n); + n++; + } + MOZ_RELEASE_ASSERT(n == 1000); + + // Pop off all of the elements. + MOZ_RELEASE_ASSERT(v.Length() == 1000); + for (int len = (int)v.Length(); len > 0; len--) { + MOZ_RELEASE_ASSERT(v.GetLast() == len - 1); + v.PopLast(); + } + MOZ_RELEASE_ASSERT(v.IsEmpty()); + MOZ_RELEASE_ASSERT(v.Length() == 0); + + // Fill the vector up again to prepare for the clear. + for (i = 0; i < 1000; i++) { + v.InfallibleAppend(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. + n = 0; + for (auto iter = v.Iter(); !iter.Done(); iter.Next()) { + MOZ_RELEASE_ASSERT(iter.Get() == n); + n++; + } + MOZ_RELEASE_ASSERT(n == 700); +} + +static size_t gNumDefaultCtors; +static size_t gNumExplicitCtors; +static size_t gNumCopyCtors; +static size_t gNumMoveCtors; +static size_t gNumDtors; + +struct NonPOD { + NonPOD() { gNumDefaultCtors++; } + explicit NonPOD(int x) { gNumExplicitCtors++; } + NonPOD(NonPOD&) { gNumCopyCtors++; } + NonPOD(NonPOD&&) { gNumMoveCtors++; } + ~NonPOD() { gNumDtors++; } +}; + +// This tests how segmented vectors with non-POD elements construct and +// destruct those elements. +void TestConstructorsAndDestructors() { + size_t defaultCtorCalls = 0; + size_t explicitCtorCalls = 0; + size_t copyCtorCalls = 0; + size_t moveCtorCalls = 0; + size_t dtorCalls = 0; + + { + static const size_t segmentSize = 64; + + // A SegmentedVector with a non-POD element type. + NonPOD x(1); // explicit constructor called + explicitCtorCalls++; + SegmentedVector<NonPOD, segmentSize, InfallibleAllocPolicy> v; + // default constructor called 0 times + MOZ_RELEASE_ASSERT(v.IsEmpty()); + gDummy = v.Append(x); // copy constructor called + copyCtorCalls++; + NonPOD y(1); // explicit constructor called + explicitCtorCalls++; + gDummy = v.Append(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(); + 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..3a7b3e3771 --- /dev/null +++ b/mfbt/tests/TestUniquePtr.cpp @@ -0,0 +1,575 @@ +/* -*- 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 <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; +} + +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; + } + return 0; +} diff --git a/mfbt/tests/TestUtf8.cpp b/mfbt/tests/TestUtf8.cpp new file mode 100644 index 0000000000..ee8810ab11 --- /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/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..21b25b8afa --- /dev/null +++ b/mfbt/tests/moz.build @@ -0,0 +1,110 @@ +# -*- 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.ini. +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", + ] + ) + +# 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", +] |