Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

View File

@ -0,0 +1,66 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/address-region.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace base {
using Address = AddressRegion::Address;
TEST(AddressRegionTest, Contains) {
struct {
Address start;
size_t size;
} test_cases[] = {{153, 771}, {0, 227}, {static_cast<Address>(-447), 447}};
for (size_t i = 0; i < arraysize(test_cases); i++) {
Address start = test_cases[i].start;
size_t size = test_cases[i].size;
Address end = start + size; // exclusive
AddressRegion region(start, size);
// Test single-argument contains().
CHECK(!region.contains(start - 1041));
CHECK(!region.contains(start - 1));
CHECK(!region.contains(end));
CHECK(!region.contains(end + 1));
CHECK(!region.contains(end + 113));
CHECK(region.contains(start));
CHECK(region.contains(start + 1));
CHECK(region.contains(start + size / 2));
CHECK(region.contains(end - 1));
// Test two-arguments contains().
CHECK(!region.contains(start - 1, size));
CHECK(!region.contains(start, size + 1));
CHECK(!region.contains(start - 17, 17));
CHECK(!region.contains(start - 17, size * 2));
CHECK(!region.contains(end, 1));
CHECK(!region.contains(end, static_cast<size_t>(0 - end)));
CHECK(region.contains(start, size));
CHECK(region.contains(start, 10));
CHECK(region.contains(start + 11, 120));
CHECK(region.contains(end - 13, 13));
CHECK(!region.contains(end, 0));
// Zero-size queries.
CHECK(!region.contains(start - 10, 0));
CHECK(!region.contains(start - 1, 0));
CHECK(!region.contains(end, 0));
CHECK(!region.contains(end + 10, 0));
CHECK(region.contains(start, 0));
CHECK(region.contains(start + 10, 0));
CHECK(region.contains(end - 1, 0));
}
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,219 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <limits.h>
#include "src/base/atomic-utils.h"
#include "src/base/platform/platform.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace base {
namespace {
enum TestFlag : base::AtomicWord { kA, kB, kC };
} // namespace
TEST(AtomicValue, Initial) {
AtomicValue<TestFlag> a(kA);
EXPECT_EQ(TestFlag::kA, a.Value());
}
TEST(AtomicValue, SetValue) {
AtomicValue<TestFlag> a(kB);
a.SetValue(kC);
EXPECT_EQ(TestFlag::kC, a.Value());
}
TEST(AtomicValue, WithVoidStar) {
AtomicValue<void*> a(nullptr);
AtomicValue<void*> dummy(nullptr);
EXPECT_EQ(nullptr, a.Value());
a.SetValue(&a);
EXPECT_EQ(&a, a.Value());
}
TEST(AsAtomic8, CompareAndSwap_Sequential) {
uint8_t bytes[8];
for (int i = 0; i < 8; i++) {
bytes[i] = 0xF0 + i;
}
for (int i = 0; i < 8; i++) {
EXPECT_EQ(0xF0 + i,
AsAtomic8::Release_CompareAndSwap(&bytes[i], i, 0xF7 + i));
}
for (int i = 0; i < 8; i++) {
EXPECT_EQ(0xF0 + i,
AsAtomic8::Release_CompareAndSwap(&bytes[i], 0xF0 + i, 0xF7 + i));
}
for (int i = 0; i < 8; i++) {
EXPECT_EQ(0xF7 + i, bytes[i]);
}
}
namespace {
class ByteIncrementingThread final : public Thread {
public:
ByteIncrementingThread()
: Thread(Options("ByteIncrementingThread")),
byte_addr_(nullptr),
increments_(0) {}
void Initialize(uint8_t* byte_addr, int increments) {
byte_addr_ = byte_addr;
increments_ = increments;
}
void Run() override {
for (int i = 0; i < increments_; i++) {
Increment();
}
}
void Increment() {
uint8_t byte;
do {
byte = AsAtomic8::Relaxed_Load(byte_addr_);
} while (AsAtomic8::Release_CompareAndSwap(byte_addr_, byte, byte + 1) !=
byte);
}
private:
uint8_t* byte_addr_;
int increments_;
};
} // namespace
TEST(AsAtomic8, CompareAndSwap_Concurrent) {
const int kIncrements = 10;
const int kByteCount = 8;
uint8_t bytes[kByteCount];
const int kThreadsPerByte = 4;
const int kThreadCount = kByteCount * kThreadsPerByte;
ByteIncrementingThread threads[kThreadCount];
for (int i = 0; i < kByteCount; i++) {
AsAtomic8::Relaxed_Store(&bytes[i], i);
for (int j = 0; j < kThreadsPerByte; j++) {
threads[i * kThreadsPerByte + j].Initialize(&bytes[i], kIncrements);
}
}
for (int i = 0; i < kThreadCount; i++) {
CHECK(threads[i].Start());
}
for (int i = 0; i < kThreadCount; i++) {
threads[i].Join();
}
for (int i = 0; i < kByteCount; i++) {
EXPECT_EQ(i + kIncrements * kThreadsPerByte,
AsAtomic8::Relaxed_Load(&bytes[i]));
}
}
TEST(AsAtomicWord, Relaxed_SetBits_Sequential) {
uintptr_t word = 0;
// Fill the word with a repeated 0xF0 pattern.
for (unsigned i = 0; i < sizeof(word); i++) {
word = (word << 8) | 0xF0;
}
// Check the pattern.
for (unsigned i = 0; i < sizeof(word); i++) {
EXPECT_EQ(0xF0u, (word >> (i * 8) & 0xFFu));
}
// Set the i-th byte value to i.
uintptr_t mask = 0xFF;
for (unsigned i = 0; i < sizeof(word); i++) {
uintptr_t byte = static_cast<uintptr_t>(i) << (i * 8);
AsAtomicWord::Relaxed_SetBits(&word, byte, mask);
mask <<= 8;
}
for (unsigned i = 0; i < sizeof(word); i++) {
EXPECT_EQ(i, (word >> (i * 8) & 0xFFu));
}
}
TEST(AsAtomicWord, Release_SetBits_Sequential) {
uintptr_t word = 0;
// Fill the word with a repeated 0xF0 pattern.
for (unsigned i = 0; i < sizeof(word); i++) {
word = (word << 8) | 0xF0;
}
// Check the pattern.
for (unsigned i = 0; i < sizeof(word); i++) {
EXPECT_EQ(0xF0u, (word >> (i * 8) & 0xFFu));
}
// Set the i-th byte value to i.
uintptr_t mask = 0xFF;
for (unsigned i = 0; i < sizeof(word); i++) {
uintptr_t byte = static_cast<uintptr_t>(i) << (i * 8);
AsAtomicWord::Release_SetBits(&word, byte, mask);
mask <<= 8;
}
for (unsigned i = 0; i < sizeof(word); i++) {
EXPECT_EQ(i, (word >> (i * 8) & 0xFFu));
}
}
namespace {
class BitSettingThread final : public Thread {
public:
BitSettingThread()
: Thread(Options("BitSettingThread")),
word_addr_(nullptr),
bit_index_(0) {}
void Initialize(uintptr_t* word_addr, int bit_index) {
word_addr_ = word_addr;
bit_index_ = bit_index;
}
void Run() override {
uintptr_t bit = 1;
bit = bit << bit_index_;
AsAtomicWord::Relaxed_SetBits(word_addr_, bit, bit);
}
private:
uintptr_t* word_addr_;
int bit_index_;
};
} // namespace.
TEST(AsAtomicWord, SetBits_Concurrent) {
const int kBitCount = sizeof(uintptr_t) * 8;
const int kThreadCount = kBitCount / 2;
BitSettingThread threads[kThreadCount];
uintptr_t word;
AsAtomicWord::Relaxed_Store(&word, 0);
for (int i = 0; i < kThreadCount; i++) {
// Thread i sets bit number i * 2.
threads[i].Initialize(&word, i * 2);
}
for (int i = 0; i < kThreadCount; i++) {
CHECK(threads[i].Start());
}
for (int i = 0; i < kThreadCount; i++) {
threads[i].Join();
}
uintptr_t actual_word = AsAtomicWord::Relaxed_Load(&word);
for (int i = 0; i < kBitCount; i++) {
// Every second bit must be set.
uintptr_t expected = (i % 2 == 0);
EXPECT_EQ(expected, actual_word & 1u);
actual_word >>= 1;
}
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,314 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "src/base/atomicops.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace base {
#define CHECK_EQU(v1, v2) \
CHECK_EQ(static_cast<int64_t>(v1), static_cast<int64_t>(v2))
#define NUM_BITS(T) (sizeof(T) * 8)
template <class AtomicType>
static void TestAtomicIncrement() {
// For now, we just test the single-threaded execution.
// Use a guard value to make sure that Relaxed_AtomicIncrement doesn't
// go outside the expected address bounds. This is to test that the
// 32-bit Relaxed_AtomicIncrement doesn't do the wrong thing on 64-bit
// machines.
struct {
AtomicType prev_word;
AtomicType count;
AtomicType next_word;
} s;
AtomicType prev_word_value, next_word_value;
memset(&prev_word_value, 0xFF, sizeof(AtomicType));
memset(&next_word_value, 0xEE, sizeof(AtomicType));
s.prev_word = prev_word_value;
s.count = 0;
s.next_word = next_word_value;
CHECK_EQU(Relaxed_AtomicIncrement(&s.count, 1), 1);
CHECK_EQU(s.count, 1);
CHECK_EQU(s.prev_word, prev_word_value);
CHECK_EQU(s.next_word, next_word_value);
CHECK_EQU(Relaxed_AtomicIncrement(&s.count, 2), 3);
CHECK_EQU(s.count, 3);
CHECK_EQU(s.prev_word, prev_word_value);
CHECK_EQU(s.next_word, next_word_value);
CHECK_EQU(Relaxed_AtomicIncrement(&s.count, 3), 6);
CHECK_EQU(s.count, 6);
CHECK_EQU(s.prev_word, prev_word_value);
CHECK_EQU(s.next_word, next_word_value);
CHECK_EQU(Relaxed_AtomicIncrement(&s.count, -3), 3);
CHECK_EQU(s.count, 3);
CHECK_EQU(s.prev_word, prev_word_value);
CHECK_EQU(s.next_word, next_word_value);
CHECK_EQU(Relaxed_AtomicIncrement(&s.count, -2), 1);
CHECK_EQU(s.count, 1);
CHECK_EQU(s.prev_word, prev_word_value);
CHECK_EQU(s.next_word, next_word_value);
CHECK_EQU(Relaxed_AtomicIncrement(&s.count, -1), 0);
CHECK_EQU(s.count, 0);
CHECK_EQU(s.prev_word, prev_word_value);
CHECK_EQU(s.next_word, next_word_value);
CHECK_EQU(Relaxed_AtomicIncrement(&s.count, -1), -1);
CHECK_EQU(s.count, -1);
CHECK_EQU(s.prev_word, prev_word_value);
CHECK_EQU(s.next_word, next_word_value);
CHECK_EQU(Relaxed_AtomicIncrement(&s.count, -4), -5);
CHECK_EQU(s.count, -5);
CHECK_EQU(s.prev_word, prev_word_value);
CHECK_EQU(s.next_word, next_word_value);
CHECK_EQU(Relaxed_AtomicIncrement(&s.count, 5), 0);
CHECK_EQU(s.count, 0);
CHECK_EQU(s.prev_word, prev_word_value);
CHECK_EQU(s.next_word, next_word_value);
}
template <class AtomicType>
static void TestCompareAndSwap() {
AtomicType value = 0;
AtomicType prev = Relaxed_CompareAndSwap(&value, 0, 1);
CHECK_EQU(1, value);
CHECK_EQU(0, prev);
// Use a test value that has non-zero bits in both halves, for testing
// the 64-bit implementation on 32-bit platforms.
const AtomicType k_test_val =
(static_cast<AtomicType>(1) << (NUM_BITS(AtomicType) - 2)) + 11;
value = k_test_val;
prev = Relaxed_CompareAndSwap(&value, 0, 5);
CHECK_EQU(k_test_val, value);
CHECK_EQU(k_test_val, prev);
value = k_test_val;
prev = Relaxed_CompareAndSwap(&value, k_test_val, 5);
CHECK_EQU(5, value);
CHECK_EQU(k_test_val, prev);
}
template <class AtomicType>
static void TestAtomicExchange() {
AtomicType value = 0;
AtomicType new_value = Relaxed_AtomicExchange(&value, 1);
CHECK_EQU(1, value);
CHECK_EQU(0, new_value);
// Use a test value that has non-zero bits in both halves, for testing
// the 64-bit implementation on 32-bit platforms.
const AtomicType k_test_val =
(static_cast<AtomicType>(1) << (NUM_BITS(AtomicType) - 2)) + 11;
value = k_test_val;
new_value = Relaxed_AtomicExchange(&value, k_test_val);
CHECK_EQU(k_test_val, value);
CHECK_EQU(k_test_val, new_value);
value = k_test_val;
new_value = Relaxed_AtomicExchange(&value, 5);
CHECK_EQU(5, value);
CHECK_EQU(k_test_val, new_value);
}
template <class AtomicType>
static void TestAtomicIncrementBounds() {
// Test at 32-bit boundary for 64-bit atomic type.
AtomicType test_val = static_cast<AtomicType>(1)
<< (NUM_BITS(AtomicType) / 2);
AtomicType value = test_val - 1;
AtomicType new_value = Relaxed_AtomicIncrement(&value, 1);
CHECK_EQU(test_val, value);
CHECK_EQU(value, new_value);
Relaxed_AtomicIncrement(&value, -1);
CHECK_EQU(test_val - 1, value);
}
// Return an AtomicType with the value 0xA5A5A5..
template <class AtomicType>
static AtomicType TestFillValue() {
AtomicType val = 0;
memset(&val, 0xA5, sizeof(AtomicType));
return val;
}
// This is a simple sanity check to ensure that values are correct.
// Not testing atomicity.
template <class AtomicType>
static void TestStore() {
const AtomicType kVal1 = TestFillValue<AtomicType>();
const AtomicType kVal2 = static_cast<AtomicType>(-1);
AtomicType value;
Relaxed_Store(&value, kVal1);
CHECK_EQU(kVal1, value);
Relaxed_Store(&value, kVal2);
CHECK_EQU(kVal2, value);
Release_Store(&value, kVal1);
CHECK_EQU(kVal1, value);
Release_Store(&value, kVal2);
CHECK_EQU(kVal2, value);
}
// Merge this test with TestStore as soon as we have Atomic8 acquire
// and release stores.
static void TestStoreAtomic8() {
const Atomic8 kVal1 = TestFillValue<Atomic8>();
const Atomic8 kVal2 = static_cast<Atomic8>(-1);
Atomic8 value;
Relaxed_Store(&value, kVal1);
CHECK_EQU(kVal1, value);
Relaxed_Store(&value, kVal2);
CHECK_EQU(kVal2, value);
}
// This is a simple sanity check to ensure that values are correct.
// Not testing atomicity.
template <class AtomicType>
static void TestLoad() {
const AtomicType kVal1 = TestFillValue<AtomicType>();
const AtomicType kVal2 = static_cast<AtomicType>(-1);
AtomicType value;
value = kVal1;
CHECK_EQU(kVal1, Relaxed_Load(&value));
value = kVal2;
CHECK_EQU(kVal2, Relaxed_Load(&value));
value = kVal1;
CHECK_EQU(kVal1, Acquire_Load(&value));
value = kVal2;
CHECK_EQU(kVal2, Acquire_Load(&value));
}
// Merge this test with TestLoad as soon as we have Atomic8 acquire
// and release loads.
static void TestLoadAtomic8() {
const Atomic8 kVal1 = TestFillValue<Atomic8>();
const Atomic8 kVal2 = static_cast<Atomic8>(-1);
Atomic8 value;
value = kVal1;
CHECK_EQU(kVal1, Relaxed_Load(&value));
value = kVal2;
CHECK_EQU(kVal2, Relaxed_Load(&value));
}
TEST(Atomicops, AtomicIncrement) {
TestAtomicIncrement<Atomic32>();
TestAtomicIncrement<AtomicWord>();
}
TEST(Atomicops, CompareAndSwap) {
TestCompareAndSwap<Atomic32>();
TestCompareAndSwap<AtomicWord>();
}
TEST(Atomicops, AtomicExchange) {
TestAtomicExchange<Atomic32>();
TestAtomicExchange<AtomicWord>();
}
TEST(Atomicops, AtomicIncrementBounds) {
TestAtomicIncrementBounds<Atomic32>();
TestAtomicIncrementBounds<AtomicWord>();
}
TEST(Atomicops, Store) {
TestStoreAtomic8();
TestStore<Atomic32>();
TestStore<AtomicWord>();
}
TEST(Atomicops, Load) {
TestLoadAtomic8();
TestLoad<Atomic32>();
TestLoad<AtomicWord>();
}
TEST(Atomicops, Relaxed_Memmove) {
constexpr size_t kLen = 6;
Atomic8 arr[kLen];
{
for (size_t i = 0; i < kLen; ++i) arr[i] = i;
Relaxed_Memmove(arr + 2, arr + 3, 2);
uint8_t expected[]{0, 1, 3, 4, 4, 5};
for (size_t i = 0; i < kLen; ++i) CHECK_EQ(arr[i], expected[i]);
}
{
for (size_t i = 0; i < kLen; ++i) arr[i] = i;
Relaxed_Memmove(arr + 3, arr + 2, 2);
uint8_t expected[]{0, 1, 2, 2, 3, 5};
for (size_t i = 0; i < kLen; ++i) CHECK_EQ(arr[i], expected[i]);
}
}
TEST(Atomicops, Relaxed_Memcmp) {
constexpr size_t kLen = 50;
Atomic8 arr1[kLen];
Atomic8 arr1_same[kLen];
Atomic8 arr2[kLen];
for (size_t i = 0; i < kLen; ++i) {
arr1[i] = arr1_same[i] = i;
arr2[i] = i + 1;
}
for (size_t offset = 0; offset < kLen; offset++) {
const Atomic8* arr1p = arr1 + offset;
const Atomic8* arr1_samep = arr1_same + offset;
const Atomic8* arr2p = arr2 + offset;
const size_t len = kLen - offset;
CHECK_EQ(0, Relaxed_Memcmp(arr1p, arr1p, len));
CHECK_EQ(0, Relaxed_Memcmp(arr1p, arr1_samep, len));
CHECK_LT(Relaxed_Memcmp(arr1p, arr2p, len), 0);
CHECK_GT(Relaxed_Memcmp(arr2p, arr1p, len), 0);
}
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,307 @@
// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "src/base/numbers/bignum-dtoa.h"
#include <stdlib.h>
#include "src/base/numbers/double.h"
#include "test/unittests/gay-fixed.h"
#include "test/unittests/gay-precision.h"
#include "test/unittests/gay-shortest.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
using BignumDtoaTest = ::testing::Test;
namespace base {
namespace test_bignum_dtoa {
// Removes trailing '0' digits (modifies {representation}). Can create an empty
// string if all digits are 0.
static void TrimRepresentation(char* representation) {
size_t len = strlen(representation);
while (len > 0 && representation[len - 1] == '0') --len;
representation[len] = '\0';
}
static const int kBufferSize = 100;
TEST_F(BignumDtoaTest, BignumDtoaVariousDoubles) {
char buffer_container[kBufferSize];
Vector<char> buffer(buffer_container, kBufferSize);
int length;
int point;
BignumDtoa(1.0, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point);
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
BignumDtoa(1.0, BIGNUM_DTOA_FIXED, 3, buffer, &length, &point);
CHECK_GE(3, length - point);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
BignumDtoa(1.0, BIGNUM_DTOA_PRECISION, 3, buffer, &length, &point);
CHECK_GE(3, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
BignumDtoa(1.5, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point);
CHECK_EQ(0, strcmp("15", buffer.begin()));
CHECK_EQ(1, point);
BignumDtoa(1.5, BIGNUM_DTOA_FIXED, 10, buffer, &length, &point);
CHECK_GE(10, length - point);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("15", buffer.begin()));
CHECK_EQ(1, point);
BignumDtoa(1.5, BIGNUM_DTOA_PRECISION, 10, buffer, &length, &point);
CHECK_GE(10, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("15", buffer.begin()));
CHECK_EQ(1, point);
double min_double = 5e-324;
BignumDtoa(min_double, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point);
CHECK_EQ(0, strcmp("5", buffer.begin()));
CHECK_EQ(-323, point);
BignumDtoa(min_double, BIGNUM_DTOA_FIXED, 5, buffer, &length, &point);
CHECK_GE(5, length - point);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("", buffer.begin()));
BignumDtoa(min_double, BIGNUM_DTOA_PRECISION, 5, buffer, &length, &point);
CHECK_GE(5, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("49407", buffer.begin()));
CHECK_EQ(-323, point);
double max_double = 1.7976931348623157e308;
BignumDtoa(max_double, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point);
CHECK_EQ(0, strcmp("17976931348623157", buffer.begin()));
CHECK_EQ(309, point);
BignumDtoa(max_double, BIGNUM_DTOA_PRECISION, 7, buffer, &length, &point);
CHECK_GE(7, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("1797693", buffer.begin()));
CHECK_EQ(309, point);
BignumDtoa(4294967272.0, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point);
CHECK_EQ(0, strcmp("4294967272", buffer.begin()));
CHECK_EQ(10, point);
BignumDtoa(4294967272.0, BIGNUM_DTOA_FIXED, 5, buffer, &length, &point);
CHECK_EQ(0, strcmp("429496727200000", buffer.begin()));
CHECK_EQ(10, point);
BignumDtoa(4294967272.0, BIGNUM_DTOA_PRECISION, 14, buffer, &length, &point);
CHECK_GE(14, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("4294967272", buffer.begin()));
CHECK_EQ(10, point);
BignumDtoa(4.1855804968213567e298, BIGNUM_DTOA_SHORTEST, 0, buffer, &length,
&point);
CHECK_EQ(0, strcmp("4185580496821357", buffer.begin()));
CHECK_EQ(299, point);
BignumDtoa(4.1855804968213567e298, BIGNUM_DTOA_PRECISION, 20, buffer, &length,
&point);
CHECK_GE(20, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("41855804968213567225", buffer.begin()));
CHECK_EQ(299, point);
BignumDtoa(5.5626846462680035e-309, BIGNUM_DTOA_SHORTEST, 0, buffer, &length,
&point);
CHECK_EQ(0, strcmp("5562684646268003", buffer.begin()));
CHECK_EQ(-308, point);
BignumDtoa(5.5626846462680035e-309, BIGNUM_DTOA_PRECISION, 1, buffer, &length,
&point);
CHECK_GE(1, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("6", buffer.begin()));
CHECK_EQ(-308, point);
BignumDtoa(2147483648.0, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point);
CHECK_EQ(0, strcmp("2147483648", buffer.begin()));
CHECK_EQ(10, point);
BignumDtoa(2147483648.0, BIGNUM_DTOA_FIXED, 2, buffer, &length, &point);
CHECK_GE(2, length - point);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("2147483648", buffer.begin()));
CHECK_EQ(10, point);
BignumDtoa(2147483648.0, BIGNUM_DTOA_PRECISION, 5, buffer, &length, &point);
CHECK_GE(5, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("21475", buffer.begin()));
CHECK_EQ(10, point);
BignumDtoa(3.5844466002796428e+298, BIGNUM_DTOA_SHORTEST, 0, buffer, &length,
&point);
CHECK_EQ(0, strcmp("35844466002796428", buffer.begin()));
CHECK_EQ(299, point);
BignumDtoa(3.5844466002796428e+298, BIGNUM_DTOA_PRECISION, 10, buffer,
&length, &point);
CHECK_GE(10, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("35844466", buffer.begin()));
CHECK_EQ(299, point);
uint64_t smallest_normal64 = 0x0010'0000'0000'0000;
double v = Double(smallest_normal64).value();
BignumDtoa(v, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point);
CHECK_EQ(0, strcmp("22250738585072014", buffer.begin()));
CHECK_EQ(-307, point);
BignumDtoa(v, BIGNUM_DTOA_PRECISION, 20, buffer, &length, &point);
CHECK_GE(20, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("22250738585072013831", buffer.begin()));
CHECK_EQ(-307, point);
uint64_t largest_denormal64 = 0x000F'FFFF'FFFF'FFFF;
v = Double(largest_denormal64).value();
BignumDtoa(v, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point);
CHECK_EQ(0, strcmp("2225073858507201", buffer.begin()));
CHECK_EQ(-307, point);
BignumDtoa(v, BIGNUM_DTOA_PRECISION, 20, buffer, &length, &point);
CHECK_GE(20, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("2225073858507200889", buffer.begin()));
CHECK_EQ(-307, point);
BignumDtoa(4128420500802942e-24, BIGNUM_DTOA_SHORTEST, 0, buffer, &length,
&point);
CHECK_EQ(0, strcmp("4128420500802942", buffer.begin()));
CHECK_EQ(-8, point);
v = 3.9292015898194142585311918e-10;
BignumDtoa(v, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point);
CHECK_EQ(0, strcmp("39292015898194143", buffer.begin()));
v = 4194304.0;
BignumDtoa(v, BIGNUM_DTOA_FIXED, 5, buffer, &length, &point);
CHECK_GE(5, length - point);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("4194304", buffer.begin()));
v = 3.3161339052167390562200598e-237;
BignumDtoa(v, BIGNUM_DTOA_PRECISION, 19, buffer, &length, &point);
CHECK_GE(19, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("3316133905216739056", buffer.begin()));
CHECK_EQ(-236, point);
v = 7.9885183916008099497815232e+191;
BignumDtoa(v, BIGNUM_DTOA_PRECISION, 4, buffer, &length, &point);
CHECK_GE(4, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("7989", buffer.begin()));
CHECK_EQ(192, point);
v = 1.0000000000000012800000000e+17;
BignumDtoa(v, BIGNUM_DTOA_FIXED, 1, buffer, &length, &point);
CHECK_GE(1, length - point);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("100000000000000128", buffer.begin()));
CHECK_EQ(18, point);
}
TEST_F(BignumDtoaTest, BignumDtoaGayShortest) {
char buffer_container[kBufferSize];
Vector<char> buffer(buffer_container, kBufferSize);
int length;
int point;
Vector<const PrecomputedShortest> precomputed =
PrecomputedShortestRepresentations();
for (int i = 0; i < precomputed.length(); ++i) {
const PrecomputedShortest current_test = precomputed[i];
double v = current_test.v;
BignumDtoa(v, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point);
CHECK_EQ(current_test.decimal_point, point);
CHECK_EQ(0, strcmp(current_test.representation, buffer.begin()));
}
}
TEST_F(BignumDtoaTest, BignumDtoaGayFixed) {
char buffer_container[kBufferSize];
Vector<char> buffer(buffer_container, kBufferSize);
int length;
int point;
Vector<const PrecomputedFixed> precomputed =
PrecomputedFixedRepresentations();
for (int i = 0; i < precomputed.length(); ++i) {
const PrecomputedFixed current_test = precomputed[i];
double v = current_test.v;
int number_digits = current_test.number_digits;
BignumDtoa(v, BIGNUM_DTOA_FIXED, number_digits, buffer, &length, &point);
CHECK_EQ(current_test.decimal_point, point);
CHECK_GE(number_digits, length - point);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp(current_test.representation, buffer.begin()));
}
}
TEST_F(BignumDtoaTest, BignumDtoaGayPrecision) {
char buffer_container[kBufferSize];
Vector<char> buffer(buffer_container, kBufferSize);
int length;
int point;
Vector<const PrecomputedPrecision> precomputed =
PrecomputedPrecisionRepresentations();
for (int i = 0; i < precomputed.length(); ++i) {
const PrecomputedPrecision current_test = precomputed[i];
double v = current_test.v;
int number_digits = current_test.number_digits;
BignumDtoa(v, BIGNUM_DTOA_PRECISION, number_digits, buffer, &length,
&point);
CHECK_EQ(current_test.decimal_point, point);
CHECK_GE(number_digits, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp(current_test.representation, buffer.begin()));
}
}
} // namespace test_bignum_dtoa
} // namespace base
} // namespace v8

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,375 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/bits.h"
#include <limits>
#include "testing/gtest-support.h"
#ifdef DEBUG
#define DISABLE_IN_RELEASE(Name) Name
#else
#define DISABLE_IN_RELEASE(Name) DISABLED_##Name
#endif
namespace v8 {
namespace base {
namespace bits {
TEST(Bits, CountPopulation8) {
EXPECT_EQ(0u, CountPopulation(uint8_t{0}));
EXPECT_EQ(1u, CountPopulation(uint8_t{1}));
EXPECT_EQ(2u, CountPopulation(uint8_t{0x11}));
EXPECT_EQ(4u, CountPopulation(uint8_t{0x0F}));
EXPECT_EQ(6u, CountPopulation(uint8_t{0x3F}));
EXPECT_EQ(8u, CountPopulation(uint8_t{0xFF}));
}
TEST(Bits, CountPopulation16) {
EXPECT_EQ(0u, CountPopulation(uint16_t{0}));
EXPECT_EQ(1u, CountPopulation(uint16_t{1}));
EXPECT_EQ(4u, CountPopulation(uint16_t{0x1111}));
EXPECT_EQ(8u, CountPopulation(uint16_t{0xF0F0}));
EXPECT_EQ(12u, CountPopulation(uint16_t{0xF0FF}));
EXPECT_EQ(16u, CountPopulation(uint16_t{0xFFFF}));
}
TEST(Bits, CountPopulation32) {
EXPECT_EQ(0u, CountPopulation(uint32_t{0}));
EXPECT_EQ(1u, CountPopulation(uint32_t{1}));
EXPECT_EQ(8u, CountPopulation(uint32_t{0x11111111}));
EXPECT_EQ(16u, CountPopulation(uint32_t{0xF0F0F0F0}));
EXPECT_EQ(24u, CountPopulation(uint32_t{0xFFF0F0FF}));
EXPECT_EQ(32u, CountPopulation(uint32_t{0xFFFFFFFF}));
}
TEST(Bits, CountPopulation64) {
EXPECT_EQ(0u, CountPopulation(uint64_t{0}));
EXPECT_EQ(1u, CountPopulation(uint64_t{1}));
EXPECT_EQ(2u, CountPopulation(uint64_t{0x8000000000000001}));
EXPECT_EQ(8u, CountPopulation(uint64_t{0x11111111}));
EXPECT_EQ(16u, CountPopulation(uint64_t{0xF0F0F0F0}));
EXPECT_EQ(24u, CountPopulation(uint64_t{0xFFF0F0FF}));
EXPECT_EQ(32u, CountPopulation(uint64_t{0xFFFFFFFF}));
EXPECT_EQ(16u, CountPopulation(uint64_t{0x1111111111111111}));
EXPECT_EQ(32u, CountPopulation(uint64_t{0xF0F0F0F0F0F0F0F0}));
EXPECT_EQ(48u, CountPopulation(uint64_t{0xFFF0F0FFFFF0F0FF}));
EXPECT_EQ(64u, CountPopulation(uint64_t{0xFFFFFFFFFFFFFFFF}));
}
TEST(Bits, CountLeadingZeros16) {
EXPECT_EQ(16u, CountLeadingZeros(uint16_t{0}));
EXPECT_EQ(15u, CountLeadingZeros(uint16_t{1}));
TRACED_FORRANGE(uint16_t, shift, 0, 15) {
EXPECT_EQ(15u - shift,
CountLeadingZeros(static_cast<uint16_t>(1 << shift)));
}
EXPECT_EQ(4u, CountLeadingZeros(uint16_t{0x0F0F}));
}
TEST(Bits, CountLeadingZeros32) {
EXPECT_EQ(32u, CountLeadingZeros(uint32_t{0}));
EXPECT_EQ(31u, CountLeadingZeros(uint32_t{1}));
TRACED_FORRANGE(uint32_t, shift, 0, 31) {
EXPECT_EQ(31u - shift, CountLeadingZeros(uint32_t{1} << shift));
}
EXPECT_EQ(4u, CountLeadingZeros(uint32_t{0x0F0F0F0F}));
}
TEST(Bits, CountLeadingZeros64) {
EXPECT_EQ(64u, CountLeadingZeros(uint64_t{0}));
EXPECT_EQ(63u, CountLeadingZeros(uint64_t{1}));
TRACED_FORRANGE(uint32_t, shift, 0, 63) {
EXPECT_EQ(63u - shift, CountLeadingZeros(uint64_t{1} << shift));
}
EXPECT_EQ(36u, CountLeadingZeros(uint64_t{0x0F0F0F0F}));
EXPECT_EQ(4u, CountLeadingZeros(uint64_t{0x0F0F0F0F00000000}));
}
TEST(Bits, CountTrailingZeros16) {
EXPECT_EQ(16u, CountTrailingZeros(uint16_t{0}));
EXPECT_EQ(15u, CountTrailingZeros(uint16_t{0x8000}));
TRACED_FORRANGE(uint16_t, shift, 0, 15) {
EXPECT_EQ(shift, CountTrailingZeros(static_cast<uint16_t>(1 << shift)));
}
EXPECT_EQ(4u, CountTrailingZeros(uint16_t{0xF0F0u}));
}
TEST(Bits, CountTrailingZerosu32) {
EXPECT_EQ(32u, CountTrailingZeros(uint32_t{0}));
EXPECT_EQ(31u, CountTrailingZeros(uint32_t{0x80000000}));
TRACED_FORRANGE(uint32_t, shift, 0, 31) {
EXPECT_EQ(shift, CountTrailingZeros(uint32_t{1} << shift));
}
EXPECT_EQ(4u, CountTrailingZeros(uint32_t{0xF0F0F0F0u}));
}
TEST(Bits, CountTrailingZerosi32) {
EXPECT_EQ(32u, CountTrailingZeros(int32_t{0}));
TRACED_FORRANGE(uint32_t, shift, 0, 31) {
EXPECT_EQ(shift, CountTrailingZeros(int32_t{1} << shift));
}
EXPECT_EQ(4u, CountTrailingZeros(int32_t{0x70F0F0F0u}));
EXPECT_EQ(2u, CountTrailingZeros(int32_t{-4}));
EXPECT_EQ(0u, CountTrailingZeros(int32_t{-1}));
}
TEST(Bits, CountTrailingZeros64) {
EXPECT_EQ(64u, CountTrailingZeros(uint64_t{0}));
EXPECT_EQ(63u, CountTrailingZeros(uint64_t{0x8000000000000000}));
TRACED_FORRANGE(uint32_t, shift, 0, 63) {
EXPECT_EQ(shift, CountTrailingZeros(uint64_t{1} << shift));
}
EXPECT_EQ(4u, CountTrailingZeros(uint64_t{0xF0F0F0F0}));
EXPECT_EQ(36u, CountTrailingZeros(uint64_t{0xF0F0F0F000000000}));
}
TEST(Bits, IsPowerOfTwo32) {
EXPECT_FALSE(IsPowerOfTwo(0U));
TRACED_FORRANGE(uint32_t, shift, 0, 31) {
EXPECT_TRUE(IsPowerOfTwo(1U << shift));
EXPECT_FALSE(IsPowerOfTwo((1U << shift) + 5U));
EXPECT_FALSE(IsPowerOfTwo(~(1U << shift)));
}
TRACED_FORRANGE(uint32_t, shift, 2, 31) {
EXPECT_FALSE(IsPowerOfTwo((1U << shift) - 1U));
}
EXPECT_FALSE(IsPowerOfTwo(0xFFFFFFFF));
}
TEST(Bits, IsPowerOfTwo64) {
EXPECT_FALSE(IsPowerOfTwo(uint64_t{0}));
TRACED_FORRANGE(uint32_t, shift, 0, 63) {
EXPECT_TRUE(IsPowerOfTwo(uint64_t{1} << shift));
EXPECT_FALSE(IsPowerOfTwo((uint64_t{1} << shift) + 5U));
EXPECT_FALSE(IsPowerOfTwo(~(uint64_t{1} << shift)));
}
TRACED_FORRANGE(uint32_t, shift, 2, 63) {
EXPECT_FALSE(IsPowerOfTwo((uint64_t{1} << shift) - 1U));
}
EXPECT_FALSE(IsPowerOfTwo(uint64_t{0xFFFFFFFFFFFFFFFF}));
}
TEST(Bits, WhichPowerOfTwo32) {
TRACED_FORRANGE(int, shift, 0, 30) {
EXPECT_EQ(shift, WhichPowerOfTwo(int32_t{1} << shift));
}
TRACED_FORRANGE(int, shift, 0, 31) {
EXPECT_EQ(shift, WhichPowerOfTwo(uint32_t{1} << shift));
}
}
TEST(Bits, WhichPowerOfTwo64) {
TRACED_FORRANGE(int, shift, 0, 62) {
EXPECT_EQ(shift, WhichPowerOfTwo(int64_t{1} << shift));
}
TRACED_FORRANGE(int, shift, 0, 63) {
EXPECT_EQ(shift, WhichPowerOfTwo(uint64_t{1} << shift));
}
}
TEST(Bits, RoundUpToPowerOfTwo32) {
TRACED_FORRANGE(uint32_t, shift, 0, 31) {
EXPECT_EQ(1u << shift, RoundUpToPowerOfTwo32(1u << shift));
}
EXPECT_EQ(1u, RoundUpToPowerOfTwo32(0));
EXPECT_EQ(1u, RoundUpToPowerOfTwo32(1));
EXPECT_EQ(4u, RoundUpToPowerOfTwo32(3));
EXPECT_EQ(0x80000000u, RoundUpToPowerOfTwo32(0x7FFFFFFFu));
}
TEST(BitsDeathTest, DISABLE_IN_RELEASE(RoundUpToPowerOfTwo32)) {
ASSERT_DEATH_IF_SUPPORTED({ RoundUpToPowerOfTwo32(0x80000001u); },
".*heck failed:.* << 31");
}
TEST(Bits, RoundUpToPowerOfTwo64) {
TRACED_FORRANGE(uint64_t, shift, 0, 63) {
uint64_t value = uint64_t{1} << shift;
EXPECT_EQ(value, RoundUpToPowerOfTwo64(value));
}
EXPECT_EQ(uint64_t{1}, RoundUpToPowerOfTwo64(0));
EXPECT_EQ(uint64_t{1}, RoundUpToPowerOfTwo64(1));
EXPECT_EQ(uint64_t{4}, RoundUpToPowerOfTwo64(3));
EXPECT_EQ(uint64_t{1} << 63, RoundUpToPowerOfTwo64((uint64_t{1} << 63) - 1));
EXPECT_EQ(uint64_t{1} << 63, RoundUpToPowerOfTwo64(uint64_t{1} << 63));
}
TEST(BitsDeathTest, DISABLE_IN_RELEASE(RoundUpToPowerOfTwo64)) {
ASSERT_DEATH_IF_SUPPORTED({ RoundUpToPowerOfTwo64((uint64_t{1} << 63) + 1); },
".*heck failed:.* << 63");
}
TEST(Bits, RoundDownToPowerOfTwo32) {
TRACED_FORRANGE(uint32_t, shift, 0, 31) {
EXPECT_EQ(1u << shift, RoundDownToPowerOfTwo32(1u << shift));
}
EXPECT_EQ(0u, RoundDownToPowerOfTwo32(0));
EXPECT_EQ(4u, RoundDownToPowerOfTwo32(5));
EXPECT_EQ(0x80000000u, RoundDownToPowerOfTwo32(0x80000001u));
}
TEST(Bits, RotateRight32) {
TRACED_FORRANGE(uint32_t, shift, 0, 31) {
EXPECT_EQ(0u, RotateRight32(0u, shift));
}
EXPECT_EQ(1u, RotateRight32(1, 0));
EXPECT_EQ(1u, RotateRight32(2, 1));
EXPECT_EQ(0x80000000u, RotateRight32(1, 1));
}
TEST(Bits, RotateRight64) {
TRACED_FORRANGE(uint64_t, shift, 0, 63) {
EXPECT_EQ(0u, RotateRight64(0u, shift));
}
EXPECT_EQ(1u, RotateRight64(1, 0));
EXPECT_EQ(1u, RotateRight64(2, 1));
EXPECT_EQ(uint64_t{0x8000000000000000}, RotateRight64(1, 1));
}
TEST(Bits, SignedAddOverflow32) {
int32_t val = 0;
EXPECT_FALSE(SignedAddOverflow32(0, 0, &val));
EXPECT_EQ(0, val);
EXPECT_TRUE(
SignedAddOverflow32(std::numeric_limits<int32_t>::max(), 1, &val));
EXPECT_EQ(std::numeric_limits<int32_t>::min(), val);
EXPECT_TRUE(
SignedAddOverflow32(std::numeric_limits<int32_t>::min(), -1, &val));
EXPECT_EQ(std::numeric_limits<int32_t>::max(), val);
EXPECT_TRUE(SignedAddOverflow32(std::numeric_limits<int32_t>::max(),
std::numeric_limits<int32_t>::max(), &val));
EXPECT_EQ(-2, val);
TRACED_FORRANGE(int32_t, i, 1, 50) {
TRACED_FORRANGE(int32_t, j, 1, i) {
EXPECT_FALSE(SignedAddOverflow32(i, j, &val));
EXPECT_EQ(i + j, val);
}
}
}
TEST(Bits, SignedSubOverflow32) {
int32_t val = 0;
EXPECT_FALSE(SignedSubOverflow32(0, 0, &val));
EXPECT_EQ(0, val);
EXPECT_TRUE(
SignedSubOverflow32(std::numeric_limits<int32_t>::min(), 1, &val));
EXPECT_EQ(std::numeric_limits<int32_t>::max(), val);
EXPECT_TRUE(
SignedSubOverflow32(std::numeric_limits<int32_t>::max(), -1, &val));
EXPECT_EQ(std::numeric_limits<int32_t>::min(), val);
TRACED_FORRANGE(int32_t, i, 1, 50) {
TRACED_FORRANGE(int32_t, j, 1, i) {
EXPECT_FALSE(SignedSubOverflow32(i, j, &val));
EXPECT_EQ(i - j, val);
}
}
}
TEST(Bits, SignedMulHigh32) {
EXPECT_EQ(0, SignedMulHigh32(0, 0));
TRACED_FORRANGE(int32_t, i, 1, 50) {
TRACED_FORRANGE(int32_t, j, 1, i) { EXPECT_EQ(0, SignedMulHigh32(i, j)); }
}
EXPECT_EQ(-1073741824, SignedMulHigh32(std::numeric_limits<int32_t>::max(),
std::numeric_limits<int32_t>::min()));
EXPECT_EQ(-1073741824, SignedMulHigh32(std::numeric_limits<int32_t>::min(),
std::numeric_limits<int32_t>::max()));
EXPECT_EQ(1, SignedMulHigh32(1024 * 1024 * 1024, 4));
EXPECT_EQ(2, SignedMulHigh32(8 * 1024, 1024 * 1024));
}
TEST(Bits, SignedMulHighAndAdd32) {
TRACED_FORRANGE(int32_t, i, 1, 50) {
EXPECT_EQ(i, SignedMulHighAndAdd32(0, 0, i));
TRACED_FORRANGE(int32_t, j, 1, i) {
EXPECT_EQ(i, SignedMulHighAndAdd32(j, j, i));
}
EXPECT_EQ(i + 1, SignedMulHighAndAdd32(1024 * 1024 * 1024, 4, i));
}
}
TEST(Bits, SignedDiv32) {
EXPECT_EQ(std::numeric_limits<int32_t>::min(),
SignedDiv32(std::numeric_limits<int32_t>::min(), -1));
EXPECT_EQ(std::numeric_limits<int32_t>::max(),
SignedDiv32(std::numeric_limits<int32_t>::max(), 1));
TRACED_FORRANGE(int32_t, i, 0, 50) {
EXPECT_EQ(0, SignedDiv32(i, 0));
TRACED_FORRANGE(int32_t, j, 1, i) {
EXPECT_EQ(1, SignedDiv32(j, j));
EXPECT_EQ(i / j, SignedDiv32(i, j));
EXPECT_EQ(-i / j, SignedDiv32(i, -j));
}
}
}
TEST(Bits, SignedMod32) {
EXPECT_EQ(0, SignedMod32(std::numeric_limits<int32_t>::min(), -1));
EXPECT_EQ(0, SignedMod32(std::numeric_limits<int32_t>::max(), 1));
TRACED_FORRANGE(int32_t, i, 0, 50) {
EXPECT_EQ(0, SignedMod32(i, 0));
TRACED_FORRANGE(int32_t, j, 1, i) {
EXPECT_EQ(0, SignedMod32(j, j));
EXPECT_EQ(i % j, SignedMod32(i, j));
EXPECT_EQ(i % j, SignedMod32(i, -j));
}
}
}
TEST(Bits, UnsignedAddOverflow32) {
uint32_t val = 0;
EXPECT_FALSE(UnsignedAddOverflow32(0, 0, &val));
EXPECT_EQ(0u, val);
EXPECT_TRUE(
UnsignedAddOverflow32(std::numeric_limits<uint32_t>::max(), 1u, &val));
EXPECT_EQ(std::numeric_limits<uint32_t>::min(), val);
EXPECT_TRUE(UnsignedAddOverflow32(std::numeric_limits<uint32_t>::max(),
std::numeric_limits<uint32_t>::max(),
&val));
TRACED_FORRANGE(uint32_t, i, 1, 50) {
TRACED_FORRANGE(uint32_t, j, 1, i) {
EXPECT_FALSE(UnsignedAddOverflow32(i, j, &val));
EXPECT_EQ(i + j, val);
}
}
}
TEST(Bits, UnsignedDiv32) {
TRACED_FORRANGE(uint32_t, i, 0, 50) {
EXPECT_EQ(0u, UnsignedDiv32(i, 0));
TRACED_FORRANGE(uint32_t, j, i + 1, 100) {
EXPECT_EQ(1u, UnsignedDiv32(j, j));
EXPECT_EQ(i / j, UnsignedDiv32(i, j));
}
}
}
TEST(Bits, UnsignedMod32) {
TRACED_FORRANGE(uint32_t, i, 0, 50) {
EXPECT_EQ(0u, UnsignedMod32(i, 0));
TRACED_FORRANGE(uint32_t, j, i + 1, 100) {
EXPECT_EQ(0u, UnsignedMod32(j, j));
EXPECT_EQ(i % j, UnsignedMod32(i, j));
}
}
}
} // namespace bits
} // namespace base
} // namespace v8

View File

@ -0,0 +1,77 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/cpu.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "src/heap/base/memory-tagging.h"
namespace v8 {
namespace base {
#if defined(V8_HOST_ARCH_ARM64)
TEST(CPUTest, SuppressTagCheckingScope) {
CPU cpu;
if (!cpu.has_mte()) GTEST_SKIP();
// Read the current value of PSTATE.TCO (it should be zero).
uint64_t val;
asm volatile(".arch_extension memtag \n mrs %0, tco" : "=r" (val));
EXPECT_EQ(val, 0u);
// Create a scope where MTE tag checks are temporarily suspended.
{
heap::base::SuspendTagCheckingScope s;
asm volatile(".arch_extension memtag \n mrs %0, tco" : "=r" (val));
EXPECT_EQ(val, 1u << 25);
}
// Check that the scope restores TCO afterwards.
asm volatile(".arch_extension memtag \n mrs %0, tco" : "=r" (val));
EXPECT_EQ(val, 0u);
}
#endif
TEST(CPUTest, FeatureImplications) {
CPU cpu;
// ia32 and x64 features
EXPECT_TRUE(!cpu.has_sse() || cpu.has_mmx());
EXPECT_TRUE(!cpu.has_sse2() || cpu.has_sse());
EXPECT_TRUE(!cpu.has_sse3() || cpu.has_sse2());
EXPECT_TRUE(!cpu.has_ssse3() || cpu.has_sse3());
EXPECT_TRUE(!cpu.has_sse41() || cpu.has_sse3());
EXPECT_TRUE(!cpu.has_sse42() || cpu.has_sse41());
EXPECT_TRUE(!cpu.has_avx() || cpu.has_sse2());
EXPECT_TRUE(!cpu.has_fma3() || cpu.has_avx());
EXPECT_TRUE(!cpu.has_avx2() || cpu.has_avx());
// arm features
EXPECT_TRUE(!cpu.has_vfp3_d32() || cpu.has_vfp3());
}
TEST(CPUTest, RequiredFeatures) {
CPU cpu;
#if V8_HOST_ARCH_ARM
EXPECT_TRUE(cpu.has_fpu());
#endif
#if V8_HOST_ARCH_IA32
EXPECT_TRUE(cpu.has_fpu());
EXPECT_TRUE(cpu.has_sahf());
#endif
#if V8_HOST_ARCH_X64
EXPECT_TRUE(cpu.has_fpu());
EXPECT_TRUE(cpu.has_cmov());
EXPECT_TRUE(cpu.has_mmx());
EXPECT_TRUE(cpu.has_sse());
EXPECT_TRUE(cpu.has_sse2());
#endif
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,133 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Check all examples from table 10-1 of "Hacker's Delight".
#include "src/base/division-by-constant.h"
#include <stdint.h>
#include <ostream>
#include "testing/gtest-support.h"
namespace v8 {
namespace base {
template <class T>
std::ostream& operator<<(std::ostream& os,
const MagicNumbersForDivision<T>& mag) {
return os << "{ multiplier: " << mag.multiplier << ", shift: " << mag.shift
<< ", add: " << mag.add << " }";
}
// Some abbreviations...
using M32 = MagicNumbersForDivision<uint32_t>;
using M64 = MagicNumbersForDivision<uint64_t>;
static M32 s32(int32_t d) {
return SignedDivisionByConstant<uint32_t>(static_cast<uint32_t>(d));
}
static M64 s64(int64_t d) {
return SignedDivisionByConstant<uint64_t>(static_cast<uint64_t>(d));
}
static M32 u32(uint32_t d) { return UnsignedDivisionByConstant<uint32_t>(d); }
static M64 u64(uint64_t d) { return UnsignedDivisionByConstant<uint64_t>(d); }
TEST(DivisionByConstant, Signed32) {
EXPECT_EQ(M32(0x99999999U, 1, false), s32(-5));
EXPECT_EQ(M32(0x55555555U, 1, false), s32(-3));
int32_t d = -1;
for (unsigned k = 1; k <= 32 - 1; ++k) {
d *= 2;
EXPECT_EQ(M32(0x7FFFFFFFU, k - 1, false), s32(d));
}
for (unsigned k = 1; k <= 32 - 2; ++k) {
EXPECT_EQ(M32(0x80000001U, k - 1, false), s32(1 << k));
}
EXPECT_EQ(M32(0x55555556U, 0, false), s32(3));
EXPECT_EQ(M32(0x66666667U, 1, false), s32(5));
EXPECT_EQ(M32(0x2AAAAAABU, 0, false), s32(6));
EXPECT_EQ(M32(0x92492493U, 2, false), s32(7));
EXPECT_EQ(M32(0x38E38E39U, 1, false), s32(9));
EXPECT_EQ(M32(0x66666667U, 2, false), s32(10));
EXPECT_EQ(M32(0x2E8BA2E9U, 1, false), s32(11));
EXPECT_EQ(M32(0x2AAAAAABU, 1, false), s32(12));
EXPECT_EQ(M32(0x51EB851FU, 3, false), s32(25));
EXPECT_EQ(M32(0x10624DD3U, 3, false), s32(125));
EXPECT_EQ(M32(0x68DB8BADU, 8, false), s32(625));
}
TEST(DivisionByConstant, Unsigned32) {
EXPECT_EQ(M32(0x00000000U, 0, true), u32(1));
for (unsigned k = 1; k <= 30; ++k) {
EXPECT_EQ(M32(1U << (32 - k), 0, false), u32(1U << k));
}
EXPECT_EQ(M32(0xAAAAAAABU, 1, false), u32(3));
EXPECT_EQ(M32(0xCCCCCCCDU, 2, false), u32(5));
EXPECT_EQ(M32(0xAAAAAAABU, 2, false), u32(6));
EXPECT_EQ(M32(0x24924925U, 3, true), u32(7));
EXPECT_EQ(M32(0x38E38E39U, 1, false), u32(9));
EXPECT_EQ(M32(0xCCCCCCCDU, 3, false), u32(10));
EXPECT_EQ(M32(0xBA2E8BA3U, 3, false), u32(11));
EXPECT_EQ(M32(0xAAAAAAABU, 3, false), u32(12));
EXPECT_EQ(M32(0x51EB851FU, 3, false), u32(25));
EXPECT_EQ(M32(0x10624DD3U, 3, false), u32(125));
EXPECT_EQ(M32(0xD1B71759U, 9, false), u32(625));
}
TEST(DivisionByConstant, Signed64) {
EXPECT_EQ(M64(0x9999999999999999ULL, 1, false), s64(-5));
EXPECT_EQ(M64(0x5555555555555555ULL, 1, false), s64(-3));
int64_t d = -1;
for (unsigned k = 1; k <= 64 - 1; ++k) {
d *= 2;
EXPECT_EQ(M64(0x7FFFFFFFFFFFFFFFULL, k - 1, false), s64(d));
}
for (unsigned k = 1; k <= 64 - 2; ++k) {
EXPECT_EQ(M64(0x8000000000000001ULL, k - 1, false), s64(1LL << k));
}
EXPECT_EQ(M64(0x5555555555555556ULL, 0, false), s64(3));
EXPECT_EQ(M64(0x6666666666666667ULL, 1, false), s64(5));
EXPECT_EQ(M64(0x2AAAAAAAAAAAAAABULL, 0, false), s64(6));
EXPECT_EQ(M64(0x4924924924924925ULL, 1, false), s64(7));
EXPECT_EQ(M64(0x1C71C71C71C71C72ULL, 0, false), s64(9));
EXPECT_EQ(M64(0x6666666666666667ULL, 2, false), s64(10));
EXPECT_EQ(M64(0x2E8BA2E8BA2E8BA3ULL, 1, false), s64(11));
EXPECT_EQ(M64(0x2AAAAAAAAAAAAAABULL, 1, false), s64(12));
EXPECT_EQ(M64(0xA3D70A3D70A3D70BULL, 4, false), s64(25));
EXPECT_EQ(M64(0x20C49BA5E353F7CFULL, 4, false), s64(125));
EXPECT_EQ(M64(0x346DC5D63886594BULL, 7, false), s64(625));
}
TEST(DivisionByConstant, Unsigned64) {
EXPECT_EQ(M64(0x0000000000000000ULL, 0, true), u64(1));
for (unsigned k = 1; k <= 64 - 2; ++k) {
EXPECT_EQ(M64(1ULL << (64 - k), 0, false), u64(1ULL << k));
}
EXPECT_EQ(M64(0xAAAAAAAAAAAAAAABULL, 1, false), u64(3));
EXPECT_EQ(M64(0xCCCCCCCCCCCCCCCDULL, 2, false), u64(5));
EXPECT_EQ(M64(0xAAAAAAAAAAAAAAABULL, 2, false), u64(6));
EXPECT_EQ(M64(0x2492492492492493ULL, 3, true), u64(7));
EXPECT_EQ(M64(0xE38E38E38E38E38FULL, 3, false), u64(9));
EXPECT_EQ(M64(0xCCCCCCCCCCCCCCCDULL, 3, false), u64(10));
EXPECT_EQ(M64(0x2E8BA2E8BA2E8BA3ULL, 1, false), u64(11));
EXPECT_EQ(M64(0xAAAAAAAAAAAAAAABULL, 3, false), u64(12));
EXPECT_EQ(M64(0x47AE147AE147AE15ULL, 5, true), u64(25));
EXPECT_EQ(M64(0x0624DD2F1A9FBE77ULL, 7, true), u64(125));
EXPECT_EQ(M64(0x346DC5D63886594BULL, 7, false), u64(625));
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,223 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "src/base/numbers/double.h"
#include <stdlib.h>
#include "src/base/numbers/diy-fp.h"
#include "src/common/globals.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
using DoubleTest = ::testing::Test;
namespace base {
TEST_F(DoubleTest, Uint64Conversions) {
// Start by checking the byte-order.
uint64_t ordered = 0x0123'4567'89AB'CDEF;
CHECK_EQ(3512700564088504e-318, Double(ordered).value());
uint64_t min_double64 = 0x0000'0000'0000'0001;
CHECK_EQ(5e-324, Double(min_double64).value());
uint64_t max_double64 = 0x7FEF'FFFF'FFFF'FFFF;
CHECK_EQ(1.7976931348623157e308, Double(max_double64).value());
}
TEST_F(DoubleTest, AsDiyFp) {
uint64_t ordered = 0x0123'4567'89AB'CDEF;
DiyFp diy_fp = Double(ordered).AsDiyFp();
CHECK_EQ(0x12 - 0x3FF - 52, diy_fp.e());
// The 52 mantissa bits, plus the implicit 1 in bit 52 as a UINT64.
CHECK(0x0013'4567'89AB'CDEF == diy_fp.f()); // NOLINT
uint64_t min_double64 = 0x0000'0000'0000'0001;
diy_fp = Double(min_double64).AsDiyFp();
CHECK_EQ(-0x3FF - 52 + 1, diy_fp.e());
// This is a denormal; so no hidden bit.
CHECK_EQ(1, diy_fp.f());
uint64_t max_double64 = 0x7FEF'FFFF'FFFF'FFFF;
diy_fp = Double(max_double64).AsDiyFp();
CHECK_EQ(0x7FE - 0x3FF - 52, diy_fp.e());
CHECK(0x001F'FFFF'FFFF'FFFF == diy_fp.f()); // NOLINT
}
TEST_F(DoubleTest, AsNormalizedDiyFp) {
uint64_t ordered = 0x0123'4567'89AB'CDEF;
DiyFp diy_fp = Double(ordered).AsNormalizedDiyFp();
CHECK_EQ(0x12 - 0x3FF - 52 - 11, diy_fp.e());
CHECK((uint64_t{0x0013'4567'89AB'CDEF} << 11) == diy_fp.f()); // NOLINT
uint64_t min_double64 = 0x0000'0000'0000'0001;
diy_fp = Double(min_double64).AsNormalizedDiyFp();
CHECK_EQ(-0x3FF - 52 + 1 - 63, diy_fp.e());
// This is a denormal; so no hidden bit.
CHECK(0x8000'0000'0000'0000 == diy_fp.f()); // NOLINT
uint64_t max_double64 = 0x7FEF'FFFF'FFFF'FFFF;
diy_fp = Double(max_double64).AsNormalizedDiyFp();
CHECK_EQ(0x7FE - 0x3FF - 52 - 11, diy_fp.e());
CHECK((uint64_t{0x001F'FFFF'FFFF'FFFF} << 11) == diy_fp.f());
}
TEST_F(DoubleTest, IsDenormal) {
uint64_t min_double64 = 0x0000'0000'0000'0001;
CHECK(Double(min_double64).IsDenormal());
uint64_t bits = 0x000F'FFFF'FFFF'FFFF;
CHECK(Double(bits).IsDenormal());
bits = 0x0010'0000'0000'0000;
CHECK(!Double(bits).IsDenormal());
}
TEST_F(DoubleTest, IsSpecial) {
CHECK(Double(V8_INFINITY).IsSpecial());
CHECK(Double(-V8_INFINITY).IsSpecial());
CHECK(Double(std::numeric_limits<double>::quiet_NaN()).IsSpecial());
uint64_t bits = 0xFFF1'2345'0000'0000;
CHECK(Double(bits).IsSpecial());
// Denormals are not special:
CHECK(!Double(5e-324).IsSpecial());
CHECK(!Double(-5e-324).IsSpecial());
// And some random numbers:
CHECK(!Double(0.0).IsSpecial());
CHECK(!Double(-0.0).IsSpecial());
CHECK(!Double(1.0).IsSpecial());
CHECK(!Double(-1.0).IsSpecial());
CHECK(!Double(1000000.0).IsSpecial());
CHECK(!Double(-1000000.0).IsSpecial());
CHECK(!Double(1e23).IsSpecial());
CHECK(!Double(-1e23).IsSpecial());
CHECK(!Double(1.7976931348623157e308).IsSpecial());
CHECK(!Double(-1.7976931348623157e308).IsSpecial());
}
TEST_F(DoubleTest, IsInfinite) {
CHECK(Double(V8_INFINITY).IsInfinite());
CHECK(Double(-V8_INFINITY).IsInfinite());
CHECK(!Double(std::numeric_limits<double>::quiet_NaN()).IsInfinite());
CHECK(!Double(0.0).IsInfinite());
CHECK(!Double(-0.0).IsInfinite());
CHECK(!Double(1.0).IsInfinite());
CHECK(!Double(-1.0).IsInfinite());
uint64_t min_double64 = 0x0000'0000'0000'0001;
CHECK(!Double(min_double64).IsInfinite());
}
TEST_F(DoubleTest, Sign) {
CHECK_EQ(1, Double(1.0).Sign());
CHECK_EQ(1, Double(V8_INFINITY).Sign());
CHECK_EQ(-1, Double(-V8_INFINITY).Sign());
CHECK_EQ(1, Double(0.0).Sign());
CHECK_EQ(-1, Double(-0.0).Sign());
uint64_t min_double64 = 0x0000'0000'0000'0001;
CHECK_EQ(1, Double(min_double64).Sign());
}
TEST_F(DoubleTest, NormalizedBoundaries) {
DiyFp boundary_plus;
DiyFp boundary_minus;
DiyFp diy_fp = Double(1.5).AsNormalizedDiyFp();
Double(1.5).NormalizedBoundaries(&boundary_minus, &boundary_plus);
CHECK_EQ(diy_fp.e(), boundary_minus.e());
CHECK_EQ(diy_fp.e(), boundary_plus.e());
// 1.5 does not have a significand of the form 2^p (for some p).
// Therefore its boundaries are at the same distance.
CHECK(diy_fp.f() - boundary_minus.f() == boundary_plus.f() - diy_fp.f());
CHECK((1 << 10) == diy_fp.f() - boundary_minus.f());
diy_fp = Double(1.0).AsNormalizedDiyFp();
Double(1.0).NormalizedBoundaries(&boundary_minus, &boundary_plus);
CHECK_EQ(diy_fp.e(), boundary_minus.e());
CHECK_EQ(diy_fp.e(), boundary_plus.e());
// 1.0 does have a significand of the form 2^p (for some p).
// Therefore its lower boundary is twice as close as the upper boundary.
CHECK_GT(boundary_plus.f() - diy_fp.f(), diy_fp.f() - boundary_minus.f());
CHECK((1 << 9) == diy_fp.f() - boundary_minus.f());
CHECK((1 << 10) == boundary_plus.f() - diy_fp.f());
uint64_t min_double64 = 0x0000'0000'0000'0001;
diy_fp = Double(min_double64).AsNormalizedDiyFp();
Double(min_double64).NormalizedBoundaries(&boundary_minus, &boundary_plus);
CHECK_EQ(diy_fp.e(), boundary_minus.e());
CHECK_EQ(diy_fp.e(), boundary_plus.e());
// min-value does not have a significand of the form 2^p (for some p).
// Therefore its boundaries are at the same distance.
CHECK(diy_fp.f() - boundary_minus.f() == boundary_plus.f() - diy_fp.f());
// Denormals have their boundaries much closer.
CHECK((static_cast<uint64_t>(1) << 62) == diy_fp.f() - boundary_minus.f());
uint64_t smallest_normal64 = 0x0010'0000'0000'0000;
diy_fp = Double(smallest_normal64).AsNormalizedDiyFp();
Double(smallest_normal64)
.NormalizedBoundaries(&boundary_minus, &boundary_plus);
CHECK_EQ(diy_fp.e(), boundary_minus.e());
CHECK_EQ(diy_fp.e(), boundary_plus.e());
// Even though the significand is of the form 2^p (for some p), its boundaries
// are at the same distance. (This is the only exception).
CHECK(diy_fp.f() - boundary_minus.f() == boundary_plus.f() - diy_fp.f());
CHECK((1 << 10) == diy_fp.f() - boundary_minus.f());
uint64_t largest_denormal64 = 0x000F'FFFF'FFFF'FFFF;
diy_fp = Double(largest_denormal64).AsNormalizedDiyFp();
Double(largest_denormal64)
.NormalizedBoundaries(&boundary_minus, &boundary_plus);
CHECK_EQ(diy_fp.e(), boundary_minus.e());
CHECK_EQ(diy_fp.e(), boundary_plus.e());
CHECK(diy_fp.f() - boundary_minus.f() == boundary_plus.f() - diy_fp.f());
CHECK((1 << 11) == diy_fp.f() - boundary_minus.f());
uint64_t max_double64 = 0x7FEF'FFFF'FFFF'FFFF;
diy_fp = Double(max_double64).AsNormalizedDiyFp();
Double(max_double64).NormalizedBoundaries(&boundary_minus, &boundary_plus);
CHECK_EQ(diy_fp.e(), boundary_minus.e());
CHECK_EQ(diy_fp.e(), boundary_plus.e());
// max-value does not have a significand of the form 2^p (for some p).
// Therefore its boundaries are at the same distance.
CHECK(diy_fp.f() - boundary_minus.f() == boundary_plus.f() - diy_fp.f());
CHECK((1 << 10) == diy_fp.f() - boundary_minus.f());
}
TEST_F(DoubleTest, NextDouble) {
CHECK_EQ(4e-324, Double(0.0).NextDouble());
CHECK_EQ(0.0, Double(-0.0).NextDouble());
CHECK_EQ(-0.0, Double(-4e-324).NextDouble());
Double d0(-4e-324);
Double d1(d0.NextDouble());
Double d2(d1.NextDouble());
CHECK_EQ(-0.0, d1.value());
CHECK_EQ(0.0, d2.value());
CHECK_EQ(4e-324, d2.NextDouble());
CHECK_EQ(-1.7976931348623157e308, Double(-V8_INFINITY).NextDouble());
CHECK_EQ(V8_INFINITY, Double(uint64_t{0x7FEF'FFFF'FFFF'FFFF}).NextDouble());
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,152 @@
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/doubly-threaded-list.h"
#include "src/base/vector.h"
#include "test/unittests/test-utils.h"
namespace v8::base {
class DoublyThreadedListTest : public TestWithPlatform {};
TEST_F(DoublyThreadedListTest, BasicTest) {
struct Elem {
int val;
bool operator==(const Elem& other) const {
return val == other.val && prev_ == other.prev_ && next_ == other.next_;
}
Elem** prev_;
Elem* next_;
// Defining getters required by the default DoublyThreadedListTraits.
Elem*** prev() { return &prev_; }
Elem** next() { return &next_; }
};
DoublyThreadedList<Elem*> list;
Elem e1{1, nullptr, nullptr};
Elem e2{1, nullptr, nullptr};
Elem e3{1, nullptr, nullptr};
Elem e4{1, nullptr, nullptr};
list.PushFront(&e1);
EXPECT_EQ(**(list.begin()), e1);
list.PushFront(&e2);
list.PushFront(&e3);
list.PushFront(&e4);
EXPECT_EQ(**(list.begin()), e4);
EXPECT_EQ(*e4.next_, e3);
EXPECT_EQ(*e3.next_, e2);
EXPECT_EQ(*e2.next_, e1);
EXPECT_EQ(e1.next_, nullptr);
EXPECT_EQ(*e1.prev_, &e1);
EXPECT_EQ(*e2.prev_, &e2);
EXPECT_EQ(*e3.prev_, &e3);
EXPECT_EQ(*e4.prev_, &e4);
// Removing front
list.Remove(&e4);
EXPECT_EQ(**(list.begin()), e3);
EXPECT_EQ(*e3.prev_, &e3);
EXPECT_EQ(e4.next_, nullptr);
EXPECT_EQ(e4.prev_, nullptr);
// Removing middle
list.Remove(&e2);
EXPECT_EQ(*e3.next_, e1);
EXPECT_EQ(e2.prev_, nullptr);
EXPECT_EQ(e2.next_, nullptr);
EXPECT_EQ(e1.prev_, &e3.next_);
EXPECT_EQ(*e1.prev_, &e1);
// Removing back
list.Remove(&e1);
EXPECT_EQ(e3.next_, nullptr);
EXPECT_EQ(e1.prev_, nullptr);
EXPECT_EQ(e1.next_, nullptr);
EXPECT_EQ(**(list.begin()), e3);
// Removing only item
list.Remove(&e3);
EXPECT_EQ(e3.prev_, nullptr);
EXPECT_EQ(e3.next_, nullptr);
EXPECT_TRUE(list.empty());
}
TEST_F(DoublyThreadedListTest, IteratorTest) {
struct Elem {
int val;
bool operator==(const Elem& other) const {
return val == other.val && prev_ == other.prev_ && next_ == other.next_;
}
Elem** prev_;
Elem* next_;
// Defining getters required by the default DoublyThreadedListTraits.
Elem*** prev() { return &prev_; }
Elem** next() { return &next_; }
};
DoublyThreadedList<Elem*> list;
Elem e1{1, nullptr, nullptr};
Elem e2{1, nullptr, nullptr};
Elem e3{1, nullptr, nullptr};
Elem e4{1, nullptr, nullptr};
list.PushFront(&e1);
list.PushFront(&e2);
list.PushFront(&e3);
list.PushFront(&e4);
int count = 0;
for (Elem* e : list) {
USE(e);
count++;
}
EXPECT_EQ(count, 4);
// Iterating and checking that all items are where they should be
auto it = list.begin();
EXPECT_EQ(**it, e4);
++it;
EXPECT_EQ(**it, e3);
++it;
EXPECT_EQ(**it, e2);
++it;
EXPECT_EQ(**it, e1);
++it;
EXPECT_FALSE(it != list.end());
// Removing with the iterator
it = list.begin();
EXPECT_EQ(**it, e4);
it = list.RemoveAt(it);
EXPECT_EQ(**it, e3);
++it;
EXPECT_EQ(**it, e2);
it = list.RemoveAt(it);
EXPECT_EQ(**it, e1);
EXPECT_EQ(*e3.next_, e1);
it = list.RemoveAt(it);
EXPECT_FALSE(it != list.end());
EXPECT_EQ(e3.next_, nullptr);
it = list.begin();
it = list.RemoveAt(it);
EXPECT_TRUE(list.empty());
EXPECT_EQ(e1.next_, nullptr);
EXPECT_EQ(e2.next_, nullptr);
EXPECT_EQ(e3.next_, nullptr);
EXPECT_EQ(e4.next_, nullptr);
EXPECT_EQ(e1.prev_, nullptr);
EXPECT_EQ(e2.prev_, nullptr);
EXPECT_EQ(e3.prev_, nullptr);
EXPECT_EQ(e4.prev_, nullptr);
}
} // namespace v8::base

View File

@ -0,0 +1,326 @@
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "src/base/numbers/dtoa.h"
#include <stdlib.h>
#include "src/base/numbers/double.h"
#include "test/unittests/gay-fixed.h"
#include "test/unittests/gay-precision.h"
#include "test/unittests/gay-shortest.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
using DtoaTest = ::testing::Test;
namespace base {
namespace test_dtoa {
// Removes trailing '0' digits (modifies {representation}). Can create an empty
// string if all digits are 0.
static void TrimRepresentation(char* representation) {
size_t len = strlen(representation);
while (len > 0 && representation[len - 1] == '0') --len;
representation[len] = '\0';
}
static const int kBufferSize = 100;
TEST_F(DtoaTest, DtoaVariousDoubles) {
char buffer_container[kBufferSize];
base::Vector<char> buffer(buffer_container, kBufferSize);
int length;
int point;
int sign;
DoubleToAscii(0.0, DTOA_SHORTEST, 0, buffer, &sign, &length, &point);
CHECK_EQ(0, strcmp("0", buffer.begin()));
CHECK_EQ(1, point);
DoubleToAscii(0.0, DTOA_FIXED, 2, buffer, &sign, &length, &point);
CHECK_EQ(1, length);
CHECK_EQ(0, strcmp("0", buffer.begin()));
CHECK_EQ(1, point);
DoubleToAscii(0.0, DTOA_PRECISION, 3, buffer, &sign, &length, &point);
CHECK_EQ(1, length);
CHECK_EQ(0, strcmp("0", buffer.begin()));
CHECK_EQ(1, point);
DoubleToAscii(1.0, DTOA_SHORTEST, 0, buffer, &sign, &length, &point);
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
DoubleToAscii(1.0, DTOA_FIXED, 3, buffer, &sign, &length, &point);
CHECK_GE(3, length - point);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
DoubleToAscii(1.0, DTOA_PRECISION, 3, buffer, &sign, &length, &point);
CHECK_GE(3, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
DoubleToAscii(1.5, DTOA_SHORTEST, 0, buffer, &sign, &length, &point);
CHECK_EQ(0, strcmp("15", buffer.begin()));
CHECK_EQ(1, point);
DoubleToAscii(1.5, DTOA_FIXED, 10, buffer, &sign, &length, &point);
CHECK_GE(10, length - point);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("15", buffer.begin()));
CHECK_EQ(1, point);
DoubleToAscii(1.5, DTOA_PRECISION, 10, buffer, &sign, &length, &point);
CHECK_GE(10, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("15", buffer.begin()));
CHECK_EQ(1, point);
double min_double = 5e-324;
DoubleToAscii(min_double, DTOA_SHORTEST, 0, buffer, &sign, &length, &point);
CHECK_EQ(0, strcmp("5", buffer.begin()));
CHECK_EQ(-323, point);
DoubleToAscii(min_double, DTOA_FIXED, 5, buffer, &sign, &length, &point);
CHECK_GE(5, length - point);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("", buffer.begin()));
CHECK_GE(-5, point);
DoubleToAscii(min_double, DTOA_PRECISION, 5, buffer, &sign, &length, &point);
CHECK_GE(5, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("49407", buffer.begin()));
CHECK_EQ(-323, point);
double max_double = 1.7976931348623157e308;
DoubleToAscii(max_double, DTOA_SHORTEST, 0, buffer, &sign, &length, &point);
CHECK_EQ(0, strcmp("17976931348623157", buffer.begin()));
CHECK_EQ(309, point);
DoubleToAscii(max_double, DTOA_PRECISION, 7, buffer, &sign, &length, &point);
CHECK_GE(7, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("1797693", buffer.begin()));
CHECK_EQ(309, point);
DoubleToAscii(4294967272.0, DTOA_SHORTEST, 0, buffer, &sign, &length, &point);
CHECK_EQ(0, strcmp("4294967272", buffer.begin()));
CHECK_EQ(10, point);
DoubleToAscii(4294967272.0, DTOA_FIXED, 5, buffer, &sign, &length, &point);
CHECK_GE(5, length - point);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("4294967272", buffer.begin()));
CHECK_EQ(10, point);
DoubleToAscii(4294967272.0, DTOA_PRECISION, 14, buffer, &sign, &length,
&point);
CHECK_GE(14, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("4294967272", buffer.begin()));
CHECK_EQ(10, point);
DoubleToAscii(4.1855804968213567e298, DTOA_SHORTEST, 0, buffer, &sign,
&length, &point);
CHECK_EQ(0, strcmp("4185580496821357", buffer.begin()));
CHECK_EQ(299, point);
DoubleToAscii(4.1855804968213567e298, DTOA_PRECISION, 20, buffer, &sign,
&length, &point);
CHECK_GE(20, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("41855804968213567225", buffer.begin()));
CHECK_EQ(299, point);
DoubleToAscii(5.5626846462680035e-309, DTOA_SHORTEST, 0, buffer, &sign,
&length, &point);
CHECK_EQ(0, strcmp("5562684646268003", buffer.begin()));
CHECK_EQ(-308, point);
DoubleToAscii(5.5626846462680035e-309, DTOA_PRECISION, 1, buffer, &sign,
&length, &point);
CHECK_GE(1, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("6", buffer.begin()));
CHECK_EQ(-308, point);
DoubleToAscii(-2147483648.0, DTOA_SHORTEST, 0, buffer, &sign, &length,
&point);
CHECK_EQ(1, sign);
CHECK_EQ(0, strcmp("2147483648", buffer.begin()));
CHECK_EQ(10, point);
DoubleToAscii(-2147483648.0, DTOA_FIXED, 2, buffer, &sign, &length, &point);
CHECK_GE(2, length - point);
TrimRepresentation(buffer.begin());
CHECK_EQ(1, sign);
CHECK_EQ(0, strcmp("2147483648", buffer.begin()));
CHECK_EQ(10, point);
DoubleToAscii(-2147483648.0, DTOA_PRECISION, 5, buffer, &sign, &length,
&point);
CHECK_GE(5, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(1, sign);
CHECK_EQ(0, strcmp("21475", buffer.begin()));
CHECK_EQ(10, point);
DoubleToAscii(-3.5844466002796428e+298, DTOA_SHORTEST, 0, buffer, &sign,
&length, &point);
CHECK_EQ(1, sign);
CHECK_EQ(0, strcmp("35844466002796428", buffer.begin()));
CHECK_EQ(299, point);
DoubleToAscii(-3.5844466002796428e+298, DTOA_PRECISION, 10, buffer, &sign,
&length, &point);
CHECK_EQ(1, sign);
CHECK_GE(10, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("35844466", buffer.begin()));
CHECK_EQ(299, point);
uint64_t smallest_normal64 = 0x0010'0000'0000'0000;
double v = Double(smallest_normal64).value();
DoubleToAscii(v, DTOA_SHORTEST, 0, buffer, &sign, &length, &point);
CHECK_EQ(0, strcmp("22250738585072014", buffer.begin()));
CHECK_EQ(-307, point);
DoubleToAscii(v, DTOA_PRECISION, 20, buffer, &sign, &length, &point);
CHECK_GE(20, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("22250738585072013831", buffer.begin()));
CHECK_EQ(-307, point);
uint64_t largest_denormal64 = 0x000F'FFFF'FFFF'FFFF;
v = Double(largest_denormal64).value();
DoubleToAscii(v, DTOA_SHORTEST, 0, buffer, &sign, &length, &point);
CHECK_EQ(0, strcmp("2225073858507201", buffer.begin()));
CHECK_EQ(-307, point);
DoubleToAscii(v, DTOA_PRECISION, 20, buffer, &sign, &length, &point);
CHECK_GE(20, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("2225073858507200889", buffer.begin()));
CHECK_EQ(-307, point);
DoubleToAscii(4128420500802942e-24, DTOA_SHORTEST, 0, buffer, &sign, &length,
&point);
CHECK_EQ(0, sign);
CHECK_EQ(0, strcmp("4128420500802942", buffer.begin()));
CHECK_EQ(-8, point);
v = -3.9292015898194142585311918e-10;
DoubleToAscii(v, DTOA_SHORTEST, 0, buffer, &sign, &length, &point);
CHECK_EQ(0, strcmp("39292015898194143", buffer.begin()));
v = 4194304.0;
DoubleToAscii(v, DTOA_FIXED, 5, buffer, &sign, &length, &point);
CHECK_GE(5, length - point);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("4194304", buffer.begin()));
v = 3.3161339052167390562200598e-237;
DoubleToAscii(v, DTOA_PRECISION, 19, buffer, &sign, &length, &point);
CHECK_GE(19, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("3316133905216739056", buffer.begin()));
CHECK_EQ(-236, point);
}
TEST_F(DtoaTest, DtoaGayShortest) {
char buffer_container[kBufferSize];
base::Vector<char> buffer(buffer_container, kBufferSize);
int sign;
int length;
int point;
base::Vector<const PrecomputedShortest> precomputed =
PrecomputedShortestRepresentations();
for (int i = 0; i < precomputed.length(); ++i) {
const PrecomputedShortest current_test = precomputed[i];
double v = current_test.v;
DoubleToAscii(v, DTOA_SHORTEST, 0, buffer, &sign, &length, &point);
CHECK_EQ(0, sign); // All precomputed numbers are positive.
CHECK_EQ(current_test.decimal_point, point);
CHECK_EQ(0, strcmp(current_test.representation, buffer.begin()));
}
}
TEST_F(DtoaTest, DtoaGayFixed) {
char buffer_container[kBufferSize];
base::Vector<char> buffer(buffer_container, kBufferSize);
int sign;
int length;
int point;
base::Vector<const PrecomputedFixed> precomputed =
PrecomputedFixedRepresentations();
for (int i = 0; i < precomputed.length(); ++i) {
const PrecomputedFixed current_test = precomputed[i];
double v = current_test.v;
int number_digits = current_test.number_digits;
DoubleToAscii(v, DTOA_FIXED, number_digits, buffer, &sign, &length, &point);
CHECK_EQ(0, sign); // All precomputed numbers are positive.
CHECK_EQ(current_test.decimal_point, point);
CHECK_GE(number_digits, length - point);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp(current_test.representation, buffer.begin()));
}
}
TEST_F(DtoaTest, DtoaGayPrecision) {
char buffer_container[kBufferSize];
base::Vector<char> buffer(buffer_container, kBufferSize);
int sign;
int length;
int point;
base::Vector<const PrecomputedPrecision> precomputed =
PrecomputedPrecisionRepresentations();
for (int i = 0; i < precomputed.length(); ++i) {
const PrecomputedPrecision current_test = precomputed[i];
double v = current_test.v;
int number_digits = current_test.number_digits;
DoubleToAscii(v, DTOA_PRECISION, number_digits, buffer, &sign, &length,
&point);
CHECK_EQ(0, sign); // All precomputed numbers are positive.
CHECK_EQ(current_test.decimal_point, point);
CHECK_GE(number_digits, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp(current_test.representation, buffer.begin()));
}
}
} // namespace test_dtoa
} // namespace base
} // namespace v8

View File

@ -0,0 +1,290 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "src/base/numbers/fast-dtoa.h"
#include <stdlib.h>
#include "src/base/numbers/double.h"
#include "test/unittests/gay-precision.h"
#include "test/unittests/gay-shortest.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
using FastDtoaTest = ::testing::Test;
namespace base {
namespace test_fast_dtoa {
static const int kBufferSize = 100;
// Removes trailing '0' digits (modifies {representation}). Can create an empty
// string if all digits are 0.
static void TrimRepresentation(char* representation) {
size_t len = strlen(representation);
while (len > 0 && representation[len - 1] == '0') --len;
representation[len] = '\0';
}
TEST_F(FastDtoaTest, FastDtoaShortestVariousDoubles) {
char buffer_container[kBufferSize];
Vector<char> buffer(buffer_container, kBufferSize);
int length;
int point;
int status;
double min_double = 5e-324;
status = FastDtoa(min_double, FAST_DTOA_SHORTEST, 0, buffer, &length, &point);
CHECK(status);
CHECK_EQ(0, strcmp("5", buffer.begin()));
CHECK_EQ(-323, point);
double max_double = 1.7976931348623157e308;
status = FastDtoa(max_double, FAST_DTOA_SHORTEST, 0, buffer, &length, &point);
CHECK(status);
CHECK_EQ(0, strcmp("17976931348623157", buffer.begin()));
CHECK_EQ(309, point);
status =
FastDtoa(4294967272.0, FAST_DTOA_SHORTEST, 0, buffer, &length, &point);
CHECK(status);
CHECK_EQ(0, strcmp("4294967272", buffer.begin()));
CHECK_EQ(10, point);
status = FastDtoa(4.1855804968213567e298, FAST_DTOA_SHORTEST, 0, buffer,
&length, &point);
CHECK(status);
CHECK_EQ(0, strcmp("4185580496821357", buffer.begin()));
CHECK_EQ(299, point);
status = FastDtoa(5.5626846462680035e-309, FAST_DTOA_SHORTEST, 0, buffer,
&length, &point);
CHECK(status);
CHECK_EQ(0, strcmp("5562684646268003", buffer.begin()));
CHECK_EQ(-308, point);
status =
FastDtoa(2147483648.0, FAST_DTOA_SHORTEST, 0, buffer, &length, &point);
CHECK(status);
CHECK_EQ(0, strcmp("2147483648", buffer.begin()));
CHECK_EQ(10, point);
status = FastDtoa(3.5844466002796428e+298, FAST_DTOA_SHORTEST, 0, buffer,
&length, &point);
if (status) { // Not all FastDtoa variants manage to compute this number.
CHECK_EQ(0, strcmp("35844466002796428", buffer.begin()));
CHECK_EQ(299, point);
}
uint64_t smallest_normal64 = 0x0010'0000'0000'0000;
double v = Double(smallest_normal64).value();
status = FastDtoa(v, FAST_DTOA_SHORTEST, 0, buffer, &length, &point);
if (status) {
CHECK_EQ(0, strcmp("22250738585072014", buffer.begin()));
CHECK_EQ(-307, point);
}
uint64_t largest_denormal64 = 0x000F'FFFF'FFFF'FFFF;
v = Double(largest_denormal64).value();
status = FastDtoa(v, FAST_DTOA_SHORTEST, 0, buffer, &length, &point);
if (status) {
CHECK_EQ(0, strcmp("2225073858507201", buffer.begin()));
CHECK_EQ(-307, point);
}
}
TEST_F(FastDtoaTest, FastDtoaPrecisionVariousDoubles) {
char buffer_container[kBufferSize];
Vector<char> buffer(buffer_container, kBufferSize);
int length;
int point;
int status;
status = FastDtoa(1.0, FAST_DTOA_PRECISION, 3, buffer, &length, &point);
CHECK(status);
CHECK_GE(3, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
status = FastDtoa(1.5, FAST_DTOA_PRECISION, 10, buffer, &length, &point);
if (status) {
CHECK_GE(10, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("15", buffer.begin()));
CHECK_EQ(1, point);
}
double min_double = 5e-324;
status =
FastDtoa(min_double, FAST_DTOA_PRECISION, 5, buffer, &length, &point);
CHECK(status);
CHECK_EQ(0, strcmp("49407", buffer.begin()));
CHECK_EQ(-323, point);
double max_double = 1.7976931348623157e308;
status =
FastDtoa(max_double, FAST_DTOA_PRECISION, 7, buffer, &length, &point);
CHECK(status);
CHECK_EQ(0, strcmp("1797693", buffer.begin()));
CHECK_EQ(309, point);
status =
FastDtoa(4294967272.0, FAST_DTOA_PRECISION, 14, buffer, &length, &point);
if (status) {
CHECK_GE(14, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("4294967272", buffer.begin()));
CHECK_EQ(10, point);
}
status = FastDtoa(4.1855804968213567e298, FAST_DTOA_PRECISION, 17, buffer,
&length, &point);
CHECK(status);
CHECK_EQ(0, strcmp("41855804968213567", buffer.begin()));
CHECK_EQ(299, point);
status = FastDtoa(5.5626846462680035e-309, FAST_DTOA_PRECISION, 1, buffer,
&length, &point);
CHECK(status);
CHECK_EQ(0, strcmp("6", buffer.begin()));
CHECK_EQ(-308, point);
status =
FastDtoa(2147483648.0, FAST_DTOA_PRECISION, 5, buffer, &length, &point);
CHECK(status);
CHECK_EQ(0, strcmp("21475", buffer.begin()));
CHECK_EQ(10, point);
status = FastDtoa(3.5844466002796428e+298, FAST_DTOA_PRECISION, 10, buffer,
&length, &point);
CHECK(status);
CHECK_GE(10, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("35844466", buffer.begin()));
CHECK_EQ(299, point);
uint64_t smallest_normal64 = 0x0010'0000'0000'0000;
double v = Double(smallest_normal64).value();
status = FastDtoa(v, FAST_DTOA_PRECISION, 17, buffer, &length, &point);
CHECK(status);
CHECK_EQ(0, strcmp("22250738585072014", buffer.begin()));
CHECK_EQ(-307, point);
uint64_t largest_denormal64 = 0x000F'FFFF'FFFF'FFFF;
v = Double(largest_denormal64).value();
status = FastDtoa(v, FAST_DTOA_PRECISION, 17, buffer, &length, &point);
CHECK(status);
CHECK_GE(20, length);
TrimRepresentation(buffer.begin());
CHECK_EQ(0, strcmp("22250738585072009", buffer.begin()));
CHECK_EQ(-307, point);
v = 3.3161339052167390562200598e-237;
status = FastDtoa(v, FAST_DTOA_PRECISION, 18, buffer, &length, &point);
CHECK(status);
CHECK_EQ(0, strcmp("331613390521673906", buffer.begin()));
CHECK_EQ(-236, point);
v = 7.9885183916008099497815232e+191;
status = FastDtoa(v, FAST_DTOA_PRECISION, 4, buffer, &length, &point);
CHECK(status);
CHECK_EQ(0, strcmp("7989", buffer.begin()));
CHECK_EQ(192, point);
}
TEST_F(FastDtoaTest, FastDtoaGayShortest) {
char buffer_container[kBufferSize];
Vector<char> buffer(buffer_container, kBufferSize);
bool status;
int length;
int point;
int succeeded = 0;
int total = 0;
bool needed_max_length = false;
Vector<const PrecomputedShortest> precomputed =
PrecomputedShortestRepresentations();
for (int i = 0; i < precomputed.length(); ++i) {
const PrecomputedShortest current_test = precomputed[i];
total++;
double v = current_test.v;
status = FastDtoa(v, FAST_DTOA_SHORTEST, 0, buffer, &length, &point);
CHECK_GE(kFastDtoaMaximalLength, length);
if (!status) continue;
if (length == kFastDtoaMaximalLength) needed_max_length = true;
succeeded++;
CHECK_EQ(current_test.decimal_point, point);
CHECK_EQ(0, strcmp(current_test.representation, buffer.begin()));
}
CHECK_GT(succeeded * 1.0 / total, 0.99);
CHECK(needed_max_length);
}
TEST_F(FastDtoaTest, FastDtoaGayPrecision) {
char buffer_container[kBufferSize];
Vector<char> buffer(buffer_container, kBufferSize);
bool status;
int length;
int point;
int succeeded = 0;
int total = 0;
// Count separately for entries with less than 15 requested digits.
int succeeded_15 = 0;
int total_15 = 0;
Vector<const PrecomputedPrecision> precomputed =
PrecomputedPrecisionRepresentations();
for (int i = 0; i < precomputed.length(); ++i) {
const PrecomputedPrecision current_test = precomputed[i];
double v = current_test.v;
int number_digits = current_test.number_digits;
total++;
if (number_digits <= 15) total_15++;
status = FastDtoa(v, FAST_DTOA_PRECISION, number_digits, buffer, &length,
&point);
CHECK_GE(number_digits, length);
if (!status) continue;
succeeded++;
if (number_digits <= 15) succeeded_15++;
TrimRepresentation(buffer.begin());
CHECK_EQ(current_test.decimal_point, point);
CHECK_EQ(0, strcmp(current_test.representation, buffer.begin()));
}
// The precomputed numbers contain many entries with many requested
// digits. These have a high failure rate and we therefore expect a lower
// success rate than for the shortest representation.
CHECK_GT(succeeded * 1.0 / total, 0.85);
// However with less than 15 digits almost the algorithm should almost always
// succeed.
CHECK_GT(succeeded_15 * 1.0 / total_15, 0.9999);
}
} // namespace test_fast_dtoa
} // namespace base
} // namespace v8

View File

@ -0,0 +1,512 @@
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "src/base/numbers/fixed-dtoa.h"
#include <stdlib.h>
#include "test/unittests/gay-fixed.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
using FixedDtoaTest = ::testing::Test;
namespace base {
static const int kBufferSize = 500;
TEST_F(FixedDtoaTest, FastFixedVariousDoubles) {
char buffer_container[kBufferSize];
Vector<char> buffer(buffer_container, kBufferSize);
int length;
int point;
CHECK(FastFixedDtoa(1.0, 1, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(1.0, 15, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(1.0, 0, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(0xFFFFFFFF, 5, buffer, &length, &point));
CHECK_EQ(0, strcmp("4294967295", buffer.begin()));
CHECK_EQ(10, point);
CHECK(FastFixedDtoa(4294967296.0, 5, buffer, &length, &point));
CHECK_EQ(0, strcmp("4294967296", buffer.begin()));
CHECK_EQ(10, point);
CHECK(FastFixedDtoa(1e21, 5, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
// CHECK_EQ(22, point);
CHECK_EQ(22, point);
CHECK(FastFixedDtoa(999999999999999868928.00, 2, buffer, &length, &point));
CHECK_EQ(0, strcmp("999999999999999868928", buffer.begin()));
CHECK_EQ(21, point);
CHECK(FastFixedDtoa(6.9999999999999989514240000e+21, 5, buffer, &length,
&point));
CHECK_EQ(0, strcmp("6999999999999998951424", buffer.begin()));
CHECK_EQ(22, point);
CHECK(FastFixedDtoa(1.5, 5, buffer, &length, &point));
CHECK_EQ(0, strcmp("15", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(1.55, 5, buffer, &length, &point));
CHECK_EQ(0, strcmp("155", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(1.55, 1, buffer, &length, &point));
CHECK_EQ(0, strcmp("16", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(1.00000001, 15, buffer, &length, &point));
CHECK_EQ(0, strcmp("100000001", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(0.1, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(0, point);
CHECK(FastFixedDtoa(0.01, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-1, point);
CHECK(FastFixedDtoa(0.001, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-2, point);
CHECK(FastFixedDtoa(0.0001, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-3, point);
CHECK(FastFixedDtoa(0.00001, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-4, point);
CHECK(FastFixedDtoa(0.000001, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-5, point);
CHECK(FastFixedDtoa(0.0000001, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-6, point);
CHECK(FastFixedDtoa(0.00000001, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-7, point);
CHECK(FastFixedDtoa(0.000000001, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-8, point);
CHECK(FastFixedDtoa(0.0000000001, 15, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-9, point);
CHECK(FastFixedDtoa(0.00000000001, 15, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-10, point);
CHECK(FastFixedDtoa(0.000000000001, 15, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-11, point);
CHECK(FastFixedDtoa(0.0000000000001, 15, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-12, point);
CHECK(FastFixedDtoa(0.00000000000001, 15, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-13, point);
CHECK(FastFixedDtoa(0.000000000000001, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-14, point);
CHECK(FastFixedDtoa(0.0000000000000001, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-15, point);
CHECK(FastFixedDtoa(0.00000000000000001, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-16, point);
CHECK(FastFixedDtoa(0.000000000000000001, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-17, point);
CHECK(FastFixedDtoa(0.0000000000000000001, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-18, point);
CHECK(FastFixedDtoa(0.00000000000000000001, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-19, point);
CHECK(FastFixedDtoa(0.10000000004, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(0, point);
CHECK(FastFixedDtoa(0.01000000004, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-1, point);
CHECK(FastFixedDtoa(0.00100000004, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-2, point);
CHECK(FastFixedDtoa(0.00010000004, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-3, point);
CHECK(FastFixedDtoa(0.00001000004, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-4, point);
CHECK(FastFixedDtoa(0.00000100004, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-5, point);
CHECK(FastFixedDtoa(0.00000010004, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-6, point);
CHECK(FastFixedDtoa(0.00000001004, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-7, point);
CHECK(FastFixedDtoa(0.00000000104, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-8, point);
CHECK(FastFixedDtoa(0.0000000001000004, 15, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-9, point);
CHECK(FastFixedDtoa(0.0000000000100004, 15, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-10, point);
CHECK(FastFixedDtoa(0.0000000000010004, 15, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-11, point);
CHECK(FastFixedDtoa(0.0000000000001004, 15, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-12, point);
CHECK(FastFixedDtoa(0.0000000000000104, 15, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-13, point);
CHECK(FastFixedDtoa(0.000000000000001000004, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-14, point);
CHECK(FastFixedDtoa(0.000000000000000100004, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-15, point);
CHECK(FastFixedDtoa(0.000000000000000010004, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-16, point);
CHECK(FastFixedDtoa(0.000000000000000001004, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-17, point);
CHECK(FastFixedDtoa(0.000000000000000000104, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-18, point);
CHECK(FastFixedDtoa(0.000000000000000000014, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-19, point);
CHECK(FastFixedDtoa(0.10000000006, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1000000001", buffer.begin()));
CHECK_EQ(0, point);
CHECK(FastFixedDtoa(0.01000000006, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("100000001", buffer.begin()));
CHECK_EQ(-1, point);
CHECK(FastFixedDtoa(0.00100000006, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("10000001", buffer.begin()));
CHECK_EQ(-2, point);
CHECK(FastFixedDtoa(0.00010000006, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1000001", buffer.begin()));
CHECK_EQ(-3, point);
CHECK(FastFixedDtoa(0.00001000006, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("100001", buffer.begin()));
CHECK_EQ(-4, point);
CHECK(FastFixedDtoa(0.00000100006, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("10001", buffer.begin()));
CHECK_EQ(-5, point);
CHECK(FastFixedDtoa(0.00000010006, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1001", buffer.begin()));
CHECK_EQ(-6, point);
CHECK(FastFixedDtoa(0.00000001006, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("101", buffer.begin()));
CHECK_EQ(-7, point);
CHECK(FastFixedDtoa(0.00000000106, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("11", buffer.begin()));
CHECK_EQ(-8, point);
CHECK(FastFixedDtoa(0.0000000001000006, 15, buffer, &length, &point));
CHECK_EQ(0, strcmp("100001", buffer.begin()));
CHECK_EQ(-9, point);
CHECK(FastFixedDtoa(0.0000000000100006, 15, buffer, &length, &point));
CHECK_EQ(0, strcmp("10001", buffer.begin()));
CHECK_EQ(-10, point);
CHECK(FastFixedDtoa(0.0000000000010006, 15, buffer, &length, &point));
CHECK_EQ(0, strcmp("1001", buffer.begin()));
CHECK_EQ(-11, point);
CHECK(FastFixedDtoa(0.0000000000001006, 15, buffer, &length, &point));
CHECK_EQ(0, strcmp("101", buffer.begin()));
CHECK_EQ(-12, point);
CHECK(FastFixedDtoa(0.0000000000000106, 15, buffer, &length, &point));
CHECK_EQ(0, strcmp("11", buffer.begin()));
CHECK_EQ(-13, point);
CHECK(FastFixedDtoa(0.000000000000001000006, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("100001", buffer.begin()));
CHECK_EQ(-14, point);
CHECK(FastFixedDtoa(0.000000000000000100006, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("10001", buffer.begin()));
CHECK_EQ(-15, point);
CHECK(FastFixedDtoa(0.000000000000000010006, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("1001", buffer.begin()));
CHECK_EQ(-16, point);
CHECK(FastFixedDtoa(0.000000000000000001006, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("101", buffer.begin()));
CHECK_EQ(-17, point);
CHECK(FastFixedDtoa(0.000000000000000000106, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("11", buffer.begin()));
CHECK_EQ(-18, point);
CHECK(FastFixedDtoa(0.000000000000000000016, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("2", buffer.begin()));
CHECK_EQ(-19, point);
CHECK(FastFixedDtoa(0.6, 0, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(0.96, 1, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(0.996, 2, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(0.9996, 3, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(0.99996, 4, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(0.999996, 5, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(0.9999996, 6, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(0.99999996, 7, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(0.999999996, 8, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(0.9999999996, 9, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(0.99999999996, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(0.999999999996, 11, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(0.9999999999996, 12, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(0.99999999999996, 13, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(0.999999999999996, 14, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(0.9999999999999996, 15, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(0.00999999999999996, 16, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-1, point);
CHECK(FastFixedDtoa(0.000999999999999996, 17, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-2, point);
CHECK(FastFixedDtoa(0.0000999999999999996, 18, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-3, point);
CHECK(FastFixedDtoa(0.00000999999999999996, 19, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-4, point);
CHECK(FastFixedDtoa(0.000000999999999999996, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-5, point);
CHECK(FastFixedDtoa(323423.234234, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("323423234234", buffer.begin()));
CHECK_EQ(6, point);
CHECK(FastFixedDtoa(12345678.901234, 4, buffer, &length, &point));
CHECK_EQ(0, strcmp("123456789012", buffer.begin()));
CHECK_EQ(8, point);
CHECK(FastFixedDtoa(98765.432109, 5, buffer, &length, &point));
CHECK_EQ(0, strcmp("9876543211", buffer.begin()));
CHECK_EQ(5, point);
CHECK(FastFixedDtoa(42, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("42", buffer.begin()));
CHECK_EQ(2, point);
CHECK(FastFixedDtoa(0.5, 0, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(1, point);
CHECK(FastFixedDtoa(1e-23, 10, buffer, &length, &point));
CHECK_EQ(0, strcmp("", buffer.begin()));
CHECK_EQ(-10, point);
CHECK(FastFixedDtoa(1e-123, 2, buffer, &length, &point));
CHECK_EQ(0, strcmp("", buffer.begin()));
CHECK_EQ(-2, point);
CHECK(FastFixedDtoa(1e-123, 0, buffer, &length, &point));
CHECK_EQ(0, strcmp("", buffer.begin()));
CHECK_EQ(0, point);
CHECK(FastFixedDtoa(1e-23, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("", buffer.begin()));
CHECK_EQ(-20, point);
CHECK(FastFixedDtoa(1e-21, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("", buffer.begin()));
CHECK_EQ(-20, point);
CHECK(FastFixedDtoa(1e-22, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("", buffer.begin()));
CHECK_EQ(-20, point);
CHECK(FastFixedDtoa(6e-21, 20, buffer, &length, &point));
CHECK_EQ(0, strcmp("1", buffer.begin()));
CHECK_EQ(-19, point);
CHECK(FastFixedDtoa(9.1193616301674545152000000e+19, 0, buffer, &length,
&point));
CHECK_EQ(0, strcmp("91193616301674545152", buffer.begin()));
CHECK_EQ(20, point);
CHECK(FastFixedDtoa(4.8184662102767651659096515e-04, 19, buffer, &length,
&point));
CHECK_EQ(0, strcmp("4818466210276765", buffer.begin()));
CHECK_EQ(-3, point);
CHECK(FastFixedDtoa(1.9023164229540652612705182e-23, 8, buffer, &length,
&point));
CHECK_EQ(0, strcmp("", buffer.begin()));
CHECK_EQ(-8, point);
CHECK(FastFixedDtoa(1000000000000000128.0, 0, buffer, &length, &point));
CHECK_EQ(0, strcmp("1000000000000000128", buffer.begin()));
CHECK_EQ(19, point);
}
TEST_F(FixedDtoaTest, FastFixedDtoaGayFixed) {
char buffer_container[kBufferSize];
Vector<char> buffer(buffer_container, kBufferSize);
bool status;
int length;
int point;
Vector<const PrecomputedFixed> precomputed =
PrecomputedFixedRepresentations();
for (int i = 0; i < precomputed.length(); ++i) {
const PrecomputedFixed current_test = precomputed[i];
double v = current_test.v;
int number_digits = current_test.number_digits;
status = FastFixedDtoa(v, number_digits, buffer, &length, &point);
CHECK(status);
CHECK_EQ(current_test.decimal_point, point);
CHECK_GE(number_digits, length - point);
CHECK_EQ(0, strcmp(current_test.representation, buffer.begin()));
}
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,103 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdint.h>
#include "src/base/flags.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace base {
namespace {
enum Flag1 {
kFlag1None = 0,
kFlag1First = 1u << 1,
kFlag1Second = 1u << 2,
kFlag1All = kFlag1None | kFlag1First | kFlag1Second
};
using Flags1 = Flags<Flag1>;
DEFINE_OPERATORS_FOR_FLAGS(Flags1)
Flags1 bar(Flags1 flags1) { return flags1; }
} // namespace
TEST(FlagsTest, BasicOperations) {
Flags1 a;
EXPECT_EQ(kFlag1None, static_cast<int>(a));
a |= kFlag1First;
EXPECT_EQ(kFlag1First, static_cast<int>(a));
a = a | kFlag1Second;
EXPECT_EQ(kFlag1All, static_cast<int>(a));
a &= kFlag1Second;
EXPECT_EQ(kFlag1Second, static_cast<int>(a));
a = kFlag1None & a;
EXPECT_EQ(kFlag1None, static_cast<int>(a));
a ^= (kFlag1All | kFlag1None);
EXPECT_EQ(kFlag1All, static_cast<int>(a));
Flags1 b = ~a;
EXPECT_EQ(kFlag1All, static_cast<int>(a));
EXPECT_EQ(~static_cast<int>(a), static_cast<int>(b));
Flags1 c = a;
EXPECT_EQ(a, c);
EXPECT_NE(a, b);
EXPECT_EQ(a, bar(a));
EXPECT_EQ(a, bar(kFlag1All));
}
namespace {
namespace foo {
enum Option {
kNoOptions = 0,
kOption1 = 1,
kOption2 = 2,
kAllOptions = kNoOptions | kOption1 | kOption2
};
using Options = Flags<Option>;
} // namespace foo
DEFINE_OPERATORS_FOR_FLAGS(foo::Options)
} // namespace
TEST(FlagsTest, NamespaceScope) {
foo::Options options;
options ^= foo::kNoOptions;
options |= foo::kOption1 | foo::kOption2;
EXPECT_EQ(foo::kAllOptions, static_cast<int>(options));
}
namespace {
struct Foo {
enum Enum { kEnum1 = 1, kEnum2 = 2 };
using Enums = Flags<Enum, uint32_t>;
};
DEFINE_OPERATORS_FOR_FLAGS(Foo::Enums)
} // namespace
TEST(FlagsTest, ClassScope) {
Foo::Enums enums;
enums |= Foo::kEnum1;
enums |= Foo::kEnum2;
EXPECT_TRUE(enums & Foo::kEnum1);
EXPECT_TRUE(enums & Foo::kEnum2);
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,204 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/hashing.h"
#include <limits>
#include <set>
#include "test/unittests/test-utils.h"
namespace v8 {
namespace base {
TEST(HashingTest, HashBool) {
hash<bool> h, h1, h2;
EXPECT_EQ(h1(true), h2(true));
EXPECT_EQ(h1(false), h2(false));
EXPECT_NE(h(true), h(false));
}
TEST(HashingTest, HashFloatZero) {
hash<float> h;
EXPECT_EQ(h(0.0f), h(-0.0f));
}
TEST(HashingTest, HashDoubleZero) {
hash<double> h;
EXPECT_EQ(h(0.0), h(-0.0));
}
namespace {
inline int64_t GetRandomSeedFromFlag(int random_seed) {
return random_seed ? random_seed : TimeTicks::Now().ToInternalValue();
}
} // namespace
template <typename T>
class HashingTest : public ::testing::Test {
public:
HashingTest()
: rng_(GetRandomSeedFromFlag(::v8::internal::v8_flags.random_seed)) {}
~HashingTest() override = default;
HashingTest(const HashingTest&) = delete;
HashingTest& operator=(const HashingTest&) = delete;
RandomNumberGenerator* rng() { return &rng_; }
private:
RandomNumberGenerator rng_;
};
using HashingTypes =
::testing::Types<signed char, unsigned char,
short, // NOLINT(runtime/int)
unsigned short, // NOLINT(runtime/int)
int, unsigned int, long, // NOLINT(runtime/int)
unsigned long, // NOLINT(runtime/int)
long long, // NOLINT(runtime/int)
unsigned long long, // NOLINT(runtime/int)
int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
int64_t, uint64_t, float, double>;
TYPED_TEST_SUITE(HashingTest, HashingTypes);
TYPED_TEST(HashingTest, EqualToImpliesSameHashCode) {
hash<TypeParam> h;
std::equal_to<TypeParam> e;
TypeParam values[32];
this->rng()->NextBytes(values, sizeof(values));
TRACED_FOREACH(TypeParam, v1, values) {
TRACED_FOREACH(TypeParam, v2, values) {
if (e(v1, v2)) {
EXPECT_EQ(h(v1), h(v2));
}
}
}
}
TYPED_TEST(HashingTest, HashEqualsHashValue) {
for (int i = 0; i < 128; ++i) {
TypeParam v;
this->rng()->NextBytes(&v, sizeof(v));
hash<TypeParam> h;
EXPECT_EQ(h(v), hash_value(v));
}
}
TYPED_TEST(HashingTest, HashIsStateless) {
hash<TypeParam> h1, h2;
for (int i = 0; i < 128; ++i) {
TypeParam v;
this->rng()->NextBytes(&v, sizeof(v));
EXPECT_EQ(h1(v), h2(v));
}
}
TYPED_TEST(HashingTest, HashIsOkish) {
std::set<TypeParam> vs;
for (size_t i = 0; i < 128; ++i) {
TypeParam v;
this->rng()->NextBytes(&v, sizeof(v));
vs.insert(v);
}
std::set<size_t> hs;
for (const auto& v : vs) {
hash<TypeParam> h;
hs.insert(h(v));
}
EXPECT_LE(vs.size() / 4u, hs.size());
}
TYPED_TEST(HashingTest, HashValueArrayUsesHashRange) {
TypeParam values[128];
this->rng()->NextBytes(&values, sizeof(values));
EXPECT_EQ(hash_range(values, values + arraysize(values)), hash_value(values));
}
TYPED_TEST(HashingTest, BitEqualTo) {
bit_equal_to<TypeParam> pred;
for (size_t i = 0; i < 128; ++i) {
TypeParam v1, v2;
this->rng()->NextBytes(&v1, sizeof(v1));
this->rng()->NextBytes(&v2, sizeof(v2));
EXPECT_PRED2(pred, v1, v1);
EXPECT_PRED2(pred, v2, v2);
EXPECT_EQ(memcmp(&v1, &v2, sizeof(TypeParam)) == 0, pred(v1, v2));
}
}
TYPED_TEST(HashingTest, BitEqualToImpliesSameBitHash) {
bit_hash<TypeParam> h;
bit_equal_to<TypeParam> e;
TypeParam values[32];
this->rng()->NextBytes(&values, sizeof(values));
TRACED_FOREACH(TypeParam, v1, values) {
TRACED_FOREACH(TypeParam, v2, values) {
if (e(v1, v2)) {
EXPECT_EQ(h(v1), h(v2));
}
}
}
}
namespace {
struct Foo {
int x;
double y;
};
size_t hash_value(Foo const& v) { return hash_combine(v.x, v.y); }
} // namespace
TEST(HashingTest, HashUsesArgumentDependentLookup) {
const int kIntValues[] = {std::numeric_limits<int>::min(), -1, 0, 1, 42,
std::numeric_limits<int>::max()};
const double kDoubleValues[] = {
std::numeric_limits<double>::min(), -1, -0, 0, 1,
std::numeric_limits<double>::max()};
TRACED_FOREACH(int, x, kIntValues) {
TRACED_FOREACH(double, y, kDoubleValues) {
hash<Foo> h;
Foo foo = {x, y};
EXPECT_EQ(hash_combine(x, y), h(foo));
}
}
}
TEST(HashingTest, BitEqualToFloat) {
bit_equal_to<float> pred;
EXPECT_FALSE(pred(0.0f, -0.0f));
EXPECT_FALSE(pred(-0.0f, 0.0f));
float const qNaN = std::numeric_limits<float>::quiet_NaN();
float const sNaN = std::numeric_limits<float>::signaling_NaN();
EXPECT_PRED2(pred, qNaN, qNaN);
EXPECT_PRED2(pred, sNaN, sNaN);
}
TEST(HashingTest, BitHashFloatDifferentForZeroAndMinusZero) {
bit_hash<float> h;
EXPECT_NE(h(0.0f), h(-0.0f));
}
TEST(HashingTest, BitEqualToDouble) {
bit_equal_to<double> pred;
EXPECT_FALSE(pred(0.0, -0.0));
EXPECT_FALSE(pred(-0.0, 0.0));
double const qNaN = std::numeric_limits<double>::quiet_NaN();
double const sNaN = std::numeric_limits<double>::signaling_NaN();
EXPECT_PRED2(pred, qNaN, qNaN);
EXPECT_PRED2(pred, sNaN, sNaN);
}
TEST(HashingTest, BitHashDoubleDifferentForZeroAndMinusZero) {
bit_hash<double> h;
EXPECT_NE(h(0.0), h(-0.0));
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,174 @@
// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "src/base/hashmap.h"
#include <stdlib.h>
#include "src/base/overflowing-math.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace internal {
using HashmapTest = ::testing::Test;
using IntKeyHash = uint32_t (*)(uint32_t key);
class IntSet {
public:
explicit IntSet(IntKeyHash hash) : hash_(hash) {}
void Insert(int x) {
CHECK_NE(0, x); // 0 corresponds to (void*)nullptr - illegal key value
v8::base::HashMap::Entry* p =
map_.LookupOrInsert(reinterpret_cast<void*>(x), hash_(x));
CHECK_NOT_NULL(p); // insert is set!
CHECK_EQ(reinterpret_cast<void*>(x), p->key);
// we don't care about p->value
}
void Remove(int x) {
CHECK_NE(0, x); // 0 corresponds to (void*)nullptr - illegal key value
map_.Remove(reinterpret_cast<void*>(x), hash_(x));
}
bool Present(int x) {
v8::base::HashMap::Entry* p =
map_.Lookup(reinterpret_cast<void*>(x), hash_(x));
if (p != nullptr) {
CHECK_EQ(reinterpret_cast<void*>(x), p->key);
}
return p != nullptr;
}
void Clear() { map_.Clear(); }
uint32_t occupancy() const {
uint32_t count = 0;
for (v8::base::HashMap::Entry* p = map_.Start(); p != nullptr;
p = map_.Next(p)) {
count++;
}
CHECK_EQ(map_.occupancy(), static_cast<double>(count));
return count;
}
private:
IntKeyHash hash_;
v8::base::HashMap map_;
};
static uint32_t Hash(uint32_t key) { return 23; }
static uint32_t CollisionHash(uint32_t key) { return key & 0x3; }
void TestSet(IntKeyHash hash, int size) {
IntSet set(hash);
CHECK_EQ(0u, set.occupancy());
set.Insert(1);
set.Insert(2);
set.Insert(3);
CHECK_EQ(3u, set.occupancy());
set.Insert(2);
set.Insert(3);
CHECK_EQ(3u, set.occupancy());
CHECK(set.Present(1));
CHECK(set.Present(2));
CHECK(set.Present(3));
CHECK(!set.Present(4));
CHECK_EQ(3u, set.occupancy());
set.Remove(1);
CHECK(!set.Present(1));
CHECK(set.Present(2));
CHECK(set.Present(3));
CHECK_EQ(2u, set.occupancy());
set.Remove(3);
CHECK(!set.Present(1));
CHECK(set.Present(2));
CHECK(!set.Present(3));
CHECK_EQ(1u, set.occupancy());
set.Clear();
CHECK_EQ(0u, set.occupancy());
// Insert a long series of values.
const int start = 453;
const int factor = 13;
const int offset = 7;
const uint32_t n = size;
int x = start;
for (uint32_t i = 0; i < n; i++) {
CHECK_EQ(i, static_cast<double>(set.occupancy()));
set.Insert(x);
x = base::AddWithWraparound(base::MulWithWraparound(x, factor), offset);
}
CHECK_EQ(n, static_cast<double>(set.occupancy()));
// Verify the same sequence of values.
x = start;
for (uint32_t i = 0; i < n; i++) {
CHECK(set.Present(x));
x = base::AddWithWraparound(base::MulWithWraparound(x, factor), offset);
}
CHECK_EQ(n, static_cast<double>(set.occupancy()));
// Remove all these values.
x = start;
for (uint32_t i = 0; i < n; i++) {
CHECK_EQ(n - i, static_cast<double>(set.occupancy()));
CHECK(set.Present(x));
set.Remove(x);
CHECK(!set.Present(x));
x = base::AddWithWraparound(base::MulWithWraparound(x, factor), offset);
// Verify the the expected values are still there.
int y = start;
for (uint32_t j = 0; j < n; j++) {
if (j <= i) {
CHECK(!set.Present(y));
} else {
CHECK(set.Present(y));
}
y = base::AddWithWraparound(base::MulWithWraparound(y, factor), offset);
}
}
CHECK_EQ(0u, set.occupancy());
}
TEST_F(HashmapTest, HashSet) {
TestSet(Hash, 100);
TestSet(CollisionHash, 50);
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,576 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/ieee754.h"
#include <limits>
#include "src/base/overflowing-math.h"
#include "testing/gmock-support.h"
using testing::BitEq;
using testing::IsNaN;
namespace v8 {
namespace base {
namespace ieee754 {
namespace {
double const kE = 2.718281828459045;
double const kPI = 3.141592653589793;
double const kTwo120 = 1.329227995784916e+36;
double const kInfinity = std::numeric_limits<double>::infinity();
double const kQNaN = std::numeric_limits<double>::quiet_NaN();
double const kSNaN = std::numeric_limits<double>::signaling_NaN();
} // namespace
TEST(Ieee754, Acos) {
EXPECT_THAT(acos(kInfinity), IsNaN());
EXPECT_THAT(acos(-kInfinity), IsNaN());
EXPECT_THAT(acos(kQNaN), IsNaN());
EXPECT_THAT(acos(kSNaN), IsNaN());
EXPECT_EQ(0.0, acos(1.0));
}
TEST(Ieee754, Acosh) {
// Tests for acosh for exceptional values
EXPECT_EQ(kInfinity, acosh(kInfinity));
EXPECT_THAT(acosh(-kInfinity), IsNaN());
EXPECT_THAT(acosh(kQNaN), IsNaN());
EXPECT_THAT(acosh(kSNaN), IsNaN());
EXPECT_THAT(acosh(0.9), IsNaN());
// Test basic acosh functionality
EXPECT_EQ(0.0, acosh(1.0));
// acosh(1.5) = log((sqrt(5)+3)/2), case 1 < x < 2
EXPECT_EQ(0.9624236501192069e0, acosh(1.5));
// acosh(4) = log(sqrt(15)+4), case 2 < x < 2^28
EXPECT_EQ(2.0634370688955608e0, acosh(4.0));
// acosh(2^50), case 2^28 < x
EXPECT_EQ(35.35050620855721e0, acosh(1125899906842624.0));
// acosh(most-positive-float), no overflow
EXPECT_EQ(710.4758600739439e0, acosh(1.7976931348623157e308));
}
TEST(Ieee754, Asin) {
EXPECT_THAT(asin(kInfinity), IsNaN());
EXPECT_THAT(asin(-kInfinity), IsNaN());
EXPECT_THAT(asin(kQNaN), IsNaN());
EXPECT_THAT(asin(kSNaN), IsNaN());
EXPECT_THAT(asin(0.0), BitEq(0.0));
EXPECT_THAT(asin(-0.0), BitEq(-0.0));
}
TEST(Ieee754, Asinh) {
// Tests for asinh for exceptional values
EXPECT_EQ(kInfinity, asinh(kInfinity));
EXPECT_EQ(-kInfinity, asinh(-kInfinity));
EXPECT_THAT(asin(kQNaN), IsNaN());
EXPECT_THAT(asin(kSNaN), IsNaN());
// Test basic asinh functionality
EXPECT_THAT(asinh(0.0), BitEq(0.0));
EXPECT_THAT(asinh(-0.0), BitEq(-0.0));
// asinh(2^-29) = 2^-29, case |x| < 2^-28, where acosh(x) = x
EXPECT_EQ(1.862645149230957e-9, asinh(1.862645149230957e-9));
// asinh(-2^-29) = -2^-29, case |x| < 2^-28, where acosh(x) = x
EXPECT_EQ(-1.862645149230957e-9, asinh(-1.862645149230957e-9));
// asinh(2^-28), case 2 > |x| >= 2^-28
EXPECT_EQ(3.725290298461914e-9, asinh(3.725290298461914e-9));
// asinh(-2^-28), case 2 > |x| >= 2^-28
EXPECT_EQ(-3.725290298461914e-9, asinh(-3.725290298461914e-9));
// asinh(1), case 2 > |x| > 2^-28
EXPECT_EQ(0.881373587019543e0, asinh(1.0));
// asinh(-1), case 2 > |x| > 2^-28
EXPECT_EQ(-0.881373587019543e0, asinh(-1.0));
// asinh(5), case 2^28 > |x| > 2
EXPECT_EQ(2.3124383412727525e0, asinh(5.0));
// asinh(-5), case 2^28 > |x| > 2
EXPECT_EQ(-2.3124383412727525e0, asinh(-5.0));
// asinh(2^28), case 2^28 > |x|
EXPECT_EQ(20.101268236238415e0, asinh(268435456.0));
// asinh(-2^28), case 2^28 > |x|
EXPECT_EQ(-20.101268236238415e0, asinh(-268435456.0));
// asinh(<most-positive-float>), no overflow
EXPECT_EQ(710.4758600739439e0, asinh(1.7976931348623157e308));
// asinh(-<most-positive-float>), no overflow
EXPECT_EQ(-710.4758600739439e0, asinh(-1.7976931348623157e308));
}
TEST(Ieee754, Atan) {
EXPECT_THAT(atan(kQNaN), IsNaN());
EXPECT_THAT(atan(kSNaN), IsNaN());
EXPECT_THAT(atan(-0.0), BitEq(-0.0));
EXPECT_THAT(atan(0.0), BitEq(0.0));
EXPECT_DOUBLE_EQ(1.5707963267948966, atan(kInfinity));
EXPECT_DOUBLE_EQ(-1.5707963267948966, atan(-kInfinity));
}
TEST(Ieee754, Atan2) {
EXPECT_THAT(atan2(kQNaN, kQNaN), IsNaN());
EXPECT_THAT(atan2(kQNaN, kSNaN), IsNaN());
EXPECT_THAT(atan2(kSNaN, kQNaN), IsNaN());
EXPECT_THAT(atan2(kSNaN, kSNaN), IsNaN());
EXPECT_DOUBLE_EQ(0.7853981633974483, atan2(kInfinity, kInfinity));
EXPECT_DOUBLE_EQ(2.356194490192345, atan2(kInfinity, -kInfinity));
EXPECT_DOUBLE_EQ(-0.7853981633974483, atan2(-kInfinity, kInfinity));
EXPECT_DOUBLE_EQ(-2.356194490192345, atan2(-kInfinity, -kInfinity));
}
TEST(Ieee754, Atanh) {
EXPECT_THAT(atanh(kQNaN), IsNaN());
EXPECT_THAT(atanh(kSNaN), IsNaN());
EXPECT_THAT(atanh(kInfinity), IsNaN());
EXPECT_EQ(kInfinity, atanh(1));
EXPECT_EQ(-kInfinity, atanh(-1));
EXPECT_DOUBLE_EQ(0.54930614433405478, atanh(0.5));
}
#if defined(V8_USE_LIBM_TRIG_FUNCTIONS)
TEST(Ieee754, LibmCos) {
// Test values mentioned in the ECMAScript spec.
EXPECT_THAT(libm_cos(kQNaN), IsNaN());
EXPECT_THAT(libm_cos(kSNaN), IsNaN());
EXPECT_THAT(libm_cos(kInfinity), IsNaN());
EXPECT_THAT(libm_cos(-kInfinity), IsNaN());
// Tests for cos for |x| < pi/4
EXPECT_EQ(1.0, 1 / libm_cos(-0.0));
EXPECT_EQ(1.0, 1 / libm_cos(0.0));
// cos(x) = 1 for |x| < 2^-27
EXPECT_EQ(1, libm_cos(2.3283064365386963e-10));
EXPECT_EQ(1, libm_cos(-2.3283064365386963e-10));
// Test KERNELCOS for |x| < 0.3.
// cos(pi/20) = sqrt(sqrt(2)*sqrt(sqrt(5)+5)+4)/2^(3/2)
EXPECT_EQ(0.9876883405951378, libm_cos(0.15707963267948966));
// Test KERNELCOS for x ~= 0.78125
EXPECT_EQ(0.7100335477927638, libm_cos(0.7812504768371582));
EXPECT_EQ(0.7100338835660797, libm_cos(0.78125));
// Test KERNELCOS for |x| > 0.3.
// cos(pi/8) = sqrt(sqrt(2)+1)/2^(3/4)
EXPECT_EQ(0.9238795325112867, libm_cos(0.39269908169872414));
// Test KERNELTAN for |x| < 0.67434.
EXPECT_EQ(0.9238795325112867, libm_cos(-0.39269908169872414));
// Tests for cos.
EXPECT_EQ(1, libm_cos(3.725290298461914e-9));
// Cover different code paths in KERNELCOS.
EXPECT_EQ(0.9689124217106447, libm_cos(0.25));
EXPECT_EQ(0.8775825618903728, libm_cos(0.5));
EXPECT_EQ(0.7073882691671998, libm_cos(0.785));
// Test that cos(Math.PI/2) != 0 since Math.PI is not exact.
EXPECT_EQ(6.123233995736766e-17, libm_cos(1.5707963267948966));
// Test cos for various phases.
EXPECT_EQ(0.7071067811865474, libm_cos(7.0 / 4 * kPI));
EXPECT_EQ(0.7071067811865477, libm_cos(9.0 / 4 * kPI));
EXPECT_EQ(-0.7071067811865467, libm_cos(11.0 / 4 * kPI));
EXPECT_EQ(-0.7071067811865471, libm_cos(13.0 / 4 * kPI));
EXPECT_EQ(0.9367521275331447, libm_cos(1000000.0));
EXPECT_EQ(-3.435757038074824e-12, libm_cos(1048575.0 / 2 * kPI));
// Test Hayne-Panek reduction.
EXPECT_EQ(-0.9258790228548379e0, libm_cos(kTwo120));
EXPECT_EQ(-0.9258790228548379e0, libm_cos(-kTwo120));
}
TEST(Ieee754, LibmSin) {
// Test values mentioned in the ECMAScript spec.
EXPECT_THAT(libm_sin(kQNaN), IsNaN());
EXPECT_THAT(libm_sin(kSNaN), IsNaN());
EXPECT_THAT(libm_sin(kInfinity), IsNaN());
EXPECT_THAT(libm_sin(-kInfinity), IsNaN());
// Tests for sin for |x| < pi/4
EXPECT_EQ(-kInfinity, Divide(1.0, libm_sin(-0.0)));
EXPECT_EQ(kInfinity, Divide(1.0, libm_sin(0.0)));
// sin(x) = x for x < 2^-27
EXPECT_EQ(2.3283064365386963e-10, libm_sin(2.3283064365386963e-10));
EXPECT_EQ(-2.3283064365386963e-10, libm_sin(-2.3283064365386963e-10));
// sin(pi/8) = sqrt(sqrt(2)-1)/2^(3/4)
EXPECT_EQ(0.3826834323650898, libm_sin(0.39269908169872414));
EXPECT_EQ(-0.3826834323650898, libm_sin(-0.39269908169872414));
// Tests for sin.
EXPECT_EQ(0.479425538604203, libm_sin(0.5));
EXPECT_EQ(-0.479425538604203, libm_sin(-0.5));
EXPECT_EQ(1, libm_sin(kPI / 2.0));
EXPECT_EQ(-1, libm_sin(-kPI / 2.0));
// Test that sin(Math.PI) != 0 since Math.PI is not exact.
EXPECT_EQ(1.2246467991473532e-16, libm_sin(kPI));
EXPECT_EQ(-7.047032979958965e-14, libm_sin(2200.0 * kPI));
// Test sin for various phases.
EXPECT_EQ(-0.7071067811865477, libm_sin(7.0 / 4.0 * kPI));
EXPECT_EQ(0.7071067811865474, libm_sin(9.0 / 4.0 * kPI));
EXPECT_EQ(0.7071067811865483, libm_sin(11.0 / 4.0 * kPI));
EXPECT_EQ(-0.7071067811865479, libm_sin(13.0 / 4.0 * kPI));
EXPECT_EQ(-3.2103381051568376e-11, libm_sin(1048576.0 / 4 * kPI));
// Test Hayne-Panek reduction.
EXPECT_EQ(0.377820109360752e0, libm_sin(kTwo120));
EXPECT_EQ(-0.377820109360752e0, libm_sin(-kTwo120));
}
TEST(Ieee754, FdlibmCos) {
// Test values mentioned in the ECMAScript spec.
EXPECT_THAT(fdlibm_cos(kQNaN), IsNaN());
EXPECT_THAT(fdlibm_cos(kSNaN), IsNaN());
EXPECT_THAT(fdlibm_cos(kInfinity), IsNaN());
EXPECT_THAT(fdlibm_cos(-kInfinity), IsNaN());
// Tests for cos for |x| < pi/4
EXPECT_EQ(1.0, 1 / fdlibm_cos(-0.0));
EXPECT_EQ(1.0, 1 / fdlibm_cos(0.0));
// cos(x) = 1 for |x| < 2^-27
EXPECT_EQ(1, fdlibm_cos(2.3283064365386963e-10));
EXPECT_EQ(1, fdlibm_cos(-2.3283064365386963e-10));
// Test KERNELCOS for |x| < 0.3.
// cos(pi/20) = sqrt(sqrt(2)*sqrt(sqrt(5)+5)+4)/2^(3/2)
EXPECT_EQ(0.9876883405951378, fdlibm_cos(0.15707963267948966));
// Test KERNELCOS for x ~= 0.78125
EXPECT_EQ(0.7100335477927638, fdlibm_cos(0.7812504768371582));
EXPECT_EQ(0.7100338835660797, fdlibm_cos(0.78125));
// Test KERNELCOS for |x| > 0.3.
// cos(pi/8) = sqrt(sqrt(2)+1)/2^(3/4)
EXPECT_EQ(0.9238795325112867, fdlibm_cos(0.39269908169872414));
// Test KERNELTAN for |x| < 0.67434.
EXPECT_EQ(0.9238795325112867, fdlibm_cos(-0.39269908169872414));
// Tests for cos.
EXPECT_EQ(1, fdlibm_cos(3.725290298461914e-9));
// Cover different code paths in KERNELCOS.
EXPECT_EQ(0.9689124217106447, fdlibm_cos(0.25));
EXPECT_EQ(0.8775825618903728, fdlibm_cos(0.5));
EXPECT_EQ(0.7073882691671998, fdlibm_cos(0.785));
// Test that cos(Math.PI/2) != 0 since Math.PI is not exact.
EXPECT_EQ(6.123233995736766e-17, fdlibm_cos(1.5707963267948966));
// Test cos for various phases.
EXPECT_EQ(0.7071067811865474, fdlibm_cos(7.0 / 4 * kPI));
EXPECT_EQ(0.7071067811865477, fdlibm_cos(9.0 / 4 * kPI));
EXPECT_EQ(-0.7071067811865467, fdlibm_cos(11.0 / 4 * kPI));
EXPECT_EQ(-0.7071067811865471, fdlibm_cos(13.0 / 4 * kPI));
EXPECT_EQ(0.9367521275331447, fdlibm_cos(1000000.0));
EXPECT_EQ(-3.435757038074824e-12, fdlibm_cos(1048575.0 / 2 * kPI));
// Test Hayne-Panek reduction.
EXPECT_EQ(-0.9258790228548379e0, fdlibm_cos(kTwo120));
EXPECT_EQ(-0.9258790228548379e0, fdlibm_cos(-kTwo120));
}
TEST(Ieee754, FdlibmSin) {
// Test values mentioned in the ECMAScript spec.
EXPECT_THAT(fdlibm_sin(kQNaN), IsNaN());
EXPECT_THAT(fdlibm_sin(kSNaN), IsNaN());
EXPECT_THAT(fdlibm_sin(kInfinity), IsNaN());
EXPECT_THAT(fdlibm_sin(-kInfinity), IsNaN());
// Tests for sin for |x| < pi/4
EXPECT_EQ(-kInfinity, Divide(1.0, fdlibm_sin(-0.0)));
EXPECT_EQ(kInfinity, Divide(1.0, fdlibm_sin(0.0)));
// sin(x) = x for x < 2^-27
EXPECT_EQ(2.3283064365386963e-10, fdlibm_sin(2.3283064365386963e-10));
EXPECT_EQ(-2.3283064365386963e-10, fdlibm_sin(-2.3283064365386963e-10));
// sin(pi/8) = sqrt(sqrt(2)-1)/2^(3/4)
EXPECT_EQ(0.3826834323650898, fdlibm_sin(0.39269908169872414));
EXPECT_EQ(-0.3826834323650898, fdlibm_sin(-0.39269908169872414));
// Tests for sin.
EXPECT_EQ(0.479425538604203, fdlibm_sin(0.5));
EXPECT_EQ(-0.479425538604203, fdlibm_sin(-0.5));
EXPECT_EQ(1, fdlibm_sin(kPI / 2.0));
EXPECT_EQ(-1, fdlibm_sin(-kPI / 2.0));
// Test that sin(Math.PI) != 0 since Math.PI is not exact.
EXPECT_EQ(1.2246467991473532e-16, fdlibm_sin(kPI));
EXPECT_EQ(-7.047032979958965e-14, fdlibm_sin(2200.0 * kPI));
// Test sin for various phases.
EXPECT_EQ(-0.7071067811865477, fdlibm_sin(7.0 / 4.0 * kPI));
EXPECT_EQ(0.7071067811865474, fdlibm_sin(9.0 / 4.0 * kPI));
EXPECT_EQ(0.7071067811865483, fdlibm_sin(11.0 / 4.0 * kPI));
EXPECT_EQ(-0.7071067811865479, fdlibm_sin(13.0 / 4.0 * kPI));
EXPECT_EQ(-3.2103381051568376e-11, fdlibm_sin(1048576.0 / 4 * kPI));
// Test Hayne-Panek reduction.
EXPECT_EQ(0.377820109360752e0, fdlibm_sin(kTwo120));
EXPECT_EQ(-0.377820109360752e0, fdlibm_sin(-kTwo120));
}
#else
TEST(Ieee754, Cos) {
// Test values mentioned in the ECMAScript spec.
EXPECT_THAT(cos(kQNaN), IsNaN());
EXPECT_THAT(cos(kSNaN), IsNaN());
EXPECT_THAT(cos(kInfinity), IsNaN());
EXPECT_THAT(cos(-kInfinity), IsNaN());
// Tests for cos for |x| < pi/4
EXPECT_EQ(1.0, 1 / cos(-0.0));
EXPECT_EQ(1.0, 1 / cos(0.0));
// cos(x) = 1 for |x| < 2^-27
EXPECT_EQ(1, cos(2.3283064365386963e-10));
EXPECT_EQ(1, cos(-2.3283064365386963e-10));
// Test KERNELCOS for |x| < 0.3.
// cos(pi/20) = sqrt(sqrt(2)*sqrt(sqrt(5)+5)+4)/2^(3/2)
EXPECT_EQ(0.9876883405951378, cos(0.15707963267948966));
// Test KERNELCOS for x ~= 0.78125
EXPECT_EQ(0.7100335477927638, cos(0.7812504768371582));
EXPECT_EQ(0.7100338835660797, cos(0.78125));
// Test KERNELCOS for |x| > 0.3.
// cos(pi/8) = sqrt(sqrt(2)+1)/2^(3/4)
EXPECT_EQ(0.9238795325112867, cos(0.39269908169872414));
// Test KERNELTAN for |x| < 0.67434.
EXPECT_EQ(0.9238795325112867, cos(-0.39269908169872414));
// Tests for cos.
EXPECT_EQ(1, cos(3.725290298461914e-9));
// Cover different code paths in KERNELCOS.
EXPECT_EQ(0.9689124217106447, cos(0.25));
EXPECT_EQ(0.8775825618903728, cos(0.5));
EXPECT_EQ(0.7073882691671998, cos(0.785));
// Test that cos(Math.PI/2) != 0 since Math.PI is not exact.
EXPECT_EQ(6.123233995736766e-17, cos(1.5707963267948966));
// Test cos for various phases.
EXPECT_EQ(0.7071067811865474, cos(7.0 / 4 * kPI));
EXPECT_EQ(0.7071067811865477, cos(9.0 / 4 * kPI));
EXPECT_EQ(-0.7071067811865467, cos(11.0 / 4 * kPI));
EXPECT_EQ(-0.7071067811865471, cos(13.0 / 4 * kPI));
EXPECT_EQ(0.9367521275331447, cos(1000000.0));
EXPECT_EQ(-3.435757038074824e-12, cos(1048575.0 / 2 * kPI));
// Test Hayne-Panek reduction.
EXPECT_EQ(-0.9258790228548379e0, cos(kTwo120));
EXPECT_EQ(-0.9258790228548379e0, cos(-kTwo120));
}
TEST(Ieee754, Sin) {
// Test values mentioned in the ECMAScript spec.
EXPECT_THAT(sin(kQNaN), IsNaN());
EXPECT_THAT(sin(kSNaN), IsNaN());
EXPECT_THAT(sin(kInfinity), IsNaN());
EXPECT_THAT(sin(-kInfinity), IsNaN());
// Tests for sin for |x| < pi/4
EXPECT_EQ(-kInfinity, Divide(1.0, sin(-0.0)));
EXPECT_EQ(kInfinity, Divide(1.0, sin(0.0)));
// sin(x) = x for x < 2^-27
EXPECT_EQ(2.3283064365386963e-10, sin(2.3283064365386963e-10));
EXPECT_EQ(-2.3283064365386963e-10, sin(-2.3283064365386963e-10));
// sin(pi/8) = sqrt(sqrt(2)-1)/2^(3/4)
EXPECT_EQ(0.3826834323650898, sin(0.39269908169872414));
EXPECT_EQ(-0.3826834323650898, sin(-0.39269908169872414));
// Tests for sin.
EXPECT_EQ(0.479425538604203, sin(0.5));
EXPECT_EQ(-0.479425538604203, sin(-0.5));
EXPECT_EQ(1, sin(kPI / 2.0));
EXPECT_EQ(-1, sin(-kPI / 2.0));
// Test that sin(Math.PI) != 0 since Math.PI is not exact.
EXPECT_EQ(1.2246467991473532e-16, sin(kPI));
EXPECT_EQ(-7.047032979958965e-14, sin(2200.0 * kPI));
// Test sin for various phases.
EXPECT_EQ(-0.7071067811865477, sin(7.0 / 4.0 * kPI));
EXPECT_EQ(0.7071067811865474, sin(9.0 / 4.0 * kPI));
EXPECT_EQ(0.7071067811865483, sin(11.0 / 4.0 * kPI));
EXPECT_EQ(-0.7071067811865479, sin(13.0 / 4.0 * kPI));
EXPECT_EQ(-3.2103381051568376e-11, sin(1048576.0 / 4 * kPI));
// Test Hayne-Panek reduction.
EXPECT_EQ(0.377820109360752e0, sin(kTwo120));
EXPECT_EQ(-0.377820109360752e0, sin(-kTwo120));
}
#endif
TEST(Ieee754, Cosh) {
// Test values mentioned in the ECMAScript spec.
EXPECT_THAT(cosh(kQNaN), IsNaN());
EXPECT_THAT(cosh(kSNaN), IsNaN());
EXPECT_THAT(cosh(kInfinity), kInfinity);
EXPECT_THAT(cosh(-kInfinity), kInfinity);
EXPECT_EQ(1, cosh(0.0));
EXPECT_EQ(1, cosh(-0.0));
}
TEST(Ieee754, Exp) {
EXPECT_THAT(exp(kQNaN), IsNaN());
EXPECT_THAT(exp(kSNaN), IsNaN());
EXPECT_EQ(0.0, exp(-kInfinity));
EXPECT_EQ(0.0, exp(-1000));
EXPECT_EQ(0.0, exp(-745.1332191019412));
EXPECT_EQ(2.2250738585072626e-308, exp(-708.39641853226408));
EXPECT_EQ(3.307553003638408e-308, exp(-708.0));
EXPECT_EQ(4.9406564584124654e-324, exp(-7.45133219101941108420e+02));
EXPECT_EQ(0.36787944117144233, exp(-1.0));
EXPECT_EQ(1.0, exp(-0.0));
EXPECT_EQ(1.0, exp(0.0));
EXPECT_EQ(1.0, exp(2.2250738585072014e-308));
// Test that exp(x) is monotonic near 1.
EXPECT_GE(exp(1.0), exp(0.9999999999999999));
EXPECT_LE(exp(1.0), exp(1.0000000000000002));
// Test that we produce the correctly rounded result for 1.
EXPECT_EQ(kE, exp(1.0));
EXPECT_EQ(7.38905609893065e0, exp(2.0));
EXPECT_EQ(1.7976931348622732e308, exp(7.09782712893383973096e+02));
EXPECT_EQ(2.6881171418161356e+43, exp(100.0));
EXPECT_EQ(8.218407461554972e+307, exp(709.0));
EXPECT_EQ(1.7968190737295725e308, exp(709.7822265625e0));
EXPECT_EQ(kInfinity, exp(709.7827128933841e0));
EXPECT_EQ(kInfinity, exp(710.0));
EXPECT_EQ(kInfinity, exp(1000.0));
EXPECT_EQ(kInfinity, exp(kInfinity));
}
TEST(Ieee754, Expm1) {
EXPECT_THAT(expm1(kQNaN), IsNaN());
EXPECT_THAT(expm1(kSNaN), IsNaN());
EXPECT_EQ(-1.0, expm1(-kInfinity));
EXPECT_EQ(kInfinity, expm1(kInfinity));
EXPECT_EQ(0.0, expm1(-0.0));
EXPECT_EQ(0.0, expm1(0.0));
EXPECT_EQ(1.718281828459045, expm1(1.0));
EXPECT_EQ(2.6881171418161356e+43, expm1(100.0));
EXPECT_EQ(8.218407461554972e+307, expm1(709.0));
EXPECT_EQ(kInfinity, expm1(710.0));
}
TEST(Ieee754, Log) {
EXPECT_THAT(log(kQNaN), IsNaN());
EXPECT_THAT(log(kSNaN), IsNaN());
EXPECT_THAT(log(-kInfinity), IsNaN());
EXPECT_THAT(log(-1.0), IsNaN());
EXPECT_EQ(-kInfinity, log(-0.0));
EXPECT_EQ(-kInfinity, log(0.0));
EXPECT_EQ(0.0, log(1.0));
EXPECT_EQ(kInfinity, log(kInfinity));
// Test that log(E) produces the correctly rounded result.
EXPECT_EQ(1.0, log(kE));
}
TEST(Ieee754, Log1p) {
EXPECT_THAT(log1p(kQNaN), IsNaN());
EXPECT_THAT(log1p(kSNaN), IsNaN());
EXPECT_THAT(log1p(-kInfinity), IsNaN());
EXPECT_EQ(-kInfinity, log1p(-1.0));
EXPECT_EQ(0.0, log1p(0.0));
EXPECT_EQ(-0.0, log1p(-0.0));
EXPECT_EQ(kInfinity, log1p(kInfinity));
EXPECT_EQ(6.9756137364252422e-03, log1p(0.007));
EXPECT_EQ(709.782712893384, log1p(1.7976931348623157e308));
EXPECT_EQ(2.7755575615628914e-17, log1p(2.7755575615628914e-17));
EXPECT_EQ(9.313225741817976e-10, log1p(9.313225746154785e-10));
EXPECT_EQ(-0.2876820724517809, log1p(-0.25));
EXPECT_EQ(0.22314355131420976, log1p(0.25));
EXPECT_EQ(2.3978952727983707, log1p(10));
EXPECT_EQ(36.841361487904734, log1p(10e15));
EXPECT_EQ(37.08337388996168, log1p(12738099905822720));
EXPECT_EQ(37.08336444902049, log1p(12737979646738432));
EXPECT_EQ(1.3862943611198906, log1p(3));
EXPECT_EQ(1.3862945995384413, log1p(3 + 9.5367431640625e-7));
EXPECT_EQ(0.5596157879354227, log1p(0.75));
EXPECT_EQ(0.8109302162163288, log1p(1.25));
}
TEST(Ieee754, Log2) {
EXPECT_THAT(log2(kQNaN), IsNaN());
EXPECT_THAT(log2(kSNaN), IsNaN());
EXPECT_THAT(log2(-kInfinity), IsNaN());
EXPECT_THAT(log2(-1.0), IsNaN());
EXPECT_EQ(-kInfinity, log2(0.0));
EXPECT_EQ(-kInfinity, log2(-0.0));
EXPECT_EQ(kInfinity, log2(kInfinity));
}
TEST(Ieee754, Log10) {
EXPECT_THAT(log10(kQNaN), IsNaN());
EXPECT_THAT(log10(kSNaN), IsNaN());
EXPECT_THAT(log10(-kInfinity), IsNaN());
EXPECT_THAT(log10(-1.0), IsNaN());
EXPECT_EQ(-kInfinity, log10(0.0));
EXPECT_EQ(-kInfinity, log10(-0.0));
EXPECT_EQ(kInfinity, log10(kInfinity));
EXPECT_EQ(3.0, log10(1000.0));
EXPECT_EQ(14.0, log10(100000000000000)); // log10(10 ^ 14)
EXPECT_EQ(3.7389561269540406, log10(5482.2158));
EXPECT_EQ(14.661551142893833, log10(458723662312872.125782332587));
EXPECT_EQ(-0.9083828622192334, log10(0.12348583358871));
EXPECT_EQ(5.0, log10(100000.0));
}
TEST(Ieee754, Cbrt) {
EXPECT_THAT(cbrt(kQNaN), IsNaN());
EXPECT_THAT(cbrt(kSNaN), IsNaN());
EXPECT_EQ(kInfinity, cbrt(kInfinity));
EXPECT_EQ(-kInfinity, cbrt(-kInfinity));
EXPECT_EQ(1.4422495703074083, cbrt(3));
EXPECT_EQ(100, cbrt(100 * 100 * 100));
EXPECT_EQ(46.415888336127786, cbrt(100000));
}
TEST(Ieee754, Sinh) {
// Test values mentioned in the ECMAScript spec.
EXPECT_THAT(sinh(kQNaN), IsNaN());
EXPECT_THAT(sinh(kSNaN), IsNaN());
EXPECT_THAT(sinh(kInfinity), kInfinity);
EXPECT_THAT(sinh(-kInfinity), -kInfinity);
EXPECT_EQ(0.0, sinh(0.0));
EXPECT_EQ(-0.0, sinh(-0.0));
}
TEST(Ieee754, Tan) {
// Test values mentioned in the ECMAScript spec.
EXPECT_THAT(tan(kQNaN), IsNaN());
EXPECT_THAT(tan(kSNaN), IsNaN());
EXPECT_THAT(tan(kInfinity), IsNaN());
EXPECT_THAT(tan(-kInfinity), IsNaN());
// Tests for tan for |x| < pi/4
EXPECT_EQ(kInfinity, Divide(1.0, tan(0.0)));
EXPECT_EQ(-kInfinity, Divide(1.0, tan(-0.0)));
// tan(x) = x for |x| < 2^-28
EXPECT_EQ(2.3283064365386963e-10, tan(2.3283064365386963e-10));
EXPECT_EQ(-2.3283064365386963e-10, tan(-2.3283064365386963e-10));
// Test KERNELTAN for |x| > 0.67434.
EXPECT_EQ(0.8211418015898941, tan(11.0 / 16.0));
EXPECT_EQ(-0.8211418015898941, tan(-11.0 / 16.0));
EXPECT_EQ(0.41421356237309503, tan(0.39269908169872414));
// crbug/427468
EXPECT_EQ(0.7993357819992383, tan(0.6743358));
// Tests for tan.
EXPECT_EQ(3.725290298461914e-9, tan(3.725290298461914e-9));
// Test that tan(PI/2) != Infinity since PI is not exact.
EXPECT_EQ(1.633123935319537e16, tan(kPI / 2));
// Cover different code paths in KERNELTAN (tangent and cotangent)
EXPECT_EQ(0.5463024898437905, tan(0.5));
EXPECT_EQ(2.0000000000000027, tan(1.107148717794091));
EXPECT_EQ(-1.0000000000000004, tan(7.0 / 4.0 * kPI));
EXPECT_EQ(0.9999999999999994, tan(9.0 / 4.0 * kPI));
EXPECT_EQ(-6.420676210313675e-11, tan(1048576.0 / 2.0 * kPI));
EXPECT_EQ(2.910566692924059e11, tan(1048575.0 / 2.0 * kPI));
// Test Hayne-Panek reduction.
EXPECT_EQ(-0.40806638884180424e0, tan(kTwo120));
EXPECT_EQ(0.40806638884180424e0, tan(-kTwo120));
}
TEST(Ieee754, Tanh) {
// Test values mentioned in the ECMAScript spec.
EXPECT_THAT(tanh(kQNaN), IsNaN());
EXPECT_THAT(tanh(kSNaN), IsNaN());
EXPECT_THAT(tanh(kInfinity), 1);
EXPECT_THAT(tanh(-kInfinity), -1);
EXPECT_EQ(0.0, tanh(0.0));
EXPECT_EQ(-0.0, tanh(-0.0));
}
} // namespace ieee754
} // namespace base
} // namespace v8

View File

@ -0,0 +1,72 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/iterator.h"
#include <deque>
#include "test/unittests/test-utils.h"
namespace v8 {
namespace base {
TEST(IteratorTest, IteratorRangeEmpty) {
base::iterator_range<char*> r;
EXPECT_EQ(r.begin(), r.end());
EXPECT_EQ(r.end(), r.cend());
EXPECT_EQ(r.begin(), r.cbegin());
EXPECT_TRUE(r.empty());
EXPECT_EQ(0, r.size());
}
TEST(IteratorTest, IteratorRangeArray) {
int array[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
base::iterator_range<int*> r1(&array[0], &array[10]);
for (auto i : r1) {
EXPECT_EQ(array[i], i);
}
EXPECT_EQ(10, r1.size());
EXPECT_FALSE(r1.empty());
for (size_t i = 0; i < arraysize(array); ++i) {
EXPECT_EQ(r1[i], array[i]);
}
base::iterator_range<int*> r2(&array[0], &array[0]);
EXPECT_EQ(0, r2.size());
EXPECT_TRUE(r2.empty());
for (auto i : array) {
EXPECT_EQ(r2.end(), std::find(r2.begin(), r2.end(), i));
}
}
TEST(IteratorTest, IteratorRangeDeque) {
using C = std::deque<int>;
C c;
c.push_back(1);
c.push_back(2);
c.push_back(2);
base::iterator_range<typename C::iterator> r(c.begin(), c.end());
EXPECT_EQ(3, r.size());
EXPECT_FALSE(r.empty());
EXPECT_TRUE(c.begin() == r.begin());
EXPECT_TRUE(c.end() == r.end());
EXPECT_EQ(0, std::count(r.begin(), r.end(), 0));
EXPECT_EQ(1, std::count(r.begin(), r.end(), 1));
EXPECT_EQ(2, std::count(r.begin(), r.end(), 2));
}
TEST(IteratorTest, IteratorTypeDeduction) {
base::iterator_range<char*> r;
auto r2 = make_iterator_range(r.begin(), r.end());
EXPECT_EQ(r2.begin(), r.begin());
EXPECT_EQ(r2.end(), r2.end());
auto I = r.begin(), E = r.end();
// Check that this compiles and does the correct thing even if the iterators
// are lvalues:
auto r3 = make_iterator_range(I, E);
EXPECT_TRUE((std::is_same<decltype(r2), decltype(r3)>::value));
EXPECT_EQ(r3.begin(), r.begin());
EXPECT_EQ(r3.end(), r2.end());
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,370 @@
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/logging.h"
#include <cstdint>
#include "src/objects/smi.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace base {
namespace logging_unittest {
namespace {
#define CHECK_SUCCEED(NAME, lhs, rhs) \
{ \
std::string* error_message = \
Check##NAME##Impl<decltype(lhs), decltype(rhs)>((lhs), (rhs), ""); \
EXPECT_EQ(nullptr, error_message); \
}
#define CHECK_FAIL(NAME, lhs, rhs) \
{ \
std::string* error_message = \
Check##NAME##Impl<decltype(lhs), decltype(rhs)>((lhs), (rhs), ""); \
EXPECT_NE(nullptr, error_message); \
delete error_message; \
}
} // namespace
TEST(LoggingTest, CheckEQImpl) {
CHECK_SUCCEED(EQ, 0.0, 0.0);
CHECK_SUCCEED(EQ, 0.0, -0.0);
CHECK_SUCCEED(EQ, -0.0, 0.0);
CHECK_SUCCEED(EQ, -0.0, -0.0);
}
TEST(LoggingTest, CompareSignedMismatch) {
CHECK_SUCCEED(EQ, static_cast<int32_t>(14), static_cast<uint32_t>(14));
CHECK_FAIL(EQ, static_cast<int32_t>(14), static_cast<uint32_t>(15));
CHECK_FAIL(EQ, static_cast<int32_t>(-1), static_cast<uint32_t>(-1));
CHECK_SUCCEED(LT, static_cast<int32_t>(-1), static_cast<uint32_t>(0));
CHECK_SUCCEED(LT, static_cast<int32_t>(-1), static_cast<uint32_t>(-1));
CHECK_SUCCEED(LE, static_cast<int32_t>(-1), static_cast<uint32_t>(0));
CHECK_SUCCEED(LE, static_cast<int32_t>(55), static_cast<uint32_t>(55));
CHECK_SUCCEED(LT, static_cast<int32_t>(55),
static_cast<uint32_t>(0x7FFFFF00));
CHECK_SUCCEED(LE, static_cast<int32_t>(55),
static_cast<uint32_t>(0x7FFFFF00));
CHECK_SUCCEED(GE, static_cast<uint32_t>(0x7FFFFF00),
static_cast<int32_t>(55));
CHECK_SUCCEED(GT, static_cast<uint32_t>(0x7FFFFF00),
static_cast<int32_t>(55));
CHECK_SUCCEED(GT, static_cast<uint32_t>(-1), static_cast<int32_t>(-1));
CHECK_SUCCEED(GE, static_cast<uint32_t>(0), static_cast<int32_t>(-1));
CHECK_SUCCEED(LT, static_cast<int8_t>(-1), static_cast<uint32_t>(0));
CHECK_SUCCEED(GT, static_cast<uint64_t>(0x7F01010101010101), 0);
CHECK_SUCCEED(LE, static_cast<int64_t>(0xFF01010101010101),
static_cast<uint8_t>(13));
}
TEST(LoggingTest, CompareAgainstStaticConstPointer) {
// These used to produce link errors before http://crrev.com/2524093002.
CHECK_FAIL(EQ, v8::internal::Smi::zero(), v8::internal::Smi::FromInt(17));
CHECK_SUCCEED(GT, 0, v8::internal::Smi::kMinValue);
}
#define CHECK_BOTH(name, lhs, rhs) \
CHECK_##name(lhs, rhs); \
DCHECK_##name(lhs, rhs)
namespace {
std::string SanitizeRegexp(std::string msg) {
size_t last_pos = 0;
do {
size_t pos = msg.find_first_of("(){}+*", last_pos);
if (pos == std::string::npos) break;
msg.insert(pos, "\\");
last_pos = pos + 2;
} while (true);
return msg;
}
std::string FailureMessage(std::string msg) {
#if !defined(DEBUG) && defined(OFFICIAL_BUILD)
// Official release builds strip all fatal messages for saving binary size,
// see src/base/logging.h.
USE(SanitizeRegexp);
return "";
#else
return SanitizeRegexp(msg);
#endif
}
std::string FailureMessage(const char* msg, const char* lhs, const char* rhs) {
#ifdef DEBUG
return SanitizeRegexp(
std::string{msg}.append(" (").append(lhs).append(" vs. ").append(rhs));
#else
return FailureMessage(msg);
#endif
}
std::string LongFailureMessage(const char* msg, const char* lhs,
const char* rhs) {
#ifdef DEBUG
return SanitizeRegexp(std::string{msg}
.append("\n ")
.append(lhs)
.append("\n vs.\n ")
.append(rhs));
#else
return FailureMessage(msg, lhs, rhs);
#endif
}
} // namespace
TEST(LoggingTest, CompareWithDifferentSignedness) {
int32_t i32 = 10;
uint32_t u32 = 20;
int64_t i64 = 30;
uint64_t u64 = 40;
// All these checks should compile (!) and succeed.
CHECK_BOTH(EQ, i32 + 10, u32);
CHECK_BOTH(LT, i32, u64);
CHECK_BOTH(LE, u32, i64);
CHECK_BOTH(IMPLIES, i32, i64);
CHECK_BOTH(IMPLIES, u32, i64);
CHECK_BOTH(IMPLIES, !u32, !i64);
// Check that the values are output correctly on error.
ASSERT_DEATH_IF_SUPPORTED(
([&] { CHECK_GT(i32, u64); })(),
FailureMessage("Check failed: i32 > u64", "10", "40"));
}
TEST(LoggingTest, CompareWithReferenceType) {
int32_t i32 = 10;
uint32_t u32 = 20;
int64_t i64 = 30;
uint64_t u64 = 40;
// All these checks should compile (!) and succeed.
CHECK_BOTH(EQ, i32 + 10, *&u32);
CHECK_BOTH(LT, *&i32, u64);
CHECK_BOTH(IMPLIES, *&i32, i64);
CHECK_BOTH(IMPLIES, *&i32, u64);
// Check that the values are output correctly on error.
ASSERT_DEATH_IF_SUPPORTED(
([&] { CHECK_GT(*&i32, u64); })(),
FailureMessage("Check failed: *&i32 > u64", "10", "40"));
}
enum TestEnum1 { ONE, TWO };
enum TestEnum2 : uint16_t { FOO = 14, BAR = 5 };
enum class TestEnum3 { A, B };
enum class TestEnum4 : uint8_t { FIRST, SECOND };
TEST(LoggingTest, CompareEnumTypes) {
// All these checks should compile (!) and succeed.
CHECK_BOTH(EQ, ONE, ONE);
CHECK_BOTH(LT, ONE, TWO);
CHECK_BOTH(EQ, BAR, 5);
CHECK_BOTH(LT, BAR, FOO);
CHECK_BOTH(EQ, TestEnum3::A, TestEnum3::A);
CHECK_BOTH(LT, TestEnum3::A, TestEnum3::B);
CHECK_BOTH(EQ, TestEnum4::FIRST, TestEnum4::FIRST);
CHECK_BOTH(LT, TestEnum4::FIRST, TestEnum4::SECOND);
}
class TestClass1 {
public:
bool operator==(const TestClass1&) const { return true; }
bool operator!=(const TestClass1&) const { return false; }
};
class TestClass2 {
public:
explicit TestClass2(int val) : val_(val) {}
bool operator<(const TestClass2& other) const { return val_ < other.val_; }
int val() const { return val_; }
private:
int val_;
};
std::ostream& operator<<(std::ostream& str, const TestClass2& val) {
return str << "TestClass2(" << val.val() << ")";
}
TEST(LoggingTest, CompareClassTypes) {
// All these checks should compile (!) and succeed.
CHECK_BOTH(EQ, TestClass1{}, TestClass1{});
CHECK_BOTH(LT, TestClass2{2}, TestClass2{7});
// Check that the values are output correctly on error.
ASSERT_DEATH_IF_SUPPORTED(
([&] { CHECK_NE(TestClass1{}, TestClass1{}); })(),
FailureMessage("Check failed: TestClass1{} != TestClass1{}",
"<unprintable>", "<unprintable>"));
ASSERT_DEATH_IF_SUPPORTED(
([&] { CHECK_LT(TestClass2{4}, TestClass2{3}); })(),
FailureMessage("Check failed: TestClass2{4} < TestClass2{3}",
"TestClass2(4)", "TestClass2(3)"));
}
TEST(LoggingDeathTest, OutputEnumValues) {
ASSERT_DEATH_IF_SUPPORTED(
([&] { CHECK_EQ(ONE, TWO); })(),
FailureMessage("Check failed: ONE == TWO", "0", "1"));
ASSERT_DEATH_IF_SUPPORTED(
([&] { CHECK_NE(BAR, 2 + 3); })(),
FailureMessage("Check failed: BAR != 2 + 3", "5", "5"));
ASSERT_DEATH_IF_SUPPORTED(
([&] { CHECK_EQ(TestEnum3::A, TestEnum3::B); })(),
FailureMessage("Check failed: TestEnum3::A == TestEnum3::B", "0", "1"));
ASSERT_DEATH_IF_SUPPORTED(
([&] { CHECK_GE(TestEnum4::FIRST, TestEnum4::SECOND); })(),
FailureMessage("Check failed: TestEnum4::FIRST >= TestEnum4::SECOND", "0",
"1"));
}
enum TestEnum5 { TEST_A, TEST_B };
enum class TestEnum6 { TEST_C, TEST_D };
std::ostream& operator<<(std::ostream& str, TestEnum5 val) {
return str << (val == TEST_A ? "A" : "B");
}
void operator<<(std::ostream& str, TestEnum6 val) {
str << (val == TestEnum6::TEST_C ? "C" : "D");
}
TEST(LoggingDeathTest, OutputEnumWithOutputOperator) {
ASSERT_DEATH_IF_SUPPORTED(
([&] { CHECK_EQ(TEST_A, TEST_B); })(),
FailureMessage("Check failed: TEST_A == TEST_B", "A (0)", "B (1)"));
ASSERT_DEATH_IF_SUPPORTED(
([&] { CHECK_GE(TestEnum6::TEST_C, TestEnum6::TEST_D); })(),
FailureMessage("Check failed: TestEnum6::TEST_C >= TestEnum6::TEST_D",
"C (0)", "D (1)"));
}
enum TestEnum7 : uint8_t { A = 2, B = 7 };
enum class TestEnum8 : int8_t { A, B };
TEST(LoggingDeathTest, OutputSingleCharEnum) {
ASSERT_DEATH_IF_SUPPORTED(
([&] { CHECK_EQ(TestEnum7::A, TestEnum7::B); })(),
FailureMessage("Check failed: TestEnum7::A == TestEnum7::B", "2", "7"));
ASSERT_DEATH_IF_SUPPORTED(
([&] { CHECK_GT(TestEnum7::A, TestEnum7::B); })(),
FailureMessage("Check failed: TestEnum7::A > TestEnum7::B", "2", "7"));
ASSERT_DEATH_IF_SUPPORTED(
([&] { CHECK_GE(TestEnum8::A, TestEnum8::B); })(),
FailureMessage("Check failed: TestEnum8::A >= TestEnum8::B", "0", "1"));
}
TEST(LoggingDeathTest, OutputLongValues) {
constexpr size_t kMaxInlineLength = 50; // see logging.h
std::string str1;
while (str1.length() < kMaxInlineLength) {
str1.push_back('a' + (str1.length() % 26));
}
std::string str2("abc");
ASSERT_DEATH_IF_SUPPORTED(
([&] { CHECK_EQ(str1, str2); })(),
FailureMessage("Check failed: str1 == str2",
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwx",
"abc"));
str1.push_back('X');
ASSERT_DEATH_IF_SUPPORTED(
([&] { CHECK_EQ(str1, str2); })(),
LongFailureMessage("Check failed: str1 == str2",
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxX",
"abc"));
}
TEST(LoggingDeathTest, FatalKills) {
ASSERT_DEATH_IF_SUPPORTED(FATAL("Dread pirate"),
FailureMessage("Dread pirate"));
}
TEST(LoggingDeathTest, DcheckIsOnlyFatalInDebug) {
#ifdef DEBUG
ASSERT_DEATH_IF_SUPPORTED(DCHECK(false && "Dread pirate"), "Dread pirate");
#else
// DCHECK should be non-fatal if DEBUG is undefined.
DCHECK(false && "I'm a benign teapot");
#endif
}
namespace {
void DcheckOverrideFunction(const char*, int, const char*) {}
} // namespace
TEST(LoggingDeathTest, V8_DcheckCanBeOverridden) {
// Default DCHECK state should be fatal.
ASSERT_DEATH_IF_SUPPORTED(V8_Dcheck(__FILE__, __LINE__, "Dread pirate"),
"Dread pirate");
ASSERT_DEATH_IF_SUPPORTED(
{
v8::base::SetDcheckFunction(&DcheckOverrideFunction);
// This should be non-fatal.
V8_Dcheck(__FILE__, __LINE__, "I'm a benign teapot.");
// Restore default behavior, and assert on lethality.
v8::base::SetDcheckFunction(nullptr);
V8_Dcheck(__FILE__, __LINE__, "Dread pirate");
},
"Dread pirate");
}
#if defined(DEBUG)
namespace {
int g_log_sink_call_count = 0;
void DcheckCountFunction(const char* file, int line, const char* message) {
++g_log_sink_call_count;
}
void DcheckEmptyFunction1() {
// Provide a body so that Release builds do not cause the compiler to
// optimize DcheckEmptyFunction1 and DcheckEmptyFunction2 as a single
// function, which breaks the Dcheck tests below.
// Note that this function is never actually called.
g_log_sink_call_count += 42;
}
void DcheckEmptyFunction2() {}
} // namespace
TEST(LoggingTest, LogFunctionPointers) {
v8::base::SetDcheckFunction(&DcheckCountFunction);
g_log_sink_call_count = 0;
void (*fp1)() = DcheckEmptyFunction1;
void (*fp2)() = DcheckEmptyFunction2;
void (*fp3)() = DcheckEmptyFunction1;
DCHECK_EQ(fp1, DcheckEmptyFunction1);
DCHECK_EQ(fp1, fp3);
EXPECT_EQ(0, g_log_sink_call_count);
DCHECK_EQ(fp1, fp2);
EXPECT_EQ(1, g_log_sink_call_count);
std::string* error_message =
CheckEQImpl<decltype(fp1), decltype(fp2)>(fp1, fp2, "");
EXPECT_NE(*error_message, "(1 vs 1)");
delete error_message;
}
#endif // defined(DEBUG)
TEST(LoggingDeathTest, CheckChars) {
ASSERT_DEATH_IF_SUPPORTED(
([&] { CHECK_EQ('a', 'b'); })(),
FailureMessage("Check failed: 'a' == 'b'", "'97'", "'98'"));
}
TEST(LoggingDeathTest, Collections) {
std::vector<int> listA{1};
std::vector<int> listB{1, 2};
ASSERT_DEATH_IF_SUPPORTED(
([&] { CHECK_EQ(listA, listB); })(),
FailureMessage("Check failed: listA == listB", "{1}", "{1,2}"));
}
} // namespace logging_unittest
} // namespace base
} // namespace v8

View File

@ -0,0 +1,61 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/macros.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace base {
TEST(AlignedAddressTest, AlignedAddress) {
EXPECT_EQ(reinterpret_cast<void*>(0xFFFF0),
AlignedAddress(reinterpret_cast<void*>(0xFFFF0), 16));
EXPECT_EQ(reinterpret_cast<void*>(0xFFFF0),
AlignedAddress(reinterpret_cast<void*>(0xFFFF2), 16));
EXPECT_EQ(reinterpret_cast<void*>(0xFFFF0),
AlignedAddress(reinterpret_cast<void*>(0xFFFF2), 16));
EXPECT_EQ(reinterpret_cast<void*>(0xFFFF0),
AlignedAddress(reinterpret_cast<void*>(0xFFFFF), 16));
EXPECT_EQ(reinterpret_cast<void*>(0x0),
AlignedAddress(reinterpret_cast<void*>(0xFFFFF), 0x100000));
}
struct TriviallyCopyable {
const int i;
};
ASSERT_TRIVIALLY_COPYABLE(TriviallyCopyable);
struct StillTriviallyCopyable {
const int i;
StillTriviallyCopyable(const StillTriviallyCopyable&) = delete;
};
ASSERT_TRIVIALLY_COPYABLE(StillTriviallyCopyable);
struct NonTrivialDestructor {
~NonTrivialDestructor() {}
};
ASSERT_NOT_TRIVIALLY_COPYABLE(NonTrivialDestructor);
struct NonTrivialCopyConstructor {
NonTrivialCopyConstructor(const NonTrivialCopyConstructor&) {}
};
ASSERT_NOT_TRIVIALLY_COPYABLE(NonTrivialCopyConstructor);
struct NonTrivialMoveConstructor {
NonTrivialMoveConstructor(const NonTrivialMoveConstructor&) {}
};
ASSERT_NOT_TRIVIALLY_COPYABLE(NonTrivialMoveConstructor);
struct NonTrivialCopyAssignment {
NonTrivialCopyAssignment(const NonTrivialCopyAssignment&) {}
};
ASSERT_NOT_TRIVIALLY_COPYABLE(NonTrivialCopyAssignment);
struct NonTrivialMoveAssignment {
NonTrivialMoveAssignment(const NonTrivialMoveAssignment&) {}
};
ASSERT_NOT_TRIVIALLY_COPYABLE(NonTrivialMoveAssignment);
} // namespace base
} // namespace v8

View File

@ -0,0 +1,67 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/utils/ostreams.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace internal {
TEST(Ostream, AsHex) {
auto testAsHex = [](const char* expected, const AsHex& value) {
std::ostringstream out;
out << value;
std::string result = out.str();
EXPECT_EQ(expected, result);
EXPECT_TRUE(result == expected)
<< "\nexpected: " << expected << "\ngot: " << result << "\n";
};
testAsHex("0", AsHex(0));
testAsHex("", AsHex(0, 0));
testAsHex("0x", AsHex(0, 0, true));
testAsHex("0x0", AsHex(0, 1, true));
testAsHex("0x00", AsHex(0, 2, true));
testAsHex("123", AsHex(0x123, 0));
testAsHex("0123", AsHex(0x123, 4));
testAsHex("0x123", AsHex(0x123, 0, true));
testAsHex("0x123", AsHex(0x123, 3, true));
testAsHex("0x0123", AsHex(0x123, 4, true));
testAsHex("0x00000123", AsHex(0x123, 8, true));
}
TEST(Ostream, AsHexBytes) {
auto testAsHexBytes = [](const char* expected, const AsHexBytes& value) {
std::ostringstream out;
out << value;
std::string result = out.str();
EXPECT_EQ(expected, result);
};
// Little endian (default):
testAsHexBytes("00", AsHexBytes(0));
testAsHexBytes("", AsHexBytes(0, 0));
testAsHexBytes("23 01", AsHexBytes(0x123));
testAsHexBytes("23 01", AsHexBytes(0x123, 1));
testAsHexBytes("23 01", AsHexBytes(0x123, 2));
testAsHexBytes("23 01 00", AsHexBytes(0x123, 3));
testAsHexBytes("ff ff ff ff", AsHexBytes(0xFFFFFFFF));
testAsHexBytes("00 00 00 00", AsHexBytes(0, 4));
testAsHexBytes("56 34 12", AsHexBytes(0x123456));
// Big endian:
testAsHexBytes("00", AsHexBytes(0, 1, AsHexBytes::kBigEndian));
testAsHexBytes("", AsHexBytes(0, 0, AsHexBytes::kBigEndian));
testAsHexBytes("01 23", AsHexBytes(0x123, 1, AsHexBytes::kBigEndian));
testAsHexBytes("01 23", AsHexBytes(0x123, 1, AsHexBytes::kBigEndian));
testAsHexBytes("01 23", AsHexBytes(0x123, 2, AsHexBytes::kBigEndian));
testAsHexBytes("00 01 23", AsHexBytes(0x123, 3, AsHexBytes::kBigEndian));
testAsHexBytes("ff ff ff ff", AsHexBytes(0xFFFFFFFF, AsHexBytes::kBigEndian));
testAsHexBytes("00 00 00 00", AsHexBytes(0, 4, AsHexBytes::kBigEndian));
testAsHexBytes("12 34 56", AsHexBytes(0x123456, 1, AsHexBytes::kBigEndian));
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,299 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/platform/condition-variable.h"
#include "src/base/platform/platform.h"
#include "src/base/platform/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace base {
TEST(ConditionVariable, WaitForAfterNofityOnSameThread) {
for (int n = 0; n < 10; ++n) {
Mutex mutex;
ConditionVariable cv;
MutexGuard lock_guard(&mutex);
cv.NotifyOne();
EXPECT_FALSE(cv.WaitFor(&mutex, TimeDelta::FromMicroseconds(n)));
cv.NotifyAll();
EXPECT_FALSE(cv.WaitFor(&mutex, TimeDelta::FromMicroseconds(n)));
}
}
namespace {
class ThreadWithMutexAndConditionVariable final : public Thread {
public:
ThreadWithMutexAndConditionVariable()
: Thread(Options("ThreadWithMutexAndConditionVariable")),
running_(false),
finished_(false) {}
void Run() override {
MutexGuard lock_guard(&mutex_);
running_ = true;
cv_.NotifyOne();
while (running_) {
cv_.Wait(&mutex_);
}
finished_ = true;
cv_.NotifyAll();
}
bool running_;
bool finished_;
ConditionVariable cv_;
Mutex mutex_;
};
} // namespace
TEST(ConditionVariable, MultipleThreadsWithSeparateConditionVariables) {
static const int kThreadCount = 128;
ThreadWithMutexAndConditionVariable threads[kThreadCount];
for (int n = 0; n < kThreadCount; ++n) {
MutexGuard lock_guard(&threads[n].mutex_);
EXPECT_FALSE(threads[n].running_);
EXPECT_FALSE(threads[n].finished_);
CHECK(threads[n].Start());
// Wait for nth thread to start.
while (!threads[n].running_) {
threads[n].cv_.Wait(&threads[n].mutex_);
}
}
for (int n = kThreadCount - 1; n >= 0; --n) {
MutexGuard lock_guard(&threads[n].mutex_);
EXPECT_TRUE(threads[n].running_);
EXPECT_FALSE(threads[n].finished_);
}
for (int n = 0; n < kThreadCount; ++n) {
MutexGuard lock_guard(&threads[n].mutex_);
EXPECT_TRUE(threads[n].running_);
EXPECT_FALSE(threads[n].finished_);
// Tell the nth thread to quit.
threads[n].running_ = false;
threads[n].cv_.NotifyOne();
}
for (int n = kThreadCount - 1; n >= 0; --n) {
// Wait for nth thread to quit.
MutexGuard lock_guard(&threads[n].mutex_);
while (!threads[n].finished_) {
threads[n].cv_.Wait(&threads[n].mutex_);
}
EXPECT_FALSE(threads[n].running_);
EXPECT_TRUE(threads[n].finished_);
}
for (int n = 0; n < kThreadCount; ++n) {
threads[n].Join();
MutexGuard lock_guard(&threads[n].mutex_);
EXPECT_FALSE(threads[n].running_);
EXPECT_TRUE(threads[n].finished_);
}
}
namespace {
class ThreadWithSharedMutexAndConditionVariable final : public Thread {
public:
ThreadWithSharedMutexAndConditionVariable()
: Thread(Options("ThreadWithSharedMutexAndConditionVariable")),
running_(false),
finished_(false),
cv_(nullptr),
mutex_(nullptr) {}
void Run() override {
MutexGuard lock_guard(mutex_);
running_ = true;
cv_->NotifyAll();
while (running_) {
cv_->Wait(mutex_);
}
finished_ = true;
cv_->NotifyAll();
}
bool running_;
bool finished_;
ConditionVariable* cv_;
Mutex* mutex_;
};
} // namespace
TEST(ConditionVariable, MultipleThreadsWithSharedSeparateConditionVariables) {
static const int kThreadCount = 128;
ThreadWithSharedMutexAndConditionVariable threads[kThreadCount];
ConditionVariable cv;
Mutex mutex;
for (int n = 0; n < kThreadCount; ++n) {
threads[n].mutex_ = &mutex;
threads[n].cv_ = &cv;
}
// Start all threads.
{
MutexGuard lock_guard(&mutex);
for (int n = 0; n < kThreadCount; ++n) {
EXPECT_FALSE(threads[n].running_);
EXPECT_FALSE(threads[n].finished_);
CHECK(threads[n].Start());
}
}
// Wait for all threads to start.
{
MutexGuard lock_guard(&mutex);
for (int n = kThreadCount - 1; n >= 0; --n) {
while (!threads[n].running_) {
cv.Wait(&mutex);
}
}
}
// Make sure that all threads are running.
{
MutexGuard lock_guard(&mutex);
for (int n = 0; n < kThreadCount; ++n) {
EXPECT_TRUE(threads[n].running_);
EXPECT_FALSE(threads[n].finished_);
}
}
// Tell all threads to quit.
{
MutexGuard lock_guard(&mutex);
for (int n = kThreadCount - 1; n >= 0; --n) {
EXPECT_TRUE(threads[n].running_);
EXPECT_FALSE(threads[n].finished_);
// Tell the nth thread to quit.
threads[n].running_ = false;
}
cv.NotifyAll();
}
// Wait for all threads to quit.
{
MutexGuard lock_guard(&mutex);
for (int n = 0; n < kThreadCount; ++n) {
while (!threads[n].finished_) {
cv.Wait(&mutex);
}
}
}
// Make sure all threads are finished.
{
MutexGuard lock_guard(&mutex);
for (int n = kThreadCount - 1; n >= 0; --n) {
EXPECT_FALSE(threads[n].running_);
EXPECT_TRUE(threads[n].finished_);
}
}
// Join all threads.
for (int n = 0; n < kThreadCount; ++n) {
threads[n].Join();
}
}
namespace {
class LoopIncrementThread final : public Thread {
public:
LoopIncrementThread(int rem, int* counter, int limit, int thread_count,
ConditionVariable* cv, Mutex* mutex)
: Thread(Options("LoopIncrementThread")),
rem_(rem),
counter_(counter),
limit_(limit),
thread_count_(thread_count),
cv_(cv),
mutex_(mutex) {
EXPECT_LT(rem, thread_count);
EXPECT_EQ(0, limit % thread_count);
}
void Run() override {
int last_count = -1;
while (true) {
MutexGuard lock_guard(mutex_);
int count = *counter_;
while (count % thread_count_ != rem_ && count < limit_) {
cv_->Wait(mutex_);
count = *counter_;
}
if (count >= limit_) break;
EXPECT_EQ(*counter_, count);
if (last_count != -1) {
EXPECT_EQ(last_count + (thread_count_ - 1), count);
}
count++;
*counter_ = count;
last_count = count;
cv_->NotifyAll();
}
}
private:
const int rem_;
int* counter_;
const int limit_;
const int thread_count_;
ConditionVariable* cv_;
Mutex* mutex_;
};
} // namespace
TEST(ConditionVariable, LoopIncrement) {
static const int kMaxThreadCount = 16;
Mutex mutex;
ConditionVariable cv;
for (int thread_count = 1; thread_count < kMaxThreadCount; ++thread_count) {
int limit = thread_count * 10;
int counter = 0;
// Setup the threads.
Thread** threads = new Thread* [thread_count];
for (int n = 0; n < thread_count; ++n) {
threads[n] = new LoopIncrementThread(n, &counter, limit, thread_count,
&cv, &mutex);
}
// Start all threads.
for (int n = thread_count - 1; n >= 0; --n) {
CHECK(threads[n]->Start());
}
// Join and cleanup all threads.
for (int n = 0; n < thread_count; ++n) {
threads[n]->Join();
delete threads[n];
}
delete[] threads;
EXPECT_EQ(limit, counter);
}
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,100 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/platform/mutex.h"
#include <chrono> // NOLINT(build/c++11)
#include <queue>
#include <thread> // NOLINT(build/c++11)
#include "src/base/platform/condition-variable.h"
#include "src/base/platform/platform.h"
#include "src/base/utils/random-number-generator.h"
#include "test/unittests/fuzztest.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace base {
TEST(Mutex, LockGuardMutex) {
Mutex mutex;
{ MutexGuard lock_guard(&mutex); }
{ MutexGuard lock_guard(&mutex); }
}
TEST(Mutex, LockGuardRecursiveMutex) {
RecursiveMutex recursive_mutex;
{ LockGuard<RecursiveMutex> lock_guard(&recursive_mutex); }
{
LockGuard<RecursiveMutex> lock_guard1(&recursive_mutex);
LockGuard<RecursiveMutex> lock_guard2(&recursive_mutex);
}
}
TEST(Mutex, LockGuardLazyMutex) {
LazyMutex lazy_mutex = LAZY_MUTEX_INITIALIZER;
{ MutexGuard lock_guard(lazy_mutex.Pointer()); }
{ MutexGuard lock_guard(lazy_mutex.Pointer()); }
}
TEST(Mutex, LockGuardLazyRecursiveMutex) {
LazyRecursiveMutex lazy_recursive_mutex = LAZY_RECURSIVE_MUTEX_INITIALIZER;
{ LockGuard<RecursiveMutex> lock_guard(lazy_recursive_mutex.Pointer()); }
{
LockGuard<RecursiveMutex> lock_guard1(lazy_recursive_mutex.Pointer());
LockGuard<RecursiveMutex> lock_guard2(lazy_recursive_mutex.Pointer());
}
}
TEST(Mutex, MultipleMutexes) {
Mutex mutex1;
Mutex mutex2;
Mutex mutex3;
// Order 1
mutex1.Lock();
mutex2.Lock();
mutex3.Lock();
mutex1.Unlock();
mutex2.Unlock();
mutex3.Unlock();
// Order 2
mutex1.Lock();
mutex2.Lock();
mutex3.Lock();
mutex3.Unlock();
mutex2.Unlock();
mutex1.Unlock();
}
TEST(Mutex, MultipleRecursiveMutexes) {
RecursiveMutex recursive_mutex1;
RecursiveMutex recursive_mutex2;
// Order 1
recursive_mutex1.Lock();
recursive_mutex2.Lock();
EXPECT_TRUE(recursive_mutex1.TryLock());
EXPECT_TRUE(recursive_mutex2.TryLock());
recursive_mutex1.Unlock();
recursive_mutex1.Unlock();
recursive_mutex2.Unlock();
recursive_mutex2.Unlock();
// Order 2
recursive_mutex1.Lock();
EXPECT_TRUE(recursive_mutex1.TryLock());
recursive_mutex2.Lock();
EXPECT_TRUE(recursive_mutex2.TryLock());
recursive_mutex2.Unlock();
recursive_mutex1.Unlock();
recursive_mutex2.Unlock();
recursive_mutex1.Unlock();
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,318 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/platform/platform.h"
#include <cstdio>
#include <cstring>
#include "include/v8-function.h"
#include "src/base/build_config.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#ifdef V8_TARGET_OS_LINUX
#include <sys/sysmacros.h>
#include "src/base/platform/platform-linux.h"
#endif
#ifdef V8_OS_WIN
#include <windows.h>
#endif
namespace v8 {
namespace base {
#ifdef V8_TARGET_OS_WIN
// Alignment is constrained on Windows.
constexpr size_t kMaxPageSize = 4096;
#elif V8_HOST_ARCH_PPC64
#if defined(_AIX)
// gcc might complain about overalignment (bug):
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89357
constexpr size_t kMaxPageSize = 4096;
#else
// Native PPC linux has large (64KB) physical pages.
constexpr size_t kMaxPageSize = 65536;
#endif
#else
constexpr size_t kMaxPageSize = 16384;
#endif
alignas(kMaxPageSize) const char kArray[kMaxPageSize] =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
"tempor incididunt ut labore et dolore magna aliqua.";
TEST(OS, GetCurrentProcessId) {
#ifdef V8_OS_POSIX
EXPECT_EQ(static_cast<int>(getpid()), OS::GetCurrentProcessId());
#endif
#ifdef V8_OS_WIN
EXPECT_EQ(static_cast<int>(::GetCurrentProcessId()),
OS::GetCurrentProcessId());
#endif
}
TEST(OS, RemapPages) {
if constexpr (OS::IsRemapPageSupported()) {
const size_t size = base::OS::AllocatePageSize();
ASSERT_TRUE(size <= kMaxPageSize);
const void* data = static_cast<const void*>(kArray);
// Target mapping.
void* remapped_data =
OS::Allocate(nullptr, size, base::OS::AllocatePageSize(),
OS::MemoryPermission::kReadWrite);
ASSERT_TRUE(remapped_data);
EXPECT_TRUE(OS::RemapPages(data, size, remapped_data,
OS::MemoryPermission::kReadExecute));
EXPECT_EQ(0, memcmp(remapped_data, data, size));
OS::Free(remapped_data, size);
}
}
#ifdef V8_TARGET_OS_LINUX
TEST(OS, ParseProcMaps) {
// Truncated
std::string line = "00000000-12345678 r--p";
EXPECT_FALSE(MemoryRegion::FromMapsLine(line.c_str()));
// Constants below are for 64 bit architectures.
#ifdef V8_TARGET_ARCH_64_BIT
// File-backed.
line =
"7f861d1e3000-7f861d33b000 r-xp 00026000 fe:01 12583839 "
" /lib/x86_64-linux-gnu/libc-2.33.so";
auto region = MemoryRegion::FromMapsLine(line.c_str());
EXPECT_TRUE(region);
EXPECT_EQ(region->start, 0x7f861d1e3000u);
EXPECT_EQ(region->end, 0x7f861d33b000u);
EXPECT_EQ(std::string(region->permissions), std::string("r-xp"));
EXPECT_EQ(region->offset, 0x00026000u);
EXPECT_EQ(region->dev, makedev(0xfe, 0x01));
EXPECT_EQ(region->inode, 12583839u);
EXPECT_EQ(region->pathname,
std::string("/lib/x86_64-linux-gnu/libc-2.33.so"));
// Large device numbers. (The major device number 0x103 is from a real
// system, the minor device number 0x104 is synthetic.)
line =
"556bea200000-556beaa1c000 r--p 00000000 103:104 22 "
" /usr/local/bin/node";
region = MemoryRegion::FromMapsLine(line.c_str());
EXPECT_EQ(region->start, 0x556bea200000u);
EXPECT_EQ(region->end, 0x556beaa1c000u);
EXPECT_EQ(std::string(region->permissions), std::string("r--p"));
EXPECT_EQ(region->offset, 0x00000000);
EXPECT_EQ(region->dev, makedev(0x103, 0x104));
EXPECT_EQ(region->inode, 22u);
EXPECT_EQ(region->pathname, std::string("/usr/local/bin/node"));
// Anonymous, but named.
line =
"5611cc7eb000-5611cc80c000 rw-p 00000000 00:00 0 "
" [heap]";
region = MemoryRegion::FromMapsLine(line.c_str());
EXPECT_TRUE(region);
EXPECT_EQ(region->start, 0x5611cc7eb000u);
EXPECT_EQ(region->end, 0x5611cc80c000u);
EXPECT_EQ(std::string(region->permissions), std::string("rw-p"));
EXPECT_EQ(region->offset, 0u);
EXPECT_EQ(region->dev, makedev(0x0, 0x0));
EXPECT_EQ(region->inode, 0u);
EXPECT_EQ(region->pathname, std::string("[heap]"));
// Anonymous, not named.
line = "5611cc7eb000-5611cc80c000 rw-p 00000000 00:00 0";
region = MemoryRegion::FromMapsLine(line.c_str());
EXPECT_TRUE(region);
EXPECT_EQ(region->start, 0x5611cc7eb000u);
EXPECT_EQ(region->end, 0x5611cc80c000u);
EXPECT_EQ(std::string(region->permissions), std::string("rw-p"));
EXPECT_EQ(region->offset, 0u);
EXPECT_EQ(region->dev, makedev(0x0, 0x0));
EXPECT_EQ(region->inode, 0u);
EXPECT_EQ(region->pathname, std::string(""));
#endif // V8_TARGET_ARCH_64_BIT
}
TEST(OS, GetSharedLibraryAddresses) {
FILE* fp = tmpfile();
ASSERT_TRUE(fp);
const char* contents =
R"EOF(12340000-12345000 r-xp 00026000 fe:01 12583839 /lib/x86_64-linux-gnu/libc-2.33.so
12365000-12376000 rw-p 00000000 00:00 0 [heap]
12430000-12435000 r-xp 00062000 fe:01 12583839 /path/to/SomeApplication.apk
)EOF";
size_t length = strlen(contents);
ASSERT_EQ(fwrite(contents, 1, length, fp), length);
rewind(fp);
auto shared_library_addresses = GetSharedLibraryAddresses(fp);
EXPECT_EQ(shared_library_addresses.size(), 2u);
EXPECT_EQ(shared_library_addresses[0].library_path,
"/lib/x86_64-linux-gnu/libc-2.33.so");
EXPECT_EQ(shared_library_addresses[0].start, 0x12340000u - 0x26000);
EXPECT_EQ(shared_library_addresses[1].library_path,
"/path/to/SomeApplication.apk");
#if defined(V8_OS_ANDROID)
EXPECT_EQ(shared_library_addresses[1].start, 0x12430000u);
#else
EXPECT_EQ(shared_library_addresses[1].start, 0x12430000u - 0x62000);
#endif
}
#endif // V8_TARGET_OS_LINUX
namespace {
class ThreadLocalStorageTest : public Thread, public ::testing::Test {
public:
ThreadLocalStorageTest() : Thread(Options("ThreadLocalStorageTest")) {
for (size_t i = 0; i < arraysize(keys_); ++i) {
keys_[i] = Thread::CreateThreadLocalKey();
}
}
~ThreadLocalStorageTest() override {
for (size_t i = 0; i < arraysize(keys_); ++i) {
Thread::DeleteThreadLocalKey(keys_[i]);
}
}
void Run() final {
for (size_t i = 0; i < arraysize(keys_); i++) {
CHECK(!Thread::HasThreadLocal(keys_[i]));
}
for (size_t i = 0; i < arraysize(keys_); i++) {
Thread::SetThreadLocal(keys_[i], GetValue(i));
}
for (size_t i = 0; i < arraysize(keys_); i++) {
CHECK(Thread::HasThreadLocal(keys_[i]));
}
for (size_t i = 0; i < arraysize(keys_); i++) {
CHECK_EQ(GetValue(i), Thread::GetThreadLocal(keys_[i]));
CHECK_EQ(GetValue(i), Thread::GetExistingThreadLocal(keys_[i]));
}
for (size_t i = 0; i < arraysize(keys_); i++) {
Thread::SetThreadLocal(keys_[i], GetValue(arraysize(keys_) - i - 1));
}
for (size_t i = 0; i < arraysize(keys_); i++) {
CHECK(Thread::HasThreadLocal(keys_[i]));
}
for (size_t i = 0; i < arraysize(keys_); i++) {
CHECK_EQ(GetValue(arraysize(keys_) - i - 1),
Thread::GetThreadLocal(keys_[i]));
CHECK_EQ(GetValue(arraysize(keys_) - i - 1),
Thread::GetExistingThreadLocal(keys_[i]));
}
}
private:
static void* GetValue(size_t x) { return reinterpret_cast<void*>(x + 1); }
// Older versions of Android have fewer TLS slots (nominally 64, but the
// system uses "about 5 of them" itself).
Thread::LocalStorageKey keys_[32];
};
} // namespace
TEST_F(ThreadLocalStorageTest, DoTest) {
Run();
CHECK(Start());
Join();
}
TEST(StackTest, GetStackStart) { EXPECT_NE(nullptr, Stack::GetStackStart()); }
TEST(StackTest, GetCurrentStackPosition) {
EXPECT_NE(nullptr, Stack::GetCurrentStackPosition());
}
#if !defined(V8_OS_FUCHSIA)
TEST(StackTest, StackVariableInBounds) {
void* dummy;
ASSERT_GT(static_cast<void*>(Stack::GetStackStart()),
Stack::GetCurrentStackPosition());
EXPECT_GT(static_cast<void*>(Stack::GetStackStart()),
Stack::GetRealStackAddressForSlot(&dummy));
EXPECT_LT(static_cast<void*>(Stack::GetCurrentStackPosition()),
Stack::GetRealStackAddressForSlot(&dummy));
}
#endif // !V8_OS_FUCHSIA
using SetDataReadOnlyTest = ::testing::Test;
TEST_F(SetDataReadOnlyTest, SetDataReadOnly) {
static struct alignas(kMaxPageSize) TestData {
int x;
int y;
} test_data;
static_assert(alignof(TestData) == kMaxPageSize);
static_assert(sizeof(TestData) == kMaxPageSize);
test_data.x = 25;
test_data.y = 41;
OS::SetDataReadOnly(&test_data, sizeof(test_data));
CHECK_EQ(25, test_data.x);
CHECK_EQ(41, test_data.y);
ASSERT_DEATH_IF_SUPPORTED(test_data.x = 1, "");
ASSERT_DEATH_IF_SUPPORTED(test_data.y = 0, "");
}
} // namespace base
namespace {
#ifdef V8_CC_GNU
static uintptr_t sp_addr = 0;
void GetStackPointerCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
GET_STACK_POINTER_TO(sp_addr);
CHECK(i::ValidateCallbackInfo(info));
info.GetReturnValue().Set(v8::Integer::NewFromUnsigned(
info.GetIsolate(), static_cast<uint32_t>(sp_addr)));
}
using PlatformTest = v8::TestWithIsolate;
TEST_F(PlatformTest, StackAlignment) {
Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate());
global_template->Set(
isolate(), "get_stack_pointer",
FunctionTemplate::New(isolate(), GetStackPointerCallback));
Local<Context> context = Context::New(isolate(), nullptr, global_template);
Context::Scope context_scope(context);
TryRunJS(
"function foo() {"
" return get_stack_pointer();"
"}");
Local<Object> global_object = context->Global();
Local<Function> foo = v8::Local<v8::Function>::Cast(
global_object->Get(isolate()->GetCurrentContext(), NewString("foo"))
.ToLocalChecked());
Local<v8::Value> result =
foo->Call(isolate()->GetCurrentContext(), global_object, 0, nullptr)
.ToLocalChecked();
CHECK_EQ(0u, result->Uint32Value(isolate()->GetCurrentContext()).FromJust() %
base::OS::ActivationFrameAlignment());
}
#endif // V8_CC_GNU
} // namespace
} // namespace v8

View File

@ -0,0 +1,142 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cstring>
#include "src/base/platform/platform.h"
#include "src/base/platform/semaphore.h"
#include "src/base/platform/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace base {
namespace {
static const char kAlphabet[] = "XKOAD";
static const size_t kAlphabetSize = sizeof(kAlphabet) - 1;
static const size_t kBufferSize = 987; // GCD(buffer size, alphabet size) = 1
static const size_t kDataSize = kBufferSize * kAlphabetSize * 10;
class ProducerThread final : public Thread {
public:
ProducerThread(char* buffer, Semaphore* free_space, Semaphore* used_space)
: Thread(Options("ProducerThread")),
buffer_(buffer),
free_space_(free_space),
used_space_(used_space) {}
void Run() override {
for (size_t n = 0; n < kDataSize; ++n) {
free_space_->Wait();
buffer_[n % kBufferSize] = kAlphabet[n % kAlphabetSize];
used_space_->Signal();
}
}
private:
char* buffer_;
Semaphore* const free_space_;
Semaphore* const used_space_;
};
class ConsumerThread final : public Thread {
public:
ConsumerThread(const char* buffer, Semaphore* free_space,
Semaphore* used_space)
: Thread(Options("ConsumerThread")),
buffer_(buffer),
free_space_(free_space),
used_space_(used_space) {}
void Run() override {
for (size_t n = 0; n < kDataSize; ++n) {
used_space_->Wait();
EXPECT_EQ(kAlphabet[n % kAlphabetSize], buffer_[n % kBufferSize]);
free_space_->Signal();
}
}
private:
const char* buffer_;
Semaphore* const free_space_;
Semaphore* const used_space_;
};
class WaitAndSignalThread final : public Thread {
public:
explicit WaitAndSignalThread(Semaphore* semaphore)
: Thread(Options("WaitAndSignalThread")), semaphore_(semaphore) {}
void Run() override {
for (int n = 0; n < 100; ++n) {
semaphore_->Wait();
ASSERT_FALSE(semaphore_->WaitFor(TimeDelta::FromMicroseconds(1)));
semaphore_->Signal();
}
}
private:
Semaphore* const semaphore_;
};
} // namespace
TEST(Semaphore, ProducerConsumer) {
char buffer[kBufferSize];
std::memset(buffer, 0, sizeof(buffer));
Semaphore free_space(kBufferSize);
Semaphore used_space(0);
ProducerThread producer_thread(buffer, &free_space, &used_space);
ConsumerThread consumer_thread(buffer, &free_space, &used_space);
CHECK(producer_thread.Start());
CHECK(consumer_thread.Start());
producer_thread.Join();
consumer_thread.Join();
}
TEST(Semaphore, WaitAndSignal) {
Semaphore semaphore(0);
WaitAndSignalThread t1(&semaphore);
WaitAndSignalThread t2(&semaphore);
CHECK(t1.Start());
CHECK(t2.Start());
// Make something available.
semaphore.Signal();
t1.Join();
t2.Join();
semaphore.Wait();
EXPECT_FALSE(semaphore.WaitFor(TimeDelta::FromMicroseconds(1)));
}
TEST(Semaphore, WaitFor) {
Semaphore semaphore(0);
// Semaphore not signalled - timeout.
ASSERT_FALSE(semaphore.WaitFor(TimeDelta::FromMicroseconds(0)));
ASSERT_FALSE(semaphore.WaitFor(TimeDelta::FromMicroseconds(100)));
ASSERT_FALSE(semaphore.WaitFor(TimeDelta::FromMicroseconds(1000)));
// Semaphore signalled - no timeout.
semaphore.Signal();
ASSERT_TRUE(semaphore.WaitFor(TimeDelta::FromMicroseconds(0)));
semaphore.Signal();
ASSERT_TRUE(semaphore.WaitFor(TimeDelta::FromMicroseconds(100)));
semaphore.Signal();
ASSERT_TRUE(semaphore.WaitFor(TimeDelta::FromMicroseconds(1000)));
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,581 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/platform/time.h"
#if V8_OS_DARWIN
#include <mach/mach_time.h>
#endif
#if V8_OS_POSIX
#include <sys/time.h>
#endif
#if V8_OS_WIN
#include <windows.h>
#endif
#include <vector>
#include "src/base/platform/elapsed-timer.h"
#include "src/base/platform/platform.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace base {
TEST(TimeDelta, ZeroMinMax) {
constexpr TimeDelta kZero;
static_assert(kZero.IsZero(), "");
constexpr TimeDelta kMax = TimeDelta::Max();
static_assert(kMax.IsMax(), "");
static_assert(kMax == TimeDelta::Max(), "");
EXPECT_GT(kMax, TimeDelta::FromDays(100 * 365));
static_assert(kMax > kZero, "");
constexpr TimeDelta kMin = TimeDelta::Min();
static_assert(kMin.IsMin(), "");
static_assert(kMin == TimeDelta::Min(), "");
EXPECT_LT(kMin, TimeDelta::FromDays(-100 * 365));
static_assert(kMin < kZero, "");
}
TEST(TimeDelta, MaxConversions) {
// static_assert also confirms constexpr works as intended.
constexpr TimeDelta kMax = TimeDelta::Max();
EXPECT_EQ(kMax.InDays(), std::numeric_limits<int>::max());
EXPECT_EQ(kMax.InHours(), std::numeric_limits<int>::max());
EXPECT_EQ(kMax.InMinutes(), std::numeric_limits<int>::max());
EXPECT_EQ(kMax.InSecondsF(), std::numeric_limits<double>::infinity());
EXPECT_EQ(kMax.InSeconds(), std::numeric_limits<int64_t>::max());
EXPECT_EQ(kMax.InMillisecondsF(), std::numeric_limits<double>::infinity());
EXPECT_EQ(kMax.InMilliseconds(), std::numeric_limits<int64_t>::max());
EXPECT_EQ(kMax.InMillisecondsRoundedUp(),
std::numeric_limits<int64_t>::max());
// TODO(v8-team): Import overflow support from Chromium's base.
// EXPECT_TRUE(TimeDelta::FromDays(std::numeric_limits<int>::max()).IsMax());
// EXPECT_TRUE(
// TimeDelta::FromHours(std::numeric_limits<int>::max()).IsMax());
// EXPECT_TRUE(
// TimeDelta::FromMinutes(std::numeric_limits<int>::max()).IsMax());
// constexpr int64_t max_int = std::numeric_limits<int64_t>::max();
// constexpr int64_t min_int = std::numeric_limits<int64_t>::min();
// EXPECT_TRUE(
// TimeDelta::FromSeconds(max_int / Time::kMicrosecondsPerSecond + 1)
// .IsMax());
// EXPECT_TRUE(TimeDelta::FromMilliseconds(
// max_int / Time::kMillisecondsPerSecond + 1)
// .IsMax());
// EXPECT_TRUE(TimeDelta::FromMicroseconds(max_int).IsMax());
// EXPECT_TRUE(
// TimeDelta::FromSeconds(min_int / Time::kMicrosecondsPerSecond - 1)
// .IsMin());
// EXPECT_TRUE(TimeDelta::FromMilliseconds(
// min_int / Time::kMillisecondsPerSecond - 1)
// .IsMin());
// EXPECT_TRUE(TimeDelta::FromMicroseconds(min_int).IsMin());
// EXPECT_TRUE(
// TimeDelta::FromMicroseconds(std::numeric_limits<int64_t>::min())
// .IsMin());
}
TEST(TimeDelta, NumericOperators) {
constexpr int i = 2;
EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
(TimeDelta::FromMilliseconds(1000) * i));
EXPECT_EQ(TimeDelta::FromMilliseconds(500),
(TimeDelta::FromMilliseconds(1000) / i));
EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
(TimeDelta::FromMilliseconds(1000) *= i));
EXPECT_EQ(TimeDelta::FromMilliseconds(500),
(TimeDelta::FromMilliseconds(1000) /= i));
constexpr int64_t i64 = 2;
EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
(TimeDelta::FromMilliseconds(1000) * i64));
EXPECT_EQ(TimeDelta::FromMilliseconds(500),
(TimeDelta::FromMilliseconds(1000) / i64));
EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
(TimeDelta::FromMilliseconds(1000) *= i64));
EXPECT_EQ(TimeDelta::FromMilliseconds(500),
(TimeDelta::FromMilliseconds(1000) /= i64));
EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
(TimeDelta::FromMilliseconds(1000) * 2));
EXPECT_EQ(TimeDelta::FromMilliseconds(500),
(TimeDelta::FromMilliseconds(1000) / 2));
EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
(TimeDelta::FromMilliseconds(1000) *= 2));
EXPECT_EQ(TimeDelta::FromMilliseconds(500),
(TimeDelta::FromMilliseconds(1000) /= 2));
}
// TODO(v8-team): Import support for overflow from Chromium's base.
TEST(TimeDelta, DISABLED_Overflows) {
// Some sanity checks. static_assert's used were possible to verify constexpr
// evaluation at the same time.
static_assert(TimeDelta::Max().IsMax(), "");
static_assert(-TimeDelta::Max() < TimeDelta(), "");
static_assert(-TimeDelta::Max() > TimeDelta::Min(), "");
static_assert(TimeDelta() > -TimeDelta::Max(), "");
TimeDelta large_delta = TimeDelta::Max() - TimeDelta::FromMilliseconds(1);
TimeDelta large_negative = -large_delta;
EXPECT_GT(TimeDelta(), large_negative);
EXPECT_FALSE(large_delta.IsMax());
EXPECT_FALSE((-large_negative).IsMin());
const TimeDelta kOneSecond = TimeDelta::FromSeconds(1);
// Test +, -, * and / operators.
EXPECT_TRUE((large_delta + kOneSecond).IsMax());
EXPECT_TRUE((large_negative + (-kOneSecond)).IsMin());
EXPECT_TRUE((large_negative - kOneSecond).IsMin());
EXPECT_TRUE((large_delta - (-kOneSecond)).IsMax());
EXPECT_TRUE((large_delta * 2).IsMax());
EXPECT_TRUE((large_delta * -2).IsMin());
// Test +=, -=, *= and /= operators.
TimeDelta delta = large_delta;
delta += kOneSecond;
EXPECT_TRUE(delta.IsMax());
delta = large_negative;
delta += -kOneSecond;
EXPECT_TRUE((delta).IsMin());
delta = large_negative;
delta -= kOneSecond;
EXPECT_TRUE((delta).IsMin());
delta = large_delta;
delta -= -kOneSecond;
EXPECT_TRUE(delta.IsMax());
delta = large_delta;
delta *= 2;
EXPECT_TRUE(delta.IsMax());
// Test operations with Time and TimeTicks.
EXPECT_TRUE((large_delta + Time::Now()).IsMax());
EXPECT_TRUE((large_delta + TimeTicks::Now()).IsMax());
EXPECT_TRUE((Time::Now() + large_delta).IsMax());
EXPECT_TRUE((TimeTicks::Now() + large_delta).IsMax());
Time time_now = Time::Now();
EXPECT_EQ(kOneSecond, (time_now + kOneSecond) - time_now);
EXPECT_EQ(-kOneSecond, (time_now - kOneSecond) - time_now);
TimeTicks ticks_now = TimeTicks::Now();
EXPECT_EQ(-kOneSecond, (ticks_now - kOneSecond) - ticks_now);
EXPECT_EQ(kOneSecond, (ticks_now + kOneSecond) - ticks_now);
}
TEST(TimeDelta, FromAndIn) {
EXPECT_EQ(TimeDelta::FromDays(2), TimeDelta::FromHours(48));
EXPECT_EQ(TimeDelta::FromHours(3), TimeDelta::FromMinutes(180));
EXPECT_EQ(TimeDelta::FromMinutes(2), TimeDelta::FromSeconds(120));
EXPECT_EQ(TimeDelta::FromSeconds(2), TimeDelta::FromMilliseconds(2000));
EXPECT_EQ(TimeDelta::FromMilliseconds(2), TimeDelta::FromMicroseconds(2000));
EXPECT_EQ(static_cast<int>(13), TimeDelta::FromDays(13).InDays());
EXPECT_EQ(static_cast<int>(13), TimeDelta::FromHours(13).InHours());
EXPECT_EQ(static_cast<int>(13), TimeDelta::FromMinutes(13).InMinutes());
EXPECT_EQ(static_cast<int64_t>(13), TimeDelta::FromSeconds(13).InSeconds());
EXPECT_DOUBLE_EQ(13.0, TimeDelta::FromSeconds(13).InSecondsF());
EXPECT_EQ(static_cast<int64_t>(13),
TimeDelta::FromMilliseconds(13).InMilliseconds());
EXPECT_DOUBLE_EQ(13.0, TimeDelta::FromMilliseconds(13).InMillisecondsF());
EXPECT_EQ(static_cast<int64_t>(13),
TimeDelta::FromMicroseconds(13).InMicroseconds());
}
#if V8_OS_DARWIN
TEST(TimeDelta, MachTimespec) {
TimeDelta null = TimeDelta();
EXPECT_EQ(null, TimeDelta::FromMachTimespec(null.ToMachTimespec()));
TimeDelta delta1 = TimeDelta::FromMilliseconds(42);
EXPECT_EQ(delta1, TimeDelta::FromMachTimespec(delta1.ToMachTimespec()));
TimeDelta delta2 = TimeDelta::FromDays(42);
EXPECT_EQ(delta2, TimeDelta::FromMachTimespec(delta2.ToMachTimespec()));
}
#endif
TEST(Time, Max) {
Time max = Time::Max();
EXPECT_TRUE(max.IsMax());
EXPECT_EQ(max, Time::Max());
EXPECT_GT(max, Time::Now());
EXPECT_GT(max, Time());
}
TEST(Time, MaxConversions) {
Time t = Time::Max();
EXPECT_EQ(std::numeric_limits<int64_t>::max(), t.ToInternalValue());
// TODO(v8-team): Time::FromJsTime() overflows with infinity. Import support
// from Chromium's base.
// t = Time::FromJsTime(std::numeric_limits<double>::infinity());
// EXPECT_TRUE(t.IsMax());
// EXPECT_EQ(std::numeric_limits<double>::infinity(), t.ToJsTime());
#if defined(OS_POSIX)
struct timeval tval;
tval.tv_sec = std::numeric_limits<time_t>::max();
tval.tv_usec = static_cast<suseconds_t>(Time::kMicrosecondsPerSecond) - 1;
t = Time::FromTimeVal(tval);
EXPECT_TRUE(t.IsMax());
tval = t.ToTimeVal();
EXPECT_EQ(std::numeric_limits<time_t>::max(), tval.tv_sec);
EXPECT_EQ(static_cast<suseconds_t>(Time::kMicrosecondsPerSecond) - 1,
tval.tv_usec);
#endif
#if defined(OS_WIN)
FILETIME ftime;
ftime.dwHighDateTime = std::numeric_limits<DWORD>::max();
ftime.dwLowDateTime = std::numeric_limits<DWORD>::max();
t = Time::FromFileTime(ftime);
EXPECT_TRUE(t.IsMax());
ftime = t.ToFileTime();
EXPECT_EQ(std::numeric_limits<DWORD>::max(), ftime.dwHighDateTime);
EXPECT_EQ(std::numeric_limits<DWORD>::max(), ftime.dwLowDateTime);
#endif
}
TEST(Time, JsTime) {
Time t = Time::FromJsTime(700000.3);
EXPECT_DOUBLE_EQ(700000.3, t.ToJsTime());
}
#if V8_OS_POSIX
TEST(Time, Timespec) {
Time null;
EXPECT_TRUE(null.IsNull());
EXPECT_EQ(null, Time::FromTimespec(null.ToTimespec()));
Time now = Time::Now();
EXPECT_EQ(now, Time::FromTimespec(now.ToTimespec()));
Time now_sys = Time::NowFromSystemTime();
EXPECT_EQ(now_sys, Time::FromTimespec(now_sys.ToTimespec()));
Time unix_epoch = Time::UnixEpoch();
EXPECT_EQ(unix_epoch, Time::FromTimespec(unix_epoch.ToTimespec()));
Time max = Time::Max();
EXPECT_TRUE(max.IsMax());
EXPECT_EQ(max, Time::FromTimespec(max.ToTimespec()));
}
TEST(Time, Timeval) {
Time null;
EXPECT_TRUE(null.IsNull());
EXPECT_EQ(null, Time::FromTimeval(null.ToTimeval()));
Time now = Time::Now();
EXPECT_EQ(now, Time::FromTimeval(now.ToTimeval()));
Time now_sys = Time::NowFromSystemTime();
EXPECT_EQ(now_sys, Time::FromTimeval(now_sys.ToTimeval()));
Time unix_epoch = Time::UnixEpoch();
EXPECT_EQ(unix_epoch, Time::FromTimeval(unix_epoch.ToTimeval()));
Time max = Time::Max();
EXPECT_TRUE(max.IsMax());
EXPECT_EQ(max, Time::FromTimeval(max.ToTimeval()));
}
#endif
#if V8_OS_WIN
TEST(Time, Filetime) {
Time null;
EXPECT_TRUE(null.IsNull());
EXPECT_EQ(null, Time::FromFiletime(null.ToFiletime()));
Time now = Time::Now();
EXPECT_EQ(now, Time::FromFiletime(now.ToFiletime()));
Time now_sys = Time::NowFromSystemTime();
EXPECT_EQ(now_sys, Time::FromFiletime(now_sys.ToFiletime()));
Time unix_epoch = Time::UnixEpoch();
EXPECT_EQ(unix_epoch, Time::FromFiletime(unix_epoch.ToFiletime()));
Time max = Time::Max();
EXPECT_TRUE(max.IsMax());
EXPECT_EQ(max, Time::FromFiletime(max.ToFiletime()));
}
#endif
namespace {
template <typename T>
static void ResolutionTest(T (*Now)(), TimeDelta target_granularity) {
// We're trying to measure that intervals increment in a VERY small amount
// of time -- according to the specified target granularity. Unfortunately,
// if we happen to have a context switch in the middle of our test, the
// context switch could easily exceed our limit. So, we iterate on this
// several times. As long as we're able to detect the fine-granularity
// timers at least once, then the test has succeeded.
static const TimeDelta kExpirationTimeout = TimeDelta::FromSeconds(1);
ElapsedTimer timer;
timer.Start();
TimeDelta delta;
do {
T start = Now();
T now = start;
// Loop until we can detect that the clock has changed. Non-HighRes timers
// will increment in chunks, i.e. 15ms. By spinning until we see a clock
// change, we detect the minimum time between measurements.
do {
now = Now();
delta = now - start;
} while (now <= start);
EXPECT_NE(static_cast<int64_t>(0), delta.InMicroseconds());
} while (delta > target_granularity && !timer.HasExpired(kExpirationTimeout));
EXPECT_LE(delta, target_granularity);
}
} // namespace
TEST(Time, NowResolution) {
// We assume that Time::Now() has at least 16ms resolution.
static const TimeDelta kTargetGranularity = TimeDelta::FromMilliseconds(16);
ResolutionTest<Time>(&Time::Now, kTargetGranularity);
}
TEST(TimeTicks, NowResolution) {
// TimeTicks::Now() is documented as having "no worse than one microsecond"
// resolution. Unless !TimeTicks::IsHighResolution() in which case the clock
// could be as coarse as ~15.6ms.
const TimeDelta kTargetGranularity = TimeTicks::IsHighResolution()
? TimeDelta::FromMicroseconds(1)
: TimeDelta::FromMilliseconds(16);
ResolutionTest<TimeTicks>(&TimeTicks::Now, kTargetGranularity);
}
TEST(TimeTicks, IsMonotonic) {
TimeTicks previous_ticks;
ElapsedTimer timer;
timer.Start();
while (!timer.HasExpired(TimeDelta::FromMilliseconds(100))) {
TimeTicks ticks = TimeTicks::Now();
EXPECT_GE(ticks, previous_ticks);
EXPECT_GE((ticks - previous_ticks).InMicroseconds(), 0);
previous_ticks = ticks;
}
}
namespace {
void Sleep(TimeDelta wait_time) {
ElapsedTimer waiter;
waiter.Start();
while (!waiter.HasExpired(wait_time)) {
OS::Sleep(TimeDelta::FromMilliseconds(1));
}
}
} // namespace
TEST(ElapsedTimer, StartStop) {
TimeDelta wait_time = TimeDelta::FromMilliseconds(100);
TimeDelta noise = TimeDelta::FromMilliseconds(100);
ElapsedTimer timer;
DCHECK(!timer.IsStarted());
timer.Start();
DCHECK(timer.IsStarted());
Sleep(wait_time);
TimeDelta delta = timer.Elapsed();
DCHECK(timer.IsStarted());
EXPECT_GE(delta, wait_time);
EXPECT_LT(delta, wait_time + noise);
DCHECK(!timer.IsPaused());
timer.Pause();
DCHECK(timer.IsPaused());
Sleep(wait_time);
timer.Resume();
DCHECK(timer.IsStarted());
delta = timer.Elapsed();
DCHECK(!timer.IsPaused());
timer.Pause();
DCHECK(timer.IsPaused());
EXPECT_GE(delta, wait_time);
EXPECT_LT(delta, wait_time + noise);
Sleep(wait_time);
timer.Resume();
DCHECK(!timer.IsPaused());
DCHECK(timer.IsStarted());
delta = timer.Elapsed();
EXPECT_GE(delta, wait_time);
EXPECT_LT(delta, wait_time + noise);
timer.Stop();
DCHECK(!timer.IsStarted());
}
TEST(ElapsedTimer, StartStopArgs) {
TimeDelta wait_time = TimeDelta::FromMilliseconds(100);
ElapsedTimer timer1;
ElapsedTimer timer2;
DCHECK(!timer1.IsStarted());
DCHECK(!timer2.IsStarted());
TimeTicks now = TimeTicks::Now();
timer1.Start(now);
timer2.Start(now);
DCHECK(timer1.IsStarted());
DCHECK(timer2.IsStarted());
Sleep(wait_time);
now = TimeTicks::Now();
TimeDelta delta1 = timer1.Elapsed(now);
Sleep(wait_time);
TimeDelta delta2 = timer2.Elapsed(now);
DCHECK(timer1.IsStarted());
DCHECK(timer2.IsStarted());
EXPECT_GE(delta1, delta2);
Sleep(wait_time);
EXPECT_NE(delta1, timer2.Elapsed());
TimeTicks now2 = TimeTicks::Now();
EXPECT_NE(timer1.Elapsed(now), timer1.Elapsed(now2));
EXPECT_NE(delta1, timer1.Elapsed(now2));
EXPECT_NE(delta2, timer2.Elapsed(now2));
EXPECT_GE(timer1.Elapsed(now2), timer2.Elapsed(now2));
now = TimeTicks::Now();
timer1.Pause(now);
timer2.Pause(now);
DCHECK(timer1.IsPaused());
DCHECK(timer2.IsPaused());
Sleep(wait_time);
now = TimeTicks::Now();
timer1.Resume(now);
DCHECK(!timer1.IsPaused());
DCHECK(timer2.IsPaused());
Sleep(wait_time);
timer2.Resume(now);
DCHECK(!timer1.IsPaused());
DCHECK(!timer2.IsPaused());
DCHECK(timer1.IsStarted());
DCHECK(timer2.IsStarted());
delta1 = timer1.Elapsed(now);
Sleep(wait_time);
delta2 = timer2.Elapsed(now);
EXPECT_GE(delta1, delta2);
timer1.Stop();
timer2.Stop();
DCHECK(!timer1.IsStarted());
DCHECK(!timer2.IsStarted());
}
#if V8_OS_ANDROID
#define MAYBE_ThreadNow DISABLED_ThreadNow
#else
#define MAYBE_ThreadNow ThreadNow
#endif
TEST(ThreadTicks, MAYBE_ThreadNow) {
if (ThreadTicks::IsSupported()) {
ThreadTicks::WaitUntilInitialized();
TimeTicks end, begin = TimeTicks::Now();
ThreadTicks end_thread, begin_thread = ThreadTicks::Now();
TimeDelta delta;
// Make sure that ThreadNow value is non-zero.
EXPECT_GT(begin_thread, ThreadTicks());
int iterations_count = 0;
#if V8_OS_WIN && V8_HOST_ARCH_ARM64
// The implementation of ThreadTicks::Now() is quite imprecise on arm64
// Windows, so the following test often fails with the default 10ms. By
// increasing to 100ms, we can make the test reliable.
const int limit_ms = 100;
#else
const int limit_ms = 10;
#endif
const int limit_us = limit_ms * 1000;
// Some systems have low resolution thread timers, this code makes sure
// that thread time has progressed by at least one tick.
// Limit waiting to 10ms to prevent infinite loops.
while (ThreadTicks::Now() == begin_thread &&
((TimeTicks::Now() - begin).InMicroseconds() < limit_us)) {
}
EXPECT_GT(ThreadTicks::Now(), begin_thread);
do {
// Sleep for 10 milliseconds to get the thread de-scheduled.
OS::Sleep(base::TimeDelta::FromMilliseconds(limit_ms));
end_thread = ThreadTicks::Now();
end = TimeTicks::Now();
delta = end - begin;
EXPECT_LE(++iterations_count, 2); // fail after 2 attempts.
} while (delta.InMicroseconds() <
limit_us); // Make sure that the OS did sleep for at least 10 ms.
TimeDelta delta_thread = end_thread - begin_thread;
// Make sure that some thread time have elapsed.
EXPECT_GT(delta_thread.InMicroseconds(), 0);
// But the thread time is at least 9ms less than clock time.
TimeDelta difference = delta - delta_thread;
EXPECT_GE(difference.InMicroseconds(), limit_us * 9 / 10);
}
}
#if V8_OS_WIN
TEST(TimeTicks, TimerPerformance) {
// Verify that various timer mechanisms can always complete quickly.
// Note: This is a somewhat arbitrary test.
const int kLoops = 10000;
using TestFunc = TimeTicks (*)();
struct TestCase {
TestFunc func;
const char *description;
};
// Cheating a bit here: assumes sizeof(TimeTicks) == sizeof(Time)
// in order to create a single test case list.
static_assert(sizeof(TimeTicks) == sizeof(Time),
"TimeTicks and Time must be the same size");
std::vector<TestCase> cases;
cases.push_back({reinterpret_cast<TestFunc>(&Time::Now), "Time::Now"});
cases.push_back({&TimeTicks::Now, "TimeTicks::Now"});
if (ThreadTicks::IsSupported()) {
ThreadTicks::WaitUntilInitialized();
cases.push_back(
{reinterpret_cast<TestFunc>(&ThreadTicks::Now), "ThreadTicks::Now"});
}
for (const auto& test_case : cases) {
TimeTicks start = TimeTicks::Now();
for (int index = 0; index < kLoops; index++)
test_case.func();
TimeTicks stop = TimeTicks::Now();
// Turning off the check for acceptable delays. Without this check,
// the test really doesn't do much other than measure. But the
// measurements are still useful for testing timers on various platforms.
// The reason to remove the check is because the tests run on many
// buildbots, some of which are VMs. These machines can run horribly
// slow, and there is really no value for checking against a max timer.
// const int kMaxTime = 35; // Maximum acceptable milliseconds for test.
// EXPECT_LT((stop - start).InMilliseconds(), kMaxTime);
printf("%s: %1.2fus per call\n", test_case.description,
(stop - start).InMillisecondsF() * 1000 / kLoops);
}
}
#endif // V8_OS_WIN
} // namespace base
} // namespace v8

View File

@ -0,0 +1,400 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/region-allocator.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace base {
using Address = RegionAllocator::Address;
using RegionState = RegionAllocator::RegionState;
using v8::internal::KB;
using v8::internal::MB;
TEST(RegionAllocatorTest, SimpleAllocateRegionAt) {
const size_t kPageSize = 4 * KB;
const size_t kPageCount = 16;
const size_t kSize = kPageSize * kPageCount;
const Address kBegin = static_cast<Address>(kPageSize * 153);
const Address kEnd = kBegin + kSize;
RegionAllocator ra(kBegin, kSize, kPageSize);
// Allocate the whole region.
for (Address address = kBegin; address < kEnd; address += kPageSize) {
CHECK_EQ(ra.free_size(), kEnd - address);
CHECK(ra.AllocateRegionAt(address, kPageSize));
}
// No free regions left, the allocation should fail.
CHECK_EQ(ra.free_size(), 0);
CHECK_EQ(ra.AllocateRegion(kPageSize), RegionAllocator::kAllocationFailure);
// Free one region and then the allocation should succeed.
CHECK_EQ(ra.FreeRegion(kBegin), kPageSize);
CHECK_EQ(ra.free_size(), kPageSize);
CHECK(ra.AllocateRegionAt(kBegin, kPageSize));
// Free all the pages.
for (Address address = kBegin; address < kEnd; address += kPageSize) {
CHECK_EQ(ra.FreeRegion(address), kPageSize);
}
// Check that the whole region is free and can be fully allocated.
CHECK_EQ(ra.free_size(), kSize);
CHECK_EQ(ra.AllocateRegion(kSize), kBegin);
}
TEST(RegionAllocatorTest, SimpleAllocateRegion) {
const size_t kPageSize = 4 * KB;
const size_t kPageCount = 16;
const size_t kSize = kPageSize * kPageCount;
const Address kBegin = static_cast<Address>(kPageSize * 153);
const Address kEnd = kBegin + kSize;
RegionAllocator ra(kBegin, kSize, kPageSize);
// Allocate the whole region.
for (size_t i = 0; i < kPageCount; i++) {
CHECK_EQ(ra.free_size(), kSize - kPageSize * i);
Address address = ra.AllocateRegion(kPageSize);
CHECK_NE(address, RegionAllocator::kAllocationFailure);
CHECK_EQ(address, kBegin + kPageSize * i);
}
// No free regions left, the allocation should fail.
CHECK_EQ(ra.free_size(), 0);
CHECK_EQ(ra.AllocateRegion(kPageSize), RegionAllocator::kAllocationFailure);
// Try to free one page and ensure that we are able to allocate it again.
for (Address address = kBegin; address < kEnd; address += kPageSize) {
CHECK_EQ(ra.FreeRegion(address), kPageSize);
CHECK_EQ(ra.AllocateRegion(kPageSize), address);
}
CHECK_EQ(ra.free_size(), 0);
}
TEST(RegionAllocatorTest, SimpleAllocateAlignedRegion) {
const size_t kPageSize = 4 * KB;
const size_t kPageCount = 16;
const size_t kSize = kPageSize * kPageCount;
const Address kBegin = static_cast<Address>(kPageSize * 153);
RegionAllocator ra(kBegin, kSize, kPageSize);
// Allocate regions with different alignments and verify that they are
// correctly aligned.
const size_t alignments[] = {kPageSize, kPageSize * 8, kPageSize,
kPageSize * 4, kPageSize * 2, kPageSize * 2,
kPageSize * 4, kPageSize * 2};
for (auto alignment : alignments) {
Address address = ra.AllocateAlignedRegion(kPageSize, alignment);
CHECK_NE(address, RegionAllocator::kAllocationFailure);
CHECK(IsAligned(address, alignment));
}
CHECK_EQ(ra.free_size(), 8 * kPageSize);
}
TEST(RegionAllocatorTest, AllocateRegionRandom) {
const size_t kPageSize = 8 * KB;
const size_t kPageCountLog = 16;
const size_t kPageCount = (size_t{1} << kPageCountLog);
const size_t kSize = kPageSize * kPageCount;
const Address kBegin = static_cast<Address>(153 * MB);
const Address kEnd = kBegin + kSize;
base::RandomNumberGenerator rng(GTEST_FLAG_GET(random_seed));
RegionAllocator ra(kBegin, kSize, kPageSize);
std::set<Address> allocated_pages;
// The page addresses must be randomized this number of allocated pages.
const size_t kRandomizationLimit = ra.max_load_for_randomization_ / kPageSize;
CHECK_LT(kRandomizationLimit, kPageCount);
Address last_address = kBegin;
bool saw_randomized_pages = false;
for (size_t i = 0; i < kPageCount; i++) {
Address address = ra.AllocateRegion(&rng, kPageSize);
CHECK_NE(address, RegionAllocator::kAllocationFailure);
CHECK(IsAligned(address, kPageSize));
CHECK_LE(kBegin, address);
CHECK_LT(address, kEnd);
CHECK_EQ(allocated_pages.find(address), allocated_pages.end());
allocated_pages.insert(address);
saw_randomized_pages |= (address < last_address);
last_address = address;
if (i == kRandomizationLimit) {
// We must evidence allocation randomization till this point.
// The rest of the allocations may still be randomized depending on
// the free ranges distribution, however it is not guaranteed.
CHECK(saw_randomized_pages);
}
}
// No free regions left, the allocation should fail.
CHECK_EQ(ra.free_size(), 0);
CHECK_EQ(ra.AllocateRegion(kPageSize), RegionAllocator::kAllocationFailure);
}
TEST(RegionAllocatorTest, AllocateBigRegions) {
const size_t kPageSize = 4 * KB;
const size_t kPageCountLog = 10;
const size_t kPageCount = (size_t{1} << kPageCountLog) - 1;
const size_t kSize = kPageSize * kPageCount;
const Address kBegin = static_cast<Address>(kPageSize * 153);
RegionAllocator ra(kBegin, kSize, kPageSize);
// Allocate the whole region.
for (size_t i = 0; i < kPageCountLog; i++) {
Address address = ra.AllocateRegion(kPageSize * (size_t{1} << i));
CHECK_NE(address, RegionAllocator::kAllocationFailure);
CHECK_EQ(address, kBegin + kPageSize * ((size_t{1} << i) - 1));
}
// No free regions left, the allocation should fail.
CHECK_EQ(ra.free_size(), 0);
CHECK_EQ(ra.AllocateRegion(kPageSize), RegionAllocator::kAllocationFailure);
// Try to free one page and ensure that we are able to allocate it again.
for (size_t i = 0; i < kPageCountLog; i++) {
const size_t size = kPageSize * (size_t{1} << i);
Address address = kBegin + kPageSize * ((size_t{1} << i) - 1);
CHECK_EQ(ra.FreeRegion(address), size);
CHECK_EQ(ra.AllocateRegion(size), address);
}
CHECK_EQ(ra.free_size(), 0);
}
TEST(RegionAllocatorTest, MergeLeftToRightCoalecsingRegions) {
const size_t kPageSize = 4 * KB;
const size_t kPageCountLog = 10;
const size_t kPageCount = (size_t{1} << kPageCountLog);
const size_t kSize = kPageSize * kPageCount;
const Address kBegin = static_cast<Address>(kPageSize * 153);
RegionAllocator ra(kBegin, kSize, kPageSize);
// Allocate the whole region using the following page size pattern:
// |0|1|22|3333|...
CHECK_EQ(ra.AllocateRegion(kPageSize), kBegin);
for (size_t i = 0; i < kPageCountLog; i++) {
Address address = ra.AllocateRegion(kPageSize * (size_t{1} << i));
CHECK_NE(address, RegionAllocator::kAllocationFailure);
CHECK_EQ(address, kBegin + kPageSize * (size_t{1} << i));
}
// No free regions left, the allocation should fail.
CHECK_EQ(ra.free_size(), 0);
CHECK_EQ(ra.AllocateRegion(kPageSize), RegionAllocator::kAllocationFailure);
// Try to free two coalescing regions and ensure the new page of bigger size
// can be allocated.
size_t current_size = kPageSize;
for (size_t i = 0; i < kPageCountLog; i++) {
CHECK_EQ(ra.FreeRegion(kBegin), current_size);
CHECK_EQ(ra.FreeRegion(kBegin + current_size), current_size);
current_size += current_size;
CHECK_EQ(ra.AllocateRegion(current_size), kBegin);
}
CHECK_EQ(ra.free_size(), 0);
}
TEST(RegionAllocatorTest, MergeRightToLeftCoalecsingRegions) {
base::RandomNumberGenerator rng(GTEST_FLAG_GET(random_seed));
const size_t kPageSize = 4 * KB;
const size_t kPageCountLog = 10;
const size_t kPageCount = (size_t{1} << kPageCountLog);
const size_t kSize = kPageSize * kPageCount;
const Address kBegin = static_cast<Address>(kPageSize * 153);
RegionAllocator ra(kBegin, kSize, kPageSize);
// Allocate the whole region.
for (size_t i = 0; i < kPageCount; i++) {
Address address = ra.AllocateRegion(kPageSize);
CHECK_NE(address, RegionAllocator::kAllocationFailure);
CHECK_EQ(address, kBegin + kPageSize * i);
}
// No free regions left, the allocation should fail.
CHECK_EQ(ra.free_size(), 0);
CHECK_EQ(ra.AllocateRegion(kPageSize), RegionAllocator::kAllocationFailure);
// Free pages with even indices left-to-right.
for (size_t i = 0; i < kPageCount; i += 2) {
Address address = kBegin + kPageSize * i;
CHECK_EQ(ra.FreeRegion(address), kPageSize);
}
// Free pages with odd indices right-to-left.
for (size_t i = 1; i < kPageCount; i += 2) {
Address address = kBegin + kPageSize * (kPageCount - i);
CHECK_EQ(ra.FreeRegion(address), kPageSize);
// Now we should be able to allocate a double-sized page.
CHECK_EQ(ra.AllocateRegion(kPageSize * 2), address - kPageSize);
// .. but there's a window for only one such page.
CHECK_EQ(ra.AllocateRegion(kPageSize * 2),
RegionAllocator::kAllocationFailure);
}
// Free all the double-sized pages.
for (size_t i = 0; i < kPageCount; i += 2) {
Address address = kBegin + kPageSize * i;
CHECK_EQ(ra.FreeRegion(address), kPageSize * 2);
}
// Check that the whole region is free and can be fully allocated.
CHECK_EQ(ra.free_size(), kSize);
CHECK_EQ(ra.AllocateRegion(kSize), kBegin);
}
TEST(RegionAllocatorTest, Fragmentation) {
const size_t kPageSize = 64 * KB;
const size_t kPageCount = 9;
const size_t kSize = kPageSize * kPageCount;
const Address kBegin = static_cast<Address>(kPageSize * 153);
RegionAllocator ra(kBegin, kSize, kPageSize);
// Allocate the whole region.
for (size_t i = 0; i < kPageCount; i++) {
Address address = ra.AllocateRegion(kPageSize);
CHECK_NE(address, RegionAllocator::kAllocationFailure);
CHECK_EQ(address, kBegin + kPageSize * i);
}
// No free regions left, the allocation should fail.
CHECK_EQ(ra.free_size(), 0);
CHECK_EQ(ra.AllocateRegion(kPageSize), RegionAllocator::kAllocationFailure);
// Free pages in the following order and check the freed size.
struct {
size_t page_index_to_free;
size_t expected_page_count;
} testcase[] = { // .........
{0, 9}, // x........
{2, 9}, // x.x......
{4, 9}, // x.x.x....
{6, 9}, // x.x.x.x..
{8, 9}, // x.x.x.x.x
{1, 7}, // xxx.x.x.x
{7, 5}, // xxx.x.xxx
{3, 3}, // xxxxx.xxx
{5, 1}}; // xxxxxxxxx
CHECK_EQ(kPageCount, arraysize(testcase));
CHECK_EQ(ra.all_regions_.size(), kPageCount);
for (size_t i = 0; i < kPageCount; i++) {
Address address = kBegin + kPageSize * testcase[i].page_index_to_free;
CHECK_EQ(ra.FreeRegion(address), kPageSize);
CHECK_EQ(ra.all_regions_.size(), testcase[i].expected_page_count);
}
// Check that the whole region is free and can be fully allocated.
CHECK_EQ(ra.free_size(), kSize);
CHECK_EQ(ra.AllocateRegion(kSize), kBegin);
}
TEST(RegionAllocatorTest, FindRegion) {
const size_t kPageSize = 4 * KB;
const size_t kPageCount = 16;
const size_t kSize = kPageSize * kPageCount;
const Address kBegin = static_cast<Address>(kPageSize * 153);
const Address kEnd = kBegin + kSize;
RegionAllocator ra(kBegin, kSize, kPageSize);
// Allocate the whole region.
for (Address address = kBegin; address < kEnd; address += kPageSize) {
CHECK_EQ(ra.free_size(), kEnd - address);
CHECK(ra.AllocateRegionAt(address, kPageSize));
}
// No free regions left, the allocation should fail.
CHECK_EQ(ra.free_size(), 0);
CHECK_EQ(ra.AllocateRegion(kPageSize), RegionAllocator::kAllocationFailure);
// The out-of region requests must return end iterator.
CHECK_EQ(ra.FindRegion(kBegin - 1), ra.all_regions_.end());
CHECK_EQ(ra.FindRegion(kBegin - kPageSize), ra.all_regions_.end());
CHECK_EQ(ra.FindRegion(kBegin / 2), ra.all_regions_.end());
CHECK_EQ(ra.FindRegion(kEnd), ra.all_regions_.end());
CHECK_EQ(ra.FindRegion(kEnd + kPageSize), ra.all_regions_.end());
CHECK_EQ(ra.FindRegion(kEnd * 2), ra.all_regions_.end());
for (Address address = kBegin; address < kEnd; address += kPageSize / 4) {
RegionAllocator::AllRegionsSet::iterator region_iter =
ra.FindRegion(address);
CHECK_NE(region_iter, ra.all_regions_.end());
RegionAllocator::Region* region = *region_iter;
Address region_start = RoundDown(address, kPageSize);
CHECK_EQ(region->begin(), region_start);
CHECK_LE(region->begin(), address);
CHECK_LT(address, region->end());
}
}
TEST(RegionAllocatorTest, TrimRegion) {
const size_t kPageSize = 4 * KB;
const size_t kPageCount = 64;
const size_t kSize = kPageSize * kPageCount;
const Address kBegin = static_cast<Address>(kPageSize * 153);
RegionAllocator ra(kBegin, kSize, kPageSize);
Address address = kBegin + 13 * kPageSize;
size_t size = 37 * kPageSize;
size_t free_size = kSize - size;
CHECK(ra.AllocateRegionAt(address, size));
size_t trim_size = kPageSize;
do {
CHECK_EQ(ra.CheckRegion(address), size);
CHECK_EQ(ra.free_size(), free_size);
trim_size = std::min(size, trim_size);
size -= trim_size;
free_size += trim_size;
CHECK_EQ(ra.TrimRegion(address, size), trim_size);
trim_size *= 2;
} while (size != 0);
// Check that the whole region is free and can be fully allocated.
CHECK_EQ(ra.free_size(), kSize);
CHECK_EQ(ra.AllocateRegion(kSize), kBegin);
}
TEST(RegionAllocatorTest, AllocateExcluded) {
const size_t kPageSize = 4 * KB;
const size_t kPageCount = 64;
const size_t kSize = kPageSize * kPageCount;
const Address kBegin = static_cast<Address>(kPageSize * 153);
RegionAllocator ra(kBegin, kSize, kPageSize);
Address address = kBegin + 13 * kPageSize;
size_t size = 37 * kPageSize;
CHECK(ra.AllocateRegionAt(address, size, RegionState::kExcluded));
// The region is not free and cannot be allocated at again.
CHECK(!ra.IsFree(address, size));
CHECK(!ra.AllocateRegionAt(address, size));
auto region_iter = ra.FindRegion(address);
CHECK((*region_iter)->is_excluded());
// It's not possible to free or trim an excluded region.
CHECK_EQ(ra.FreeRegion(address), 0);
CHECK_EQ(ra.TrimRegion(address, kPageSize), 0);
}
} // namespace base
} // namespace v8

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,591 @@
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Copyright 2023 the V8 project authors. All rights reserved.
// This file is a clone of "base/containers/small_map_unittest.h" in chromium.
// Keep in sync, especially when fixing bugs.
#include <algorithm>
#include <unordered_map>
#include "src/base/small-map.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace base {
TEST(SmallMapTest, General) {
SmallMap<std::unordered_map<int, int>> m;
EXPECT_TRUE(m.empty());
m[0] = 5;
EXPECT_FALSE(m.empty());
EXPECT_EQ(m.size(), 1u);
m[9] = 2;
EXPECT_FALSE(m.empty());
EXPECT_EQ(m.size(), 2u);
EXPECT_EQ(m[9], 2);
EXPECT_EQ(m[0], 5);
EXPECT_FALSE(m.UsingFullMap());
SmallMap<std::unordered_map<int, int>>::iterator iter(m.begin());
ASSERT_TRUE(iter != m.end());
EXPECT_EQ(iter->first, 0);
EXPECT_EQ(iter->second, 5);
++iter;
ASSERT_TRUE(iter != m.end());
EXPECT_EQ((*iter).first, 9);
EXPECT_EQ((*iter).second, 2);
++iter;
EXPECT_TRUE(iter == m.end());
m[8] = 23;
m[1234] = 90;
m[-5] = 6;
EXPECT_EQ(m[9], 2);
EXPECT_EQ(m[0], 5);
EXPECT_EQ(m[1234], 90);
EXPECT_EQ(m[8], 23);
EXPECT_EQ(m[-5], 6);
EXPECT_EQ(m.size(), 5u);
EXPECT_FALSE(m.empty());
EXPECT_TRUE(m.UsingFullMap());
iter = m.begin();
for (int i = 0; i < 5; i++) {
EXPECT_TRUE(iter != m.end());
++iter;
}
EXPECT_TRUE(iter == m.end());
const SmallMap<std::unordered_map<int, int>>& ref = m;
EXPECT_TRUE(ref.find(1234) != m.end());
EXPECT_TRUE(ref.find(5678) == m.end());
}
TEST(SmallMapTest, PostFixIteratorIncrement) {
SmallMap<std::unordered_map<int, int>> m;
m[0] = 5;
m[2] = 3;
{
SmallMap<std::unordered_map<int, int>>::iterator iter(m.begin());
SmallMap<std::unordered_map<int, int>>::iterator last(iter++);
++last;
EXPECT_TRUE(last == iter);
}
{
SmallMap<std::unordered_map<int, int>>::const_iterator iter(m.begin());
SmallMap<std::unordered_map<int, int>>::const_iterator last(iter++);
++last;
EXPECT_TRUE(last == iter);
}
}
// Based on the General testcase.
TEST(SmallMapTest, CopyConstructor) {
SmallMap<std::unordered_map<int, int>> src;
{
SmallMap<std::unordered_map<int, int>> m(src);
EXPECT_TRUE(m.empty());
}
src[0] = 5;
{
SmallMap<std::unordered_map<int, int>> m(src);
EXPECT_FALSE(m.empty());
EXPECT_EQ(m.size(), 1u);
}
src[9] = 2;
{
SmallMap<std::unordered_map<int, int>> m(src);
EXPECT_FALSE(m.empty());
EXPECT_EQ(m.size(), 2u);
EXPECT_EQ(m[9], 2);
EXPECT_EQ(m[0], 5);
EXPECT_FALSE(m.UsingFullMap());
}
src[8] = 23;
src[1234] = 90;
src[-5] = 6;
{
SmallMap<std::unordered_map<int, int>> m(src);
EXPECT_EQ(m[9], 2);
EXPECT_EQ(m[0], 5);
EXPECT_EQ(m[1234], 90);
EXPECT_EQ(m[8], 23);
EXPECT_EQ(m[-5], 6);
EXPECT_EQ(m.size(), 5u);
EXPECT_FALSE(m.empty());
EXPECT_TRUE(m.UsingFullMap());
}
}
template <class inner>
static bool SmallMapIsSubset(SmallMap<inner> const& a,
SmallMap<inner> const& b) {
typename SmallMap<inner>::const_iterator it;
for (it = a.begin(); it != a.end(); ++it) {
typename SmallMap<inner>::const_iterator it_in_b = b.find(it->first);
if (it_in_b == b.end() || it_in_b->second != it->second) return false;
}
return true;
}
template <class inner>
static bool SmallMapEqual(SmallMap<inner> const& a, SmallMap<inner> const& b) {
return SmallMapIsSubset(a, b) && SmallMapIsSubset(b, a);
}
TEST(SmallMapTest, AssignmentOperator) {
SmallMap<std::unordered_map<int, int>> src_small;
SmallMap<std::unordered_map<int, int>> src_large;
src_small[1] = 20;
src_small[2] = 21;
src_small[3] = 22;
EXPECT_FALSE(src_small.UsingFullMap());
src_large[1] = 20;
src_large[2] = 21;
src_large[3] = 22;
src_large[5] = 23;
src_large[6] = 24;
src_large[7] = 25;
EXPECT_TRUE(src_large.UsingFullMap());
// Assignments to empty.
SmallMap<std::unordered_map<int, int>> dest_small;
dest_small = src_small;
EXPECT_TRUE(SmallMapEqual(dest_small, src_small));
EXPECT_EQ(dest_small.UsingFullMap(), src_small.UsingFullMap());
SmallMap<std::unordered_map<int, int>> dest_large;
dest_large = src_large;
EXPECT_TRUE(SmallMapEqual(dest_large, src_large));
EXPECT_EQ(dest_large.UsingFullMap(), src_large.UsingFullMap());
// Assignments which assign from full to small, and vice versa.
dest_small = src_large;
EXPECT_TRUE(SmallMapEqual(dest_small, src_large));
EXPECT_EQ(dest_small.UsingFullMap(), src_large.UsingFullMap());
dest_large = src_small;
EXPECT_TRUE(SmallMapEqual(dest_large, src_small));
EXPECT_EQ(dest_large.UsingFullMap(), src_small.UsingFullMap());
// Double check that SmallMapEqual works:
dest_large[42] = 666;
EXPECT_FALSE(SmallMapEqual(dest_large, src_small));
}
TEST(SmallMapTest, Insert) {
SmallMap<std::unordered_map<int, int>> sm;
// loop through the transition from small map to map.
for (int i = 1; i <= 10; ++i) {
// insert an element
std::pair<SmallMap<std::unordered_map<int, int>>::iterator, bool> ret;
ret = sm.insert(std::make_pair(i, 100 * i));
EXPECT_TRUE(ret.second);
EXPECT_TRUE(ret.first == sm.find(i));
EXPECT_EQ(ret.first->first, i);
EXPECT_EQ(ret.first->second, 100 * i);
// try to insert it again with different value, fails, but we still get an
// iterator back with the original value.
ret = sm.insert(std::make_pair(i, -i));
EXPECT_FALSE(ret.second);
EXPECT_TRUE(ret.first == sm.find(i));
EXPECT_EQ(ret.first->first, i);
EXPECT_EQ(ret.first->second, 100 * i);
// check the state of the map.
for (int j = 1; j <= i; ++j) {
SmallMap<std::unordered_map<int, int>>::iterator it = sm.find(j);
EXPECT_TRUE(it != sm.end());
EXPECT_EQ(it->first, j);
EXPECT_EQ(it->second, j * 100);
}
EXPECT_EQ(sm.size(), static_cast<size_t>(i));
EXPECT_FALSE(sm.empty());
}
}
TEST(SmallMapTest, InsertRange) {
// loop through the transition from small map to map.
for (int elements = 0; elements <= 10; ++elements) {
std::unordered_map<int, int> normal_map;
for (int i = 1; i <= elements; ++i) {
normal_map.insert(std::make_pair(i, 100 * i));
}
SmallMap<std::unordered_map<int, int>> sm;
sm.insert(normal_map.begin(), normal_map.end());
EXPECT_EQ(normal_map.size(), sm.size());
for (int i = 1; i <= elements; ++i) {
EXPECT_TRUE(sm.find(i) != sm.end());
EXPECT_EQ(sm.find(i)->first, i);
EXPECT_EQ(sm.find(i)->second, 100 * i);
}
}
}
TEST(SmallMapTest, Erase) {
SmallMap<std::unordered_map<std::string, int>> m;
SmallMap<std::unordered_map<std::string, int>>::iterator iter;
m["monday"] = 1;
m["tuesday"] = 2;
m["wednesday"] = 3;
EXPECT_EQ(m["monday"], 1);
EXPECT_EQ(m["tuesday"], 2);
EXPECT_EQ(m["wednesday"], 3);
EXPECT_EQ(m.count("tuesday"), 1u);
EXPECT_FALSE(m.UsingFullMap());
iter = m.begin();
ASSERT_TRUE(iter != m.end());
EXPECT_EQ(iter->first, "monday");
EXPECT_EQ(iter->second, 1);
++iter;
ASSERT_TRUE(iter != m.end());
EXPECT_EQ(iter->first, "tuesday");
EXPECT_EQ(iter->second, 2);
++iter;
ASSERT_TRUE(iter != m.end());
EXPECT_EQ(iter->first, "wednesday");
EXPECT_EQ(iter->second, 3);
++iter;
EXPECT_TRUE(iter == m.end());
EXPECT_EQ(m.erase("tuesday"), 1u);
EXPECT_EQ(m["monday"], 1);
EXPECT_EQ(m["wednesday"], 3);
EXPECT_EQ(m.count("tuesday"), 0u);
EXPECT_EQ(m.erase("tuesday"), 0u);
iter = m.begin();
ASSERT_TRUE(iter != m.end());
EXPECT_EQ(iter->first, "monday");
EXPECT_EQ(iter->second, 1);
++iter;
ASSERT_TRUE(iter != m.end());
EXPECT_EQ(iter->first, "wednesday");
EXPECT_EQ(iter->second, 3);
++iter;
EXPECT_TRUE(iter == m.end());
m["thursday"] = 4;
m["friday"] = 5;
EXPECT_EQ(m.size(), 4u);
EXPECT_FALSE(m.empty());
EXPECT_FALSE(m.UsingFullMap());
m["saturday"] = 6;
EXPECT_TRUE(m.UsingFullMap());
EXPECT_EQ(m.count("friday"), 1u);
EXPECT_EQ(m.erase("friday"), 1u);
EXPECT_TRUE(m.UsingFullMap());
EXPECT_EQ(m.count("friday"), 0u);
EXPECT_EQ(m.erase("friday"), 0u);
EXPECT_EQ(m.size(), 4u);
EXPECT_FALSE(m.empty());
EXPECT_EQ(m.erase("monday"), 1u);
EXPECT_EQ(m.size(), 3u);
EXPECT_FALSE(m.empty());
m.clear();
EXPECT_FALSE(m.UsingFullMap());
EXPECT_EQ(m.size(), 0u);
EXPECT_TRUE(m.empty());
}
TEST(SmallMapTest, EraseReturnsIteratorFollowingRemovedElement) {
SmallMap<std::unordered_map<std::string, int>> m;
SmallMap<std::unordered_map<std::string, int>>::iterator iter;
m["a"] = 0;
m["b"] = 1;
m["c"] = 2;
// Erase first item.
auto following_iter = m.erase(m.begin());
EXPECT_EQ(m.begin(), following_iter);
EXPECT_EQ(2u, m.size());
EXPECT_EQ(m.count("a"), 0u);
EXPECT_EQ(m.count("b"), 1u);
EXPECT_EQ(m.count("c"), 1u);
// Iterate to last item and erase it.
++following_iter;
following_iter = m.erase(following_iter);
ASSERT_EQ(1u, m.size());
EXPECT_EQ(m.end(), following_iter);
EXPECT_EQ(m.count("b"), 0u);
EXPECT_EQ(m.count("c"), 1u);
// Erase remaining item.
following_iter = m.erase(m.begin());
EXPECT_TRUE(m.empty());
EXPECT_EQ(m.end(), following_iter);
}
TEST(SmallMapTest, NonHashMap) {
SmallMap<std::map<int, int>, 4, std::equal_to<int>> m;
EXPECT_TRUE(m.empty());
m[9] = 2;
m[0] = 5;
EXPECT_EQ(m[9], 2);
EXPECT_EQ(m[0], 5);
EXPECT_EQ(m.size(), 2u);
EXPECT_FALSE(m.empty());
EXPECT_FALSE(m.UsingFullMap());
SmallMap<std::map<int, int>, 4, std::equal_to<int>>::iterator iter(m.begin());
ASSERT_TRUE(iter != m.end());
EXPECT_EQ(iter->first, 9);
EXPECT_EQ(iter->second, 2);
++iter;
ASSERT_TRUE(iter != m.end());
EXPECT_EQ(iter->first, 0);
EXPECT_EQ(iter->second, 5);
++iter;
EXPECT_TRUE(iter == m.end());
--iter;
ASSERT_TRUE(iter != m.end());
EXPECT_EQ(iter->first, 0);
EXPECT_EQ(iter->second, 5);
m[8] = 23;
m[1234] = 90;
m[-5] = 6;
EXPECT_EQ(m[9], 2);
EXPECT_EQ(m[0], 5);
EXPECT_EQ(m[1234], 90);
EXPECT_EQ(m[8], 23);
EXPECT_EQ(m[-5], 6);
EXPECT_EQ(m.size(), 5u);
EXPECT_FALSE(m.empty());
EXPECT_TRUE(m.UsingFullMap());
iter = m.begin();
ASSERT_TRUE(iter != m.end());
EXPECT_EQ(iter->first, -5);
EXPECT_EQ(iter->second, 6);
++iter;
ASSERT_TRUE(iter != m.end());
EXPECT_EQ(iter->first, 0);
EXPECT_EQ(iter->second, 5);
++iter;
ASSERT_TRUE(iter != m.end());
EXPECT_EQ(iter->first, 8);
EXPECT_EQ(iter->second, 23);
++iter;
ASSERT_TRUE(iter != m.end());
EXPECT_EQ(iter->first, 9);
EXPECT_EQ(iter->second, 2);
++iter;
ASSERT_TRUE(iter != m.end());
EXPECT_EQ(iter->first, 1234);
EXPECT_EQ(iter->second, 90);
++iter;
EXPECT_TRUE(iter == m.end());
--iter;
ASSERT_TRUE(iter != m.end());
EXPECT_EQ(iter->first, 1234);
EXPECT_EQ(iter->second, 90);
}
TEST(SmallMapTest, DefaultEqualKeyWorks) {
// If these tests compile, they pass. The EXPECT calls are only there to avoid
// unused variable warnings.
SmallMap<std::unordered_map<int, int>> hm;
EXPECT_EQ(0u, hm.size());
SmallMap<std::map<int, int>> m;
EXPECT_EQ(0u, m.size());
}
namespace {
class unordered_map_add_item : public std::unordered_map<int, int> {
public:
unordered_map_add_item() = default;
explicit unordered_map_add_item(const std::pair<int, int>& item) {
insert(item);
}
};
void InitMap(unordered_map_add_item* map_ctor) {
new (map_ctor) unordered_map_add_item(std::make_pair(0, 0));
}
class unordered_map_add_item_initializer {
public:
explicit unordered_map_add_item_initializer(int item_to_add)
: item_(item_to_add) {}
unordered_map_add_item_initializer() : item_(0) {}
void operator()(unordered_map_add_item* map_ctor) const {
new (map_ctor) unordered_map_add_item(std::make_pair(item_, item_));
}
int item_;
};
} // anonymous namespace
TEST(SmallMapTest, SubclassInitializationWithFunctionPointer) {
SmallMap<unordered_map_add_item, 4, std::equal_to<int>,
void (&)(unordered_map_add_item*)>
m(InitMap);
EXPECT_TRUE(m.empty());
m[1] = 1;
m[2] = 2;
m[3] = 3;
m[4] = 4;
EXPECT_EQ(4u, m.size());
EXPECT_EQ(0u, m.count(0));
m[5] = 5;
EXPECT_EQ(6u, m.size());
// Our function adds an extra item when we convert to a map.
EXPECT_EQ(1u, m.count(0));
}
TEST(SmallMapTest, SubclassInitializationWithFunctionObject) {
SmallMap<unordered_map_add_item, 4, std::equal_to<int>,
unordered_map_add_item_initializer>
m(unordered_map_add_item_initializer(-1));
EXPECT_TRUE(m.empty());
m[1] = 1;
m[2] = 2;
m[3] = 3;
m[4] = 4;
EXPECT_EQ(4u, m.size());
EXPECT_EQ(0u, m.count(-1));
m[5] = 5;
EXPECT_EQ(6u, m.size());
// Our functor adds an extra item when we convert to a map.
EXPECT_EQ(1u, m.count(-1));
}
namespace {
// This class acts as a basic implementation of a move-only type. The canonical
// example of such a type is scoped_ptr/unique_ptr.
template <typename V>
class MoveOnlyType {
public:
MoveOnlyType() : value_(0) {}
explicit MoveOnlyType(V value) : value_(value) {}
MoveOnlyType(MoveOnlyType&& other) { *this = std::move(other); }
MoveOnlyType& operator=(MoveOnlyType&& other) {
value_ = other.value_;
other.value_ = 0;
return *this;
}
MoveOnlyType(const MoveOnlyType&) = delete;
MoveOnlyType& operator=(const MoveOnlyType&) = delete;
V value() const { return value_; }
private:
V value_;
};
} // namespace
TEST(SmallMapTest, MoveOnlyValueType) {
SmallMap<std::map<int, MoveOnlyType<int>>, 2> m;
m[0] = MoveOnlyType<int>(1);
m[1] = MoveOnlyType<int>(2);
m.erase(m.begin());
// SmallMap will move m[1] to an earlier index in the internal array.
EXPECT_EQ(m.size(), 1u);
EXPECT_EQ(m[1].value(), 2);
m[0] = MoveOnlyType<int>(1);
// SmallMap must move the values from the array into the internal std::map.
m[2] = MoveOnlyType<int>(3);
EXPECT_EQ(m.size(), 3u);
EXPECT_EQ(m[0].value(), 1);
EXPECT_EQ(m[1].value(), 2);
EXPECT_EQ(m[2].value(), 3);
m.erase(m.begin());
// SmallMap should also let internal std::map erase with a move-only type.
EXPECT_EQ(m.size(), 2u);
EXPECT_EQ(m[1].value(), 2);
EXPECT_EQ(m[2].value(), 3);
}
TEST(SmallMapTest, Emplace) {
SmallMap<std::map<size_t, MoveOnlyType<size_t>>> sm;
// loop through the transition from small map to map.
for (size_t i = 1; i <= 10; ++i) {
// insert an element
auto ret = sm.emplace(i, MoveOnlyType<size_t>(100 * i));
EXPECT_TRUE(ret.second);
EXPECT_TRUE(ret.first == sm.find(i));
EXPECT_EQ(ret.first->first, i);
EXPECT_EQ(ret.first->second.value(), 100 * i);
// try to insert it again with different value, fails, but we still get an
// iterator back with the original value.
ret = sm.emplace(i, MoveOnlyType<size_t>(i));
EXPECT_FALSE(ret.second);
EXPECT_TRUE(ret.first == sm.find(i));
EXPECT_EQ(ret.first->first, i);
EXPECT_EQ(ret.first->second.value(), 100 * i);
// check the state of the map.
for (size_t j = 1; j <= i; ++j) {
const auto it = sm.find(j);
EXPECT_TRUE(it != sm.end());
EXPECT_EQ(it->first, j);
EXPECT_EQ(it->second.value(), j * 100);
}
EXPECT_EQ(sm.size(), i);
EXPECT_FALSE(sm.empty());
}
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,105 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/string-format.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest-support.h"
namespace v8::base {
// Some hard-coded assumptions.
constexpr int kMaxPrintedIntLen = 11;
constexpr int kMaxPrintedUint32Len = 10;
constexpr int kMaxPrintedUint64Len = 20;
constexpr int kMaxPrintedSizetLen = sizeof(size_t) == sizeof(uint32_t)
? kMaxPrintedUint32Len
: kMaxPrintedUint64Len;
TEST(FormattedStringTest, Empty) {
auto empty = FormattedString{};
EXPECT_EQ("", decltype(empty)::kFormat);
EXPECT_EQ(1, decltype(empty)::kMaxLen);
EXPECT_EQ('\0', empty.PrintToArray()[0]);
}
TEST(FormattedStringTest, SingleString) {
auto message = FormattedString{} << "foo";
EXPECT_EQ("%s", decltype(message)::kFormat);
constexpr std::array<char, 4> kExpectedOutput{'f', 'o', 'o', '\0'};
EXPECT_EQ(kExpectedOutput, message.PrintToArray());
}
TEST(FormattedStringTest, Int) {
auto message = FormattedString{} << 42;
EXPECT_EQ("%d", decltype(message)::kFormat);
// +1 for null-termination.
EXPECT_EQ(kMaxPrintedIntLen + 1, decltype(message)::kMaxLen);
EXPECT_THAT(message.PrintToArray().data(), ::testing::StrEq("42"));
}
TEST(FormattedStringTest, MaxInt) {
auto message = FormattedString{} << std::numeric_limits<int>::max();
auto result_arr = message.PrintToArray();
// We *nearly* used the full reserved array size (the minimum integer is still
// one character longer)..
EXPECT_EQ(size_t{decltype(message)::kMaxLen}, result_arr.size());
EXPECT_THAT(result_arr.data(), ::testing::StrEq("2147483647"));
}
TEST(FormattedStringTest, MinInt) {
auto message = FormattedString{} << std::numeric_limits<int>::min();
auto result_arr = message.PrintToArray();
// We used the full reserved array size.
EXPECT_EQ(size_t{decltype(message)::kMaxLen}, result_arr.size());
EXPECT_THAT(result_arr.data(), ::testing::StrEq("-2147483648"));
}
TEST(FormattedStringTest, SizeT) {
auto message = FormattedString{} << size_t{42};
EXPECT_EQ(sizeof(size_t) == sizeof(uint32_t) ? "%" PRIu32 : "%" PRIu64,
decltype(message)::kFormat);
// +1 for null-termination.
EXPECT_EQ(kMaxPrintedSizetLen + 1, decltype(message)::kMaxLen);
EXPECT_THAT(message.PrintToArray().data(), ::testing::StrEq("42"));
}
TEST(FormattedStringTest, MaxSizeT) {
auto message = FormattedString{} << std::numeric_limits<size_t>::max();
auto result_arr = message.PrintToArray();
// We used the full reserved array size.
EXPECT_EQ(size_t{decltype(message)::kMaxLen}, result_arr.size());
constexpr const char* kMaxSizeTStr =
sizeof(size_t) == 4 ? "4294967295" : "18446744073709551615";
EXPECT_THAT(result_arr.data(), ::testing::StrEq(kMaxSizeTStr));
}
TEST(FormattedStringTest, Combination) {
auto message = FormattedString{} << "Expected " << 11 << " got " << size_t{42}
<< "!";
EXPECT_EQ(sizeof(size_t) == sizeof(uint32_t) ? "%s%d%s%" PRIu32 "%s"
: "%s%d%s%" PRIu64 "%s",
decltype(message)::kFormat);
size_t expected_array_len =
strlen("Expected got !") + kMaxPrintedIntLen + kMaxPrintedSizetLen + 1;
EXPECT_EQ(expected_array_len, size_t{decltype(message)::kMaxLen});
EXPECT_THAT(message.PrintToArray().data(),
::testing::StrEq("Expected 11 got 42!"));
}
TEST(FormattedStringTest, Uint32AndUint64) {
auto message = FormattedString{} << uint32_t{1} << " != " << uint64_t{2};
EXPECT_EQ("%" PRIu32 "%s%" PRIu64, decltype(message)::kFormat);
size_t expected_array_len =
kMaxPrintedUint32Len + 4 + kMaxPrintedUint64Len + 1;
EXPECT_EQ(expected_array_len, size_t{decltype(message)::kMaxLen});
EXPECT_THAT(message.PrintToArray().data(), ::testing::StrEq("1 != 2"));
}
} // namespace v8::base

View File

@ -0,0 +1,25 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/sys-info.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace base {
TEST(SysInfoTest, NumberOfProcessors) {
EXPECT_LT(0, SysInfo::NumberOfProcessors());
}
TEST(SysInfoTest, AmountOfPhysicalMemory) {
EXPECT_LT(0, SysInfo::AmountOfPhysicalMemory());
}
TEST(SysInfoTest, AmountOfVirtualMemory) {
EXPECT_LE(0, SysInfo::AmountOfVirtualMemory());
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,111 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/template-utils.h"
#include "test/unittests/test-utils.h"
namespace v8 {
namespace base {
namespace template_utils_unittest {
////////////////////////////
// Test make_array.
////////////////////////////
namespace {
template <typename T, size_t Size>
void CheckArrayEquals(const std::array<T, Size>& arr1,
const std::array<T, Size>& arr2) {
for (size_t i = 0; i < Size; ++i) {
CHECK_EQ(arr1[i], arr2[i]);
}
}
} // namespace
TEST(TemplateUtilsTest, MakeArraySimple) {
auto computed_array = base::make_array<3>([](int i) { return 1 + (i * 3); });
std::array<int, 3> expected{{1, 4, 7}};
CheckArrayEquals(computed_array, expected);
}
namespace {
constexpr int doubleIntValue(int i) { return i * 2; }
} // namespace
TEST(TemplateUtilsTest, MakeArrayConstexpr) {
constexpr auto computed_array = base::make_array<3>(doubleIntValue);
constexpr std::array<int, 3> expected{{0, 2, 4}};
CheckArrayEquals(computed_array, expected);
}
////////////////////////////
// Test pass_value_or_ref.
////////////////////////////
// Wrap into this helper struct, such that the type is printed on errors.
template <typename T1, typename T2>
struct CheckIsSame {
static_assert(std::is_same_v<T1, T2>, "test failure");
};
#define TEST_PASS_VALUE_OR_REF0(remove_extend, expected, given) \
static_assert( \
sizeof(CheckIsSame<expected, \
pass_value_or_ref<given, remove_extend>::type>) > 0, \
"check")
#define TEST_PASS_VALUE_OR_REF(expected, given) \
static_assert( \
sizeof(CheckIsSame<expected, pass_value_or_ref<given>::type>) > 0, \
"check")
TEST_PASS_VALUE_OR_REF(int, int&);
TEST_PASS_VALUE_OR_REF(int, int&&);
TEST_PASS_VALUE_OR_REF(const char*, const char[14]);
TEST_PASS_VALUE_OR_REF(const char*, const char*&&);
TEST_PASS_VALUE_OR_REF(const char*, const char (&)[14]);
TEST_PASS_VALUE_OR_REF(const std::string&, std::string);
TEST_PASS_VALUE_OR_REF(const std::string&, std::string&);
TEST_PASS_VALUE_OR_REF(const std::string&, const std::string&);
TEST_PASS_VALUE_OR_REF(int, const int);
TEST_PASS_VALUE_OR_REF(int, const int&);
TEST_PASS_VALUE_OR_REF(const int*, const int*);
TEST_PASS_VALUE_OR_REF(const int*, const int* const);
TEST_PASS_VALUE_OR_REF0(false, const char[14], const char[14]);
TEST_PASS_VALUE_OR_REF0(false, const char[14], const char (&)[14]);
TEST_PASS_VALUE_OR_REF0(false, const std::string&, std::string);
TEST_PASS_VALUE_OR_REF0(false, const std::string&, std::string&);
TEST_PASS_VALUE_OR_REF0(false, const std::string&, const std::string&);
TEST_PASS_VALUE_OR_REF0(false, int, const int);
TEST_PASS_VALUE_OR_REF0(false, int, const int&);
//////////////////////////////
// Test has_output_operator.
//////////////////////////////
// Intrinsic types:
static_assert(has_output_operator<int>, "int can be output");
static_assert(has_output_operator<void*>, "void* can be output");
static_assert(has_output_operator<uint64_t>, "int can be output");
// Classes:
class TestClass1 {};
class TestClass2 {};
extern std::ostream& operator<<(std::ostream& str, const TestClass2&);
class TestClass3 {};
extern std::ostream& operator<<(std::ostream& str, TestClass3);
static_assert(!has_output_operator<TestClass1>, "TestClass1 can not be output");
static_assert(has_output_operator<TestClass2>,
"non-const TestClass2 can be output");
static_assert(has_output_operator<const TestClass2>,
"const TestClass2 can be output");
static_assert(has_output_operator<TestClass3>,
"non-const TestClass3 can be output");
static_assert(has_output_operator<const TestClass3>,
"const TestClass3 can be output");
} // namespace template_utils_unittest
} // namespace base
} // namespace v8

View File

@ -0,0 +1,357 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <iterator>
#include "src/init/v8.h"
#include "src/base/threaded-list.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace base {
struct ThreadedListTestNode {
ThreadedListTestNode() : next_(nullptr), other_next_(nullptr) {}
ThreadedListTestNode** next() { return &next_; }
ThreadedListTestNode* next_;
struct OtherTraits {
static ThreadedListTestNode** start(ThreadedListTestNode** h) { return h; }
static ThreadedListTestNode* const* start(ThreadedListTestNode* const* h) {
return h;
}
static ThreadedListTestNode** next(ThreadedListTestNode* t) {
return t->other_next();
}
};
ThreadedListTestNode** other_next() { return &other_next_; }
ThreadedListTestNode* other_next_;
};
struct ThreadedListTest : public ::testing::Test {
static const size_t INIT_NODES = 5;
ThreadedListTest() {}
void SetUp() override {
for (size_t i = 0; i < INIT_NODES; i++) {
nodes[i] = ThreadedListTestNode();
}
for (size_t i = 0; i < INIT_NODES; i++) {
list.Add(&nodes[i]);
normal_next_list.Add(&nodes[i]);
}
// Verify if setup worked
CHECK(list.Verify());
CHECK_EQ(list.LengthForTest(), INIT_NODES);
CHECK(normal_next_list.Verify());
CHECK_EQ(normal_next_list.LengthForTest(), INIT_NODES);
extra_test_node_0 = ThreadedListTestNode();
extra_test_node_1 = ThreadedListTestNode();
extra_test_node_2 = ThreadedListTestNode();
extra_test_list.Add(&extra_test_node_0);
extra_test_list.Add(&extra_test_node_1);
extra_test_list.Add(&extra_test_node_2);
CHECK_EQ(extra_test_list.LengthForTest(), 3);
CHECK(extra_test_list.Verify());
normal_extra_test_list.Add(&extra_test_node_0);
normal_extra_test_list.Add(&extra_test_node_1);
normal_extra_test_list.Add(&extra_test_node_2);
CHECK_EQ(normal_extra_test_list.LengthForTest(), 3);
CHECK(normal_extra_test_list.Verify());
}
void TearDown() override {
// Check if the normal list threaded through next is still untouched.
CHECK(normal_next_list.Verify());
CHECK_EQ(normal_next_list.LengthForTest(), INIT_NODES);
CHECK_EQ(normal_next_list.AtForTest(0), &nodes[0]);
CHECK_EQ(normal_next_list.AtForTest(4), &nodes[4]);
CHECK(normal_extra_test_list.Verify());
CHECK_EQ(normal_extra_test_list.LengthForTest(), 3);
CHECK_EQ(normal_extra_test_list.AtForTest(0), &extra_test_node_0);
CHECK_EQ(normal_extra_test_list.AtForTest(2), &extra_test_node_2);
list.Clear();
extra_test_list.Clear();
}
ThreadedListTestNode nodes[INIT_NODES];
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits> list;
ThreadedList<ThreadedListTestNode> normal_next_list;
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits>
extra_test_list;
ThreadedList<ThreadedListTestNode> normal_extra_test_list;
ThreadedListTestNode extra_test_node_0;
ThreadedListTestNode extra_test_node_1;
ThreadedListTestNode extra_test_node_2;
};
TEST_F(ThreadedListTest, Add) {
CHECK_EQ(list.LengthForTest(), 5);
ThreadedListTestNode new_node;
// Add to existing list
list.Add(&new_node);
list.Verify();
CHECK_EQ(list.LengthForTest(), 6);
CHECK_EQ(list.AtForTest(5), &new_node);
list.Clear();
CHECK_EQ(list.LengthForTest(), 0);
new_node = ThreadedListTestNode();
// Add to empty list
list.Add(&new_node);
list.Verify();
CHECK_EQ(list.LengthForTest(), 1);
CHECK_EQ(list.AtForTest(0), &new_node);
}
TEST_F(ThreadedListTest, AddFront) {
CHECK_EQ(list.LengthForTest(), 5);
ThreadedListTestNode new_node;
// AddFront to existing list
list.AddFront(&new_node);
list.Verify();
CHECK_EQ(list.LengthForTest(), 6);
CHECK_EQ(list.first(), &new_node);
list.Clear();
CHECK_EQ(list.LengthForTest(), 0);
new_node = ThreadedListTestNode();
// AddFront to empty list
list.AddFront(&new_node);
list.Verify();
CHECK_EQ(list.LengthForTest(), 1);
CHECK_EQ(list.first(), &new_node);
}
TEST_F(ThreadedListTest, DropHead) {
CHECK_EQ(extra_test_list.LengthForTest(), 3);
CHECK_EQ(extra_test_list.first(), &extra_test_node_0);
extra_test_list.DropHead();
extra_test_list.Verify();
CHECK_EQ(extra_test_list.first(), &extra_test_node_1);
CHECK_EQ(extra_test_list.LengthForTest(), 2);
}
TEST_F(ThreadedListTest, Append) {
auto initial_extra_list_end = extra_test_list.end();
CHECK_EQ(list.LengthForTest(), 5);
list.Append(std::move(extra_test_list));
list.Verify();
extra_test_list.Verify();
CHECK(extra_test_list.is_empty());
CHECK_EQ(list.LengthForTest(), 8);
CHECK_EQ(list.AtForTest(4), &nodes[4]);
CHECK_EQ(list.AtForTest(5), &extra_test_node_0);
CHECK_EQ(list.end(), initial_extra_list_end);
}
TEST_F(ThreadedListTest, AppendOutOfScope) {
ThreadedListTestNode local_extra_test_node_0;
CHECK_EQ(list.LengthForTest(), 5);
{
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits>
scoped_extra_test_list;
list.Append(std::move(scoped_extra_test_list));
}
list.Add(&local_extra_test_node_0);
list.Verify();
CHECK_EQ(list.LengthForTest(), 6);
CHECK_EQ(list.AtForTest(4), &nodes[4]);
CHECK_EQ(list.AtForTest(5), &local_extra_test_node_0);
}
TEST_F(ThreadedListTest, Prepend) {
CHECK_EQ(list.LengthForTest(), 5);
list.Prepend(std::move(extra_test_list));
list.Verify();
extra_test_list.Verify();
CHECK(extra_test_list.is_empty());
CHECK_EQ(list.LengthForTest(), 8);
CHECK_EQ(list.first(), &extra_test_node_0);
CHECK_EQ(list.AtForTest(2), &extra_test_node_2);
CHECK_EQ(list.AtForTest(3), &nodes[0]);
}
TEST_F(ThreadedListTest, Clear) {
CHECK_NE(list.LengthForTest(), 0);
list.Clear();
CHECK_EQ(list.LengthForTest(), 0);
CHECK_NULL(list.first());
}
TEST_F(ThreadedListTest, MoveAssign) {
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits> m_list;
CHECK_EQ(extra_test_list.LengthForTest(), 3);
m_list = std::move(extra_test_list);
m_list.Verify();
CHECK_EQ(m_list.first(), &extra_test_node_0);
CHECK_EQ(m_list.LengthForTest(), 3);
// move assign from empty list
extra_test_list.Clear();
CHECK_EQ(extra_test_list.LengthForTest(), 0);
m_list = std::move(extra_test_list);
CHECK_EQ(m_list.LengthForTest(), 0);
m_list.Verify();
CHECK_NULL(m_list.first());
}
TEST_F(ThreadedListTest, MoveCtor) {
CHECK_EQ(extra_test_list.LengthForTest(), 3);
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits> m_list(
std::move(extra_test_list));
m_list.Verify();
CHECK_EQ(m_list.LengthForTest(), 3);
CHECK_EQ(m_list.first(), &extra_test_node_0);
// move construct from empty list
extra_test_list.Clear();
CHECK_EQ(extra_test_list.LengthForTest(), 0);
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits> m_list2(
std::move(extra_test_list));
CHECK_EQ(m_list2.LengthForTest(), 0);
m_list2.Verify();
CHECK_NULL(m_list2.first());
}
TEST_F(ThreadedListTest, Remove) {
CHECK_EQ(list.LengthForTest(), 5);
// Remove first
CHECK_EQ(list.first(), &nodes[0]);
list.Remove(&nodes[0]);
list.Verify();
CHECK_EQ(list.first(), &nodes[1]);
CHECK_EQ(list.LengthForTest(), 4);
// Remove middle
list.Remove(&nodes[2]);
list.Verify();
CHECK_EQ(list.LengthForTest(), 3);
CHECK_EQ(list.first(), &nodes[1]);
CHECK_EQ(list.AtForTest(1), &nodes[3]);
// Remove last
list.Remove(&nodes[4]);
list.Verify();
CHECK_EQ(list.LengthForTest(), 2);
CHECK_EQ(list.first(), &nodes[1]);
CHECK_EQ(list.AtForTest(1), &nodes[3]);
// Remove rest
list.Remove(&nodes[1]);
list.Remove(&nodes[3]);
list.Verify();
CHECK_EQ(list.LengthForTest(), 0);
// Remove not found
list.Remove(&nodes[4]);
list.Verify();
CHECK_EQ(list.LengthForTest(), 0);
}
TEST_F(ThreadedListTest, Rewind) {
CHECK_EQ(extra_test_list.LengthForTest(), 3);
for (auto iter = extra_test_list.begin(); iter != extra_test_list.end();
++iter) {
if (*iter == &extra_test_node_2) {
extra_test_list.Rewind(iter);
break;
}
}
CHECK_EQ(extra_test_list.LengthForTest(), 2);
auto iter = extra_test_list.begin();
CHECK_EQ(*iter, &extra_test_node_0);
std::advance(iter, 1);
CHECK_EQ(*iter, &extra_test_node_1);
extra_test_list.Rewind(extra_test_list.begin());
CHECK_EQ(extra_test_list.LengthForTest(), 0);
}
TEST_F(ThreadedListTest, IterComp) {
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits> c_list =
std::move(extra_test_list);
bool found_first;
for (auto iter = c_list.begin(); iter != c_list.end(); ++iter) {
// This triggers the operator== on the iterator
if (iter == c_list.begin()) {
found_first = true;
}
}
CHECK(found_first);
}
TEST_F(ThreadedListTest, ConstIterComp) {
const ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits>
c_list = std::move(extra_test_list);
bool found_first;
for (auto iter = c_list.begin(); iter != c_list.end(); ++iter) {
// This triggers the operator== on the iterator
if (iter == c_list.begin()) {
found_first = true;
}
}
CHECK(found_first);
}
TEST_F(ThreadedListTest, RemoveAt) {
auto it = list.begin();
// Removing first
ThreadedListTestNode* to_remove = list.first();
it = list.RemoveAt(it);
EXPECT_EQ(to_remove, &nodes[0]);
EXPECT_EQ(list.first(), &nodes[1]);
EXPECT_EQ(it, list.begin());
EXPECT_EQ(*it, &nodes[1]);
EXPECT_EQ(*ThreadedListTestNode::OtherTraits::next(to_remove), nullptr);
EXPECT_FALSE(list.Contains(to_remove));
EXPECT_EQ(list.LengthForTest(), 4);
list.Verify();
// Removing in the middle
++it;
to_remove = *it;
it = list.RemoveAt(it);
EXPECT_EQ(*it, &nodes[3]);
EXPECT_FALSE(list.Contains(to_remove));
EXPECT_EQ(*ThreadedListTestNode::OtherTraits::next(to_remove), nullptr);
EXPECT_EQ(*ThreadedListTestNode::OtherTraits::next(&nodes[1]), &nodes[3]);
EXPECT_EQ(list.LengthForTest(), 3);
list.Verify();
// Removing last
++it;
to_remove = *it;
it = list.RemoveAt(it);
EXPECT_EQ(it, list.end());
EXPECT_FALSE(list.Contains(to_remove));
EXPECT_EQ(*ThreadedListTestNode::OtherTraits::next(&nodes[4]), nullptr);
EXPECT_EQ(list.LengthForTest(), 2);
list.Verify();
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,262 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include <climits>
#include "src/base/utils/random-number-generator.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace base {
class RandomNumberGeneratorTest : public ::testing::TestWithParam<int> {};
static const int kMaxRuns = 12345;
static void CheckSample(std::vector<uint64_t> sample, uint64_t max,
size_t size) {
EXPECT_EQ(sample.size(), size);
// Check if values are unique.
std::sort(sample.begin(), sample.end());
EXPECT_EQ(std::adjacent_find(sample.begin(), sample.end()), sample.end());
for (uint64_t x : sample) {
EXPECT_LT(x, max);
}
}
static void CheckSlowSample(const std::vector<uint64_t>& sample, uint64_t max,
size_t size,
const std::unordered_set<uint64_t>& excluded) {
CheckSample(sample, max, size);
for (uint64_t i : sample) {
EXPECT_FALSE(excluded.count(i));
}
}
static void TestNextSample(RandomNumberGenerator* rng, uint64_t max,
size_t size, bool slow = false) {
std::vector<uint64_t> sample =
slow ? rng->NextSampleSlow(max, size) : rng->NextSample(max, size);
CheckSample(sample, max, size);
}
TEST_P(RandomNumberGeneratorTest, NextIntWithMaxValue) {
RandomNumberGenerator rng(GetParam());
for (int max = 1; max <= kMaxRuns; ++max) {
int n = rng.NextInt(max);
EXPECT_LE(0, n);
EXPECT_LT(n, max);
}
}
TEST_P(RandomNumberGeneratorTest, NextBooleanReturnsFalseOrTrue) {
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
bool b = rng.NextBool();
EXPECT_TRUE(b == false || b == true);
}
}
TEST_P(RandomNumberGeneratorTest, NextDoubleReturnsValueBetween0And1) {
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
double d = rng.NextDouble();
EXPECT_LE(0.0, d);
EXPECT_LT(d, 1.0);
}
}
#if !defined(DEBUG) && defined(OFFICIAL_BUILD)
// Official release builds strip all fatal messages for saving binary size,
// see src/base/logging.h.
#define FATAL_MSG(msg) ""
#else
#define FATAL_MSG(msg) "Check failed: " msg
#endif
TEST(RandomNumberGenerator, NextSampleInvalidParam) {
RandomNumberGenerator rng(123);
std::vector<uint64_t> sample;
ASSERT_DEATH_IF_SUPPORTED(sample = rng.NextSample(10, 11),
FATAL_MSG("n <= max"));
}
TEST(RandomNumberGenerator, NextSampleSlowInvalidParam1) {
RandomNumberGenerator rng(123);
std::vector<uint64_t> sample;
ASSERT_DEATH_IF_SUPPORTED(sample = rng.NextSampleSlow(10, 11),
FATAL_MSG("max - excluded.size"));
}
TEST(RandomNumberGenerator, NextSampleSlowInvalidParam2) {
RandomNumberGenerator rng(123);
std::vector<uint64_t> sample;
ASSERT_DEATH_IF_SUPPORTED(sample = rng.NextSampleSlow(5, 3, {0, 2, 3}),
FATAL_MSG("max - excluded.size"));
}
#undef FATAL_MSG
TEST_P(RandomNumberGeneratorTest, NextSample0) {
size_t m = 1;
RandomNumberGenerator rng(GetParam());
TestNextSample(&rng, m, 0);
}
TEST_P(RandomNumberGeneratorTest, NextSampleSlow0) {
size_t m = 1;
RandomNumberGenerator rng(GetParam());
TestNextSample(&rng, m, 0, true);
}
TEST_P(RandomNumberGeneratorTest, NextSample1) {
size_t m = 10;
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
TestNextSample(&rng, m, 1);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleSlow1) {
size_t m = 10;
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
TestNextSample(&rng, m, 1, true);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleMax) {
size_t m = 10;
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
TestNextSample(&rng, m, m);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleSlowMax) {
size_t m = 10;
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
TestNextSample(&rng, m, m, true);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleHalf) {
size_t n = 5;
uint64_t m = 10;
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
TestNextSample(&rng, m, n);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleSlowHalf) {
size_t n = 5;
uint64_t m = 10;
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
TestNextSample(&rng, m, n, true);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleMoreThanHalf) {
size_t n = 90;
uint64_t m = 100;
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
TestNextSample(&rng, m, n);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleSlowMoreThanHalf) {
size_t n = 90;
uint64_t m = 100;
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
TestNextSample(&rng, m, n, true);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleLessThanHalf) {
size_t n = 10;
uint64_t m = 100;
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
TestNextSample(&rng, m, n);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleSlowLessThanHalf) {
size_t n = 10;
uint64_t m = 100;
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
TestNextSample(&rng, m, n, true);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleSlowExcluded) {
size_t n = 2;
uint64_t m = 10;
std::unordered_set<uint64_t> excluded = {2, 4, 5, 9};
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
std::vector<uint64_t> sample = rng.NextSampleSlow(m, n, excluded);
CheckSlowSample(sample, m, n, excluded);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleSlowExcludedMax1) {
size_t n = 1;
uint64_t m = 5;
std::unordered_set<uint64_t> excluded = {0, 2, 3, 4};
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
std::vector<uint64_t> sample = rng.NextSampleSlow(m, n, excluded);
CheckSlowSample(sample, m, n, excluded);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleSlowExcludedMax2) {
size_t n = 7;
uint64_t m = 10;
std::unordered_set<uint64_t> excluded = {0, 4, 8};
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
std::vector<uint64_t> sample = rng.NextSampleSlow(m, n, excluded);
CheckSlowSample(sample, m, n, excluded);
}
}
INSTANTIATE_TEST_SUITE_P(RandomSeeds, RandomNumberGeneratorTest,
::testing::Values(INT_MIN, -1, 0, 1, 42, 100,
1234567890, 987654321, INT_MAX));
} // namespace base
} // namespace v8

View File

@ -0,0 +1,122 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/vector.h"
#include <algorithm>
#include "testing/gmock-support.h"
namespace v8 {
namespace base {
TEST(VectorTest, Factories) {
auto vec = base::CStrVector("foo");
EXPECT_EQ(3u, vec.size());
EXPECT_EQ(0, memcmp(vec.begin(), "foo", 3));
vec = base::ArrayVector("foo");
EXPECT_EQ(4u, vec.size());
EXPECT_EQ(0, memcmp(vec.begin(), "foo\0", 4));
vec = base::CStrVector("foo\0\0");
EXPECT_EQ(3u, vec.size());
EXPECT_EQ(0, memcmp(vec.begin(), "foo", 3));
vec = base::CStrVector("");
EXPECT_EQ(0u, vec.size());
vec = base::CStrVector("\0");
EXPECT_EQ(0u, vec.size());
}
// Test operator== and operator!= on different Vector types.
TEST(VectorTest, Equals) {
auto foo1 = base::CStrVector("foo");
auto foo2 = base::ArrayVector("ffoo") + 1;
EXPECT_EQ(4u, foo2.size()); // Includes trailing '\0'.
foo2.Truncate(foo2.size() - 1);
// This is a requirement for the test.
EXPECT_NE(foo1.begin(), foo2.begin());
EXPECT_EQ(foo1, foo2);
// Compare base::Vector<char> against base::Vector<const char>.
char arr1[] = {'a', 'b', 'c'};
char arr2[] = {'a', 'b', 'c'};
char arr3[] = {'a', 'b', 'd'};
base::Vector<char> vec1_char = base::ArrayVector(arr1);
base::Vector<const char> vec1_const_char = vec1_char;
base::Vector<char> vec2_char = base::ArrayVector(arr2);
base::Vector<char> vec3_char = base::ArrayVector(arr3);
EXPECT_NE(vec1_char.begin(), vec2_char.begin());
// Note: We directly call operator== and operator!= here (without EXPECT_EQ or
// EXPECT_NE) to have full control over the arguments.
EXPECT_TRUE(vec1_char == vec1_const_char);
EXPECT_TRUE(vec1_char == vec2_char);
EXPECT_TRUE(vec1_const_char == vec2_char);
EXPECT_TRUE(vec1_const_char != vec3_char);
EXPECT_TRUE(vec3_char != vec2_char);
EXPECT_TRUE(vec3_char != vec1_const_char);
}
TEST(OwnedVectorTest, Equals) {
auto int_vec = base::OwnedVector<int>::New(4);
EXPECT_EQ(4u, int_vec.size());
auto find_non_zero = [](int i) { return i != 0; };
EXPECT_EQ(int_vec.end(),
std::find_if(int_vec.begin(), int_vec.end(), find_non_zero));
constexpr int kInit[] = {4, 11, 3};
auto init_vec1 = base::OwnedCopyOf(kInit);
// Note: {const int} should also work: We initialize the owned vector, but
// afterwards it's non-modifyable.
auto init_vec2 = base::OwnedCopyOf(base::ArrayVector(kInit));
EXPECT_EQ(init_vec1.as_vector(), base::ArrayVector(kInit));
EXPECT_EQ(init_vec1.as_vector(), init_vec2.as_vector());
}
TEST(OwnedVectorTest, MoveConstructionAndAssignment) {
constexpr int kValues[] = {4, 11, 3};
auto int_vec = base::OwnedCopyOf(kValues);
EXPECT_EQ(3u, int_vec.size());
auto move_constructed_vec = std::move(int_vec);
EXPECT_EQ(move_constructed_vec.as_vector(), base::ArrayVector(kValues));
auto move_assigned_to_empty = base::OwnedVector<int>{};
move_assigned_to_empty = std::move(move_constructed_vec);
EXPECT_EQ(move_assigned_to_empty.as_vector(), base::ArrayVector(kValues));
auto move_assigned_to_non_empty = base::OwnedVector<int>::New(2);
move_assigned_to_non_empty = std::move(move_assigned_to_empty);
EXPECT_EQ(move_assigned_to_non_empty.as_vector(), base::ArrayVector(kValues));
// All but the last vector must be empty (length 0, nullptr data).
EXPECT_TRUE(int_vec.empty());
EXPECT_TRUE(int_vec.begin() == nullptr);
EXPECT_TRUE(move_constructed_vec.empty());
EXPECT_TRUE(move_constructed_vec.begin() == nullptr);
EXPECT_TRUE(move_assigned_to_empty.empty());
EXPECT_TRUE(move_assigned_to_empty.begin() == nullptr);
}
// Test that the constexpr factory methods work.
TEST(VectorTest, ConstexprFactories) {
static constexpr int kInit1[] = {4, 11, 3};
static constexpr auto kVec1 = base::ArrayVector(kInit1);
static_assert(kVec1.size() == 3);
EXPECT_THAT(kVec1, testing::ElementsAreArray(kInit1));
static constexpr auto kVec2 = base::VectorOf(kInit1, 2);
static_assert(kVec2.size() == 2);
EXPECT_THAT(kVec2, testing::ElementsAre(4, 11));
static constexpr const char kInit3[] = "foobar";
static constexpr auto kVec3 = base::StaticCharVector(kInit3);
static_assert(kVec3.size() == 6);
EXPECT_THAT(kVec3, testing::ElementsAreArray(kInit3, kInit3 + 6));
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,266 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/virtual-address-space.h"
#include "src/base/emulated-virtual-address-subspace.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace base {
constexpr size_t KB = 1024;
constexpr size_t MB = KB * 1024;
void TestRandomPageAddressGeneration(v8::VirtualAddressSpace* space) {
space->SetRandomSeed(GTEST_FLAG_GET(random_seed));
for (int i = 0; i < 10; i++) {
Address addr = space->RandomPageAddress();
EXPECT_GE(addr, space->base());
EXPECT_LT(addr, space->base() + space->size());
}
}
void TestBasicPageAllocation(v8::VirtualAddressSpace* space) {
// Allocation sizes in KB.
const size_t allocation_sizes[] = {4, 8, 12, 16, 32, 64, 128,
256, 512, 768, 1024, 768, 512, 256,
128, 64, 32, 16, 12, 8, 4};
std::vector<Address> allocations;
size_t alignment = space->allocation_granularity();
for (size_t i = 0; i < arraysize(allocation_sizes); i++) {
size_t size = allocation_sizes[i] * KB;
if (!IsAligned(size, space->allocation_granularity())) continue;
Address allocation =
space->AllocatePages(VirtualAddressSpace::kNoHint, size, alignment,
PagePermissions::kReadWrite);
ASSERT_NE(kNullAddress, allocation);
EXPECT_GE(allocation, space->base());
EXPECT_LT(allocation, space->base() + space->size());
allocations.push_back(allocation);
// Memory must be writable
*reinterpret_cast<size_t*>(allocation) = size;
}
// Windows has an allocation granularity of 64KB and macOS could have 16KB, so
// we won't necessarily have managed to obtain all allocations, but we
// should've gotten all that are >= 64KB.
EXPECT_GE(allocations.size(), 11UL);
for (Address allocation : allocations) {
//... and readable
size_t size = *reinterpret_cast<size_t*>(allocation);
space->FreePages(allocation, size);
}
}
void TestPageAllocationAlignment(v8::VirtualAddressSpace* space) {
// In multiples of the allocation_granularity.
const size_t alignments[] = {1, 2, 4, 8, 16, 32, 64};
const size_t size = space->allocation_granularity();
for (size_t i = 0; i < arraysize(alignments); i++) {
size_t alignment = alignments[i] * space->allocation_granularity();
Address allocation =
space->AllocatePages(VirtualAddressSpace::kNoHint, size, alignment,
PagePermissions::kReadWrite);
ASSERT_NE(kNullAddress, allocation);
EXPECT_TRUE(IsAligned(allocation, alignment));
EXPECT_GE(allocation, space->base());
EXPECT_LT(allocation, space->base() + space->size());
space->FreePages(allocation, size);
}
}
void TestParentSpaceCannotAllocateInChildSpace(v8::VirtualAddressSpace* parent,
v8::VirtualAddressSpace* child) {
child->SetRandomSeed(GTEST_FLAG_GET(random_seed));
size_t chunksize = parent->allocation_granularity();
size_t alignment = chunksize;
Address start = child->base();
Address end = start + child->size();
for (int i = 0; i < 10; i++) {
Address hint = child->RandomPageAddress();
Address allocation = parent->AllocatePages(hint, chunksize, alignment,
PagePermissions::kNoAccess);
ASSERT_NE(kNullAddress, allocation);
EXPECT_TRUE(allocation < start || allocation >= end);
parent->FreePages(allocation, chunksize);
}
}
void TestSharedPageAllocation(v8::VirtualAddressSpace* space) {
const size_t size = 2 * space->allocation_granularity();
PlatformSharedMemoryHandle handle =
OS::CreateSharedMemoryHandleForTesting(size);
if (handle == kInvalidSharedMemoryHandle) return;
Address mapping1 =
space->AllocateSharedPages(VirtualAddressSpace::kNoHint, size,
PagePermissions::kReadWrite, handle, 0);
ASSERT_NE(kNullAddress, mapping1);
Address mapping2 =
space->AllocateSharedPages(VirtualAddressSpace::kNoHint, size,
PagePermissions::kReadWrite, handle, 0);
ASSERT_NE(kNullAddress, mapping2);
ASSERT_NE(mapping1, mapping2);
int value = 0x42;
EXPECT_EQ(0, *reinterpret_cast<int*>(mapping2));
*reinterpret_cast<int*>(mapping1) = value;
EXPECT_EQ(value, *reinterpret_cast<int*>(mapping2));
space->FreeSharedPages(mapping1, size);
space->FreeSharedPages(mapping2, size);
OS::DestroySharedMemoryHandle(handle);
}
TEST(VirtualAddressSpaceTest, TestPagePermissionSubsets) {
const PagePermissions kNoAccess = PagePermissions::kNoAccess;
const PagePermissions kRead = PagePermissions::kRead;
const PagePermissions kReadWrite = PagePermissions::kReadWrite;
const PagePermissions kReadWriteExecute = PagePermissions::kReadWriteExecute;
const PagePermissions kReadExecute = PagePermissions::kReadExecute;
EXPECT_TRUE(IsSubset(kNoAccess, kNoAccess));
EXPECT_FALSE(IsSubset(kRead, kNoAccess));
EXPECT_FALSE(IsSubset(kReadWrite, kNoAccess));
EXPECT_FALSE(IsSubset(kReadWriteExecute, kNoAccess));
EXPECT_FALSE(IsSubset(kReadExecute, kNoAccess));
EXPECT_TRUE(IsSubset(kNoAccess, kRead));
EXPECT_TRUE(IsSubset(kRead, kRead));
EXPECT_FALSE(IsSubset(kReadWrite, kRead));
EXPECT_FALSE(IsSubset(kReadWriteExecute, kRead));
EXPECT_FALSE(IsSubset(kReadExecute, kRead));
EXPECT_TRUE(IsSubset(kNoAccess, kReadWrite));
EXPECT_TRUE(IsSubset(kRead, kReadWrite));
EXPECT_TRUE(IsSubset(kReadWrite, kReadWrite));
EXPECT_FALSE(IsSubset(kReadWriteExecute, kReadWrite));
EXPECT_FALSE(IsSubset(kReadExecute, kReadWrite));
EXPECT_TRUE(IsSubset(kNoAccess, kReadWriteExecute));
EXPECT_TRUE(IsSubset(kRead, kReadWriteExecute));
EXPECT_TRUE(IsSubset(kReadWrite, kReadWriteExecute));
EXPECT_TRUE(IsSubset(kReadWriteExecute, kReadWriteExecute));
EXPECT_TRUE(IsSubset(kReadExecute, kReadWriteExecute));
EXPECT_TRUE(IsSubset(kNoAccess, kReadExecute));
EXPECT_TRUE(IsSubset(kRead, kReadExecute));
EXPECT_FALSE(IsSubset(kReadWrite, kReadExecute));
EXPECT_FALSE(IsSubset(kReadWriteExecute, kReadExecute));
EXPECT_TRUE(IsSubset(kReadExecute, kReadExecute));
}
TEST(VirtualAddressSpaceTest, TestRootSpace) {
VirtualAddressSpace rootspace;
TestRandomPageAddressGeneration(&rootspace);
TestBasicPageAllocation(&rootspace);
TestPageAllocationAlignment(&rootspace);
TestSharedPageAllocation(&rootspace);
}
TEST(VirtualAddressSpaceTest, TestSubspace) {
constexpr size_t kSubspaceSize = 32 * MB;
constexpr size_t kSubSubspaceSize = 16 * MB;
VirtualAddressSpace rootspace;
if (!rootspace.CanAllocateSubspaces()) return;
size_t subspace_alignment = rootspace.allocation_granularity();
auto subspace = rootspace.AllocateSubspace(VirtualAddressSpace::kNoHint,
kSubspaceSize, subspace_alignment,
PagePermissions::kReadWrite);
ASSERT_TRUE(subspace);
EXPECT_NE(kNullAddress, subspace->base());
EXPECT_EQ(kSubspaceSize, subspace->size());
EXPECT_EQ(PagePermissions::kReadWrite, subspace->max_page_permissions());
TestRandomPageAddressGeneration(subspace.get());
TestBasicPageAllocation(subspace.get());
TestPageAllocationAlignment(subspace.get());
TestParentSpaceCannotAllocateInChildSpace(&rootspace, subspace.get());
TestSharedPageAllocation(subspace.get());
// Test sub-subspaces
if (!subspace->CanAllocateSubspaces()) return;
size_t subsubspace_alignment = subspace->allocation_granularity();
auto subsubspace = subspace->AllocateSubspace(
VirtualAddressSpace::kNoHint, kSubSubspaceSize, subsubspace_alignment,
PagePermissions::kReadWrite);
ASSERT_TRUE(subsubspace);
EXPECT_NE(kNullAddress, subsubspace->base());
EXPECT_EQ(kSubSubspaceSize, subsubspace->size());
EXPECT_EQ(PagePermissions::kReadWrite, subsubspace->max_page_permissions());
TestRandomPageAddressGeneration(subsubspace.get());
TestBasicPageAllocation(subsubspace.get());
TestPageAllocationAlignment(subsubspace.get());
TestParentSpaceCannotAllocateInChildSpace(subspace.get(), subsubspace.get());
TestSharedPageAllocation(subsubspace.get());
}
TEST(VirtualAddressSpaceTest, TestEmulatedSubspace) {
constexpr size_t kSubspaceSize = 32 * MB;
// Size chosen so page allocation tests will obtain pages in both the mapped
// and the unmapped region.
constexpr size_t kSubspaceMappedSize = 1 * MB;
VirtualAddressSpace rootspace;
size_t subspace_alignment = rootspace.allocation_granularity();
ASSERT_TRUE(
IsAligned(kSubspaceMappedSize, rootspace.allocation_granularity()));
Address reservation = kNullAddress;
for (int i = 0; i < 10; i++) {
// Reserve the full size first at a random address, then free it again to
// ensure that there's enough free space behind the final reservation.
Address hint = rootspace.RandomPageAddress();
reservation = rootspace.AllocatePages(hint, kSubspaceSize,
rootspace.allocation_granularity(),
PagePermissions::kNoAccess);
ASSERT_NE(kNullAddress, reservation);
hint = reservation;
rootspace.FreePages(reservation, kSubspaceSize);
reservation =
rootspace.AllocatePages(hint, kSubspaceMappedSize, subspace_alignment,
PagePermissions::kNoAccess);
if (reservation == hint) {
break;
} else {
rootspace.FreePages(reservation, kSubspaceMappedSize);
reservation = kNullAddress;
}
}
ASSERT_NE(kNullAddress, reservation);
EmulatedVirtualAddressSubspace subspace(&rootspace, reservation,
kSubspaceMappedSize, kSubspaceSize);
EXPECT_EQ(reservation, subspace.base());
EXPECT_EQ(kSubspaceSize, subspace.size());
EXPECT_EQ(rootspace.max_page_permissions(), subspace.max_page_permissions());
TestRandomPageAddressGeneration(&subspace);
TestBasicPageAllocation(&subspace);
TestPageAllocationAlignment(&subspace);
// An emulated subspace does *not* guarantee that the parent space cannot
// allocate pages in it, so no TestParentSpaceCannotAllocateInChildSpace.
TestSharedPageAllocation(&subspace);
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,137 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cstring>
#include <initializer_list>
#include <limits>
#include "src/base/vlq-base64.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace base {
TEST(VLQBASE64, charToDigit) {
char kSyms[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for (int i = 0; i < 256; ++i) {
char* pos = strchr(kSyms, static_cast<char>(i));
int8_t expected = i == 0 || pos == nullptr ? -1 : pos - kSyms;
EXPECT_EQ(expected, charToDigitDecodeForTesting(static_cast<uint8_t>(i)));
}
}
struct ExpectedVLQBase64Result {
size_t pos;
int32_t result;
};
void TestVLQBase64Decode(
const char* str,
std::initializer_list<ExpectedVLQBase64Result> expected_results) {
size_t pos = 0;
for (const auto& expect : expected_results) {
int32_t result = VLQBase64Decode(str, strlen(str), &pos);
EXPECT_EQ(expect.result, result);
EXPECT_EQ(expect.pos, pos);
}
}
TEST(VLQBASE64, DecodeOneSegment) {
TestVLQBase64Decode("", {{0, std::numeric_limits<int32_t>::min()}});
// Unsupported symbol.
TestVLQBase64Decode("*", {{0, std::numeric_limits<int32_t>::min()}});
TestVLQBase64Decode("&", {{0, std::numeric_limits<int32_t>::min()}});
TestVLQBase64Decode("kt:", {{2, std::numeric_limits<int32_t>::min()}});
TestVLQBase64Decode("k^C", {{1, std::numeric_limits<int32_t>::min()}});
// Imcomplete string.
TestVLQBase64Decode("kth4yp", {{6, std::numeric_limits<int32_t>::min()}});
// Interpretable strings.
TestVLQBase64Decode("A", {{1, 0}});
TestVLQBase64Decode("C", {{1, 1}});
TestVLQBase64Decode("Y", {{1, 12}});
TestVLQBase64Decode("2H", {{2, 123}});
TestVLQBase64Decode("ktC", {{3, 1234}});
TestVLQBase64Decode("yjY", {{3, 12345}});
TestVLQBase64Decode("gkxH", {{4, 123456}});
TestVLQBase64Decode("uorrC", {{5, 1234567}});
TestVLQBase64Decode("80wxX", {{5, 12345678}});
TestVLQBase64Decode("qxmvrH", {{6, 123456789}});
TestVLQBase64Decode("kth4ypC", {{7, 1234567890}});
TestVLQBase64Decode("+/////D", {{7, std::numeric_limits<int32_t>::max()}});
TestVLQBase64Decode("D", {{1, -1}});
TestVLQBase64Decode("Z", {{1, -12}});
TestVLQBase64Decode("3H", {{2, -123}});
TestVLQBase64Decode("ltC", {{3, -1234}});
TestVLQBase64Decode("zjY", {{3, -12345}});
TestVLQBase64Decode("hkxH", {{4, -123456}});
TestVLQBase64Decode("vorrC", {{5, -1234567}});
TestVLQBase64Decode("90wxX", {{5, -12345678}});
TestVLQBase64Decode("rxmvrH", {{6, -123456789}});
TestVLQBase64Decode("lth4ypC", {{7, -1234567890}});
TestVLQBase64Decode("//////D", {{7, -std::numeric_limits<int32_t>::max()}});
// An overflowed value 12345678901 (0x2DFDC1C35).
TestVLQBase64Decode("qjuw7/2A", {{6, std::numeric_limits<int32_t>::min()}});
// An overflowed value 123456789012(0x1CBE991A14).
TestVLQBase64Decode("ohtkz+lH", {{6, std::numeric_limits<int32_t>::min()}});
// An overflowed value 4294967296 (0x100000000).
TestVLQBase64Decode("ggggggE", {{6, std::numeric_limits<int32_t>::min()}});
// An overflowed value -12345678901, |value| = (0x2DFDC1C35).
TestVLQBase64Decode("rjuw7/2A", {{6, std::numeric_limits<int32_t>::min()}});
// An overflowed value -123456789012,|value| = (0x1CBE991A14).
TestVLQBase64Decode("phtkz+lH", {{6, std::numeric_limits<int32_t>::min()}});
// An overflowed value -4294967296, |value| = (0x100000000).
TestVLQBase64Decode("hgggggE", {{6, std::numeric_limits<int32_t>::min()}});
}
TEST(VLQBASE64, DecodeTwoSegment) {
TestVLQBase64Decode("AA", {{1, 0}, {2, 0}});
TestVLQBase64Decode("KA", {{1, 5}, {2, 0}});
TestVLQBase64Decode("AQ", {{1, 0}, {2, 8}});
TestVLQBase64Decode("MG", {{1, 6}, {2, 3}});
TestVLQBase64Decode("a4E", {{1, 13}, {3, 76}});
TestVLQBase64Decode("4GyO", {{2, 108}, {4, 233}});
TestVLQBase64Decode("ggEqnD", {{3, 2048}, {6, 1653}});
TestVLQBase64Decode("g2/D0ilF", {{4, 65376}, {8, 84522}});
TestVLQBase64Decode("ss6gBy0m3B", {{5, 537798}, {10, 904521}});
TestVLQBase64Decode("LA", {{1, -5}, {2, 0}});
TestVLQBase64Decode("AR", {{1, 0}, {2, -8}});
TestVLQBase64Decode("NH", {{1, -6}, {2, -3}});
TestVLQBase64Decode("b5E", {{1, -13}, {3, -76}});
TestVLQBase64Decode("5GzO", {{2, -108}, {4, -233}});
TestVLQBase64Decode("hgErnD", {{3, -2048}, {6, -1653}});
TestVLQBase64Decode("h2/D1ilF", {{4, -65376}, {8, -84522}});
TestVLQBase64Decode("ts6gBz0m3B", {{5, -537798}, {10, -904521}});
TestVLQBase64Decode("4GzO", {{2, 108}, {4, -233}});
TestVLQBase64Decode("ggErnD", {{3, 2048}, {6, -1653}});
TestVLQBase64Decode("g2/D1ilF", {{4, 65376}, {8, -84522}});
TestVLQBase64Decode("ss6gBz0m3B", {{5, 537798}, {10, -904521}});
TestVLQBase64Decode("5GyO", {{2, -108}, {4, 233}});
TestVLQBase64Decode("hgEqnD", {{3, -2048}, {6, 1653}});
TestVLQBase64Decode("h2/D0ilF", {{4, -65376}, {8, 84522}});
TestVLQBase64Decode("ts6gBy0m3B", {{5, -537798}, {10, 904521}});
}
TEST(VLQBASE64, DecodeFourSegment) {
TestVLQBase64Decode("AAAA", {{1, 0}, {2, 0}, {3, 0}, {4, 0}});
TestVLQBase64Decode("QADA", {{1, 8}, {2, 0}, {3, -1}, {4, 0}});
TestVLQBase64Decode("ECQY", {{1, 2}, {2, 1}, {3, 8}, {4, 12}});
TestVLQBase64Decode("goGguCioPk9I",
{{3, 3200}, {6, 1248}, {9, 7809}, {12, 4562}});
TestVLQBase64Decode("6/BACA", {{3, 1021}, {4, 0}, {5, 1}, {6, 0}});
TestVLQBase64Decode("urCAQA", {{3, 1207}, {4, 0}, {5, 8}, {6, 0}});
TestVLQBase64Decode("sDACA", {{2, 54}, {3, 0}, {4, 1}, {5, 0}});
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,123 @@
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/vlq.h"
#include <cmath>
#include <limits>
#include "src/base/memory.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace base {
int ExpectedBytesUsed(int64_t value, bool is_signed) {
uint64_t bits = value;
if (is_signed) {
bits = (value < 0 ? -value : value) << 1;
}
int num_bits = 0;
while (bits != 0) {
num_bits++;
bits >>= 1;
}
return std::max(1, static_cast<int>(ceil(static_cast<float>(num_bits) / 7)));
}
void TestVLQUnsignedEquals(uint32_t value) {
std::vector<uint8_t> buffer;
VLQEncodeUnsigned(&buffer, value);
uint8_t* data_start = buffer.data();
int index = 0;
int expected_bytes_used = ExpectedBytesUsed(value, false);
EXPECT_EQ(buffer.size(), static_cast<size_t>(expected_bytes_used));
EXPECT_EQ(value, VLQDecodeUnsigned(data_start, &index));
EXPECT_EQ(index, expected_bytes_used);
}
void TestVLQEquals(int32_t value) {
std::vector<uint8_t> buffer;
VLQEncode(&buffer, value);
uint8_t* data_start = buffer.data();
int index = 0;
int expected_bytes_used = ExpectedBytesUsed(value, true);
EXPECT_EQ(buffer.size(), static_cast<size_t>(expected_bytes_used));
EXPECT_EQ(value, VLQDecode(data_start, &index));
EXPECT_EQ(index, expected_bytes_used);
}
TEST(VLQ, Unsigned) {
TestVLQUnsignedEquals(0);
TestVLQUnsignedEquals(1);
TestVLQUnsignedEquals(63);
TestVLQUnsignedEquals(64);
TestVLQUnsignedEquals(127);
TestVLQUnsignedEquals(255);
TestVLQUnsignedEquals(256);
}
TEST(VLQ, Positive) {
TestVLQEquals(0);
TestVLQEquals(1);
TestVLQEquals(63);
TestVLQEquals(64);
TestVLQEquals(127);
TestVLQEquals(255);
TestVLQEquals(256);
}
TEST(VLQ, Negative) {
TestVLQEquals(-1);
TestVLQEquals(-63);
TestVLQEquals(-64);
TestVLQEquals(-127);
TestVLQEquals(-255);
TestVLQEquals(-256);
}
TEST(VLQ, LimitsUnsigned) {
TestVLQEquals(std::numeric_limits<uint8_t>::max());
TestVLQEquals(std::numeric_limits<uint8_t>::max() - 1);
TestVLQEquals(std::numeric_limits<uint8_t>::max() + 1);
TestVLQEquals(std::numeric_limits<uint16_t>::max());
TestVLQEquals(std::numeric_limits<uint16_t>::max() - 1);
TestVLQEquals(std::numeric_limits<uint16_t>::max() + 1);
TestVLQEquals(std::numeric_limits<uint32_t>::max());
TestVLQEquals(std::numeric_limits<uint32_t>::max() - 1);
}
TEST(VLQ, LimitsSigned) {
TestVLQEquals(std::numeric_limits<int8_t>::max());
TestVLQEquals(std::numeric_limits<int8_t>::max() - 1);
TestVLQEquals(std::numeric_limits<int8_t>::max() + 1);
TestVLQEquals(std::numeric_limits<int16_t>::max());
TestVLQEquals(std::numeric_limits<int16_t>::max() - 1);
TestVLQEquals(std::numeric_limits<int16_t>::max() + 1);
TestVLQEquals(std::numeric_limits<int32_t>::max());
TestVLQEquals(std::numeric_limits<int32_t>::max() - 1);
TestVLQEquals(std::numeric_limits<int8_t>::min());
TestVLQEquals(std::numeric_limits<int8_t>::min() - 1);
TestVLQEquals(std::numeric_limits<int8_t>::min() + 1);
TestVLQEquals(std::numeric_limits<int16_t>::min());
TestVLQEquals(std::numeric_limits<int16_t>::min() - 1);
TestVLQEquals(std::numeric_limits<int16_t>::min() + 1);
// int32_t::min() is not supported.
TestVLQEquals(std::numeric_limits<int32_t>::min() + 1);
}
TEST(VLQ, Random) {
static constexpr int RANDOM_RUNS = 50;
base::RandomNumberGenerator rng(GTEST_FLAG_GET(random_seed));
for (int i = 0; i < RANDOM_RUNS; ++i) {
TestVLQUnsignedEquals(rng.NextInt(std::numeric_limits<int32_t>::max()));
}
for (int i = 0; i < RANDOM_RUNS; ++i) {
TestVLQEquals(rng.NextInt());
}
}
} // namespace base
} // namespace v8