Initial Commit

This commit is contained in:
misterg 2017-09-19 16:54:40 -04:00
commit c2e7548296
238 changed files with 65475 additions and 0 deletions

124
absl/container/BUILD.bazel Normal file
View file

@ -0,0 +1,124 @@
#
# Copyright 2017 The Abseil Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
load(
"//absl:copts.bzl",
"ABSL_DEFAULT_COPTS",
"ABSL_TEST_COPTS",
)
load(
"//absl:test_dependencies.bzl",
"GUNIT_MAIN_DEPS_SELECTOR",
)
package(default_visibility = ["//visibility:public"])
licenses(["notice"]) # Apache 2.0
cc_library(
name = "fixed_array",
hdrs = ["fixed_array.h"],
copts = ABSL_DEFAULT_COPTS,
deps = [
"//absl/algorithm",
"//absl/base:core_headers",
"//absl/base:dynamic_annotations",
"//absl/base:throw_delegate",
],
)
cc_test(
name = "fixed_array_test",
srcs = ["fixed_array_test.cc"],
copts = ABSL_TEST_COPTS + ["-fexceptions"],
deps = [
":fixed_array",
"//absl/base:core_headers",
"//absl/base:exception_testing",
"//absl/memory",
] + select(GUNIT_MAIN_DEPS_SELECTOR),
)
cc_test(
name = "fixed_array_test_noexceptions",
srcs = ["fixed_array_test.cc"],
copts = ABSL_TEST_COPTS,
deps = [
":fixed_array",
"//absl/base:core_headers",
"//absl/base:exception_testing",
"//absl/memory",
] + select(GUNIT_MAIN_DEPS_SELECTOR),
)
cc_library(
name = "inlined_vector",
hdrs = ["inlined_vector.h"],
copts = ABSL_DEFAULT_COPTS,
deps = [
"//absl/algorithm",
"//absl/base:core_headers",
"//absl/base:throw_delegate",
"//absl/memory",
],
)
cc_test(
name = "inlined_vector_test",
srcs = ["inlined_vector_test.cc"],
copts = ABSL_TEST_COPTS + ["-fexceptions"],
deps = [
":inlined_vector",
":test_instance_tracker",
"//absl/base",
"//absl/base:core_headers",
"//absl/base:exception_testing",
"//absl/memory",
"//absl/strings",
] + select(GUNIT_MAIN_DEPS_SELECTOR),
)
cc_test(
name = "inlined_vector_test_noexceptions",
srcs = ["inlined_vector_test.cc"],
copts = ABSL_TEST_COPTS,
deps = [
":inlined_vector",
":test_instance_tracker",
"//absl/base",
"//absl/base:core_headers",
"//absl/base:exception_testing",
"//absl/memory",
"//absl/strings",
] + select(GUNIT_MAIN_DEPS_SELECTOR),
)
cc_library(
name = "test_instance_tracker",
testonly = 1,
srcs = ["internal/test_instance_tracker.cc"],
hdrs = ["internal/test_instance_tracker.h"],
copts = ABSL_DEFAULT_COPTS,
)
cc_test(
name = "test_instance_tracker_test",
srcs = ["internal/test_instance_tracker_test.cc"],
copts = ABSL_TEST_COPTS,
deps = [
":test_instance_tracker",
] + select(GUNIT_MAIN_DEPS_SELECTOR),
)

View file

@ -0,0 +1,493 @@
// Copyright 2017 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// -----------------------------------------------------------------------------
// File: fixed_array.h
// -----------------------------------------------------------------------------
//
// A `FixedArray<T>` represents a non-resizable array of `T` where the length of
// the array can be determined at run-time. It is a good replacement for
// non-standard and deprecated uses of `alloca()` and variable length arrays
// within the GCC extension. (See
// https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html).
//
// `FixedArray` allocates small arrays inline, keeping performance fast by
// avoiding heap operations. It also helps reduce the chances of
// accidentally overflowing your stack if large input is passed to
// your function.
#ifndef ABSL_CONTAINER_FIXED_ARRAY_H_
#define ABSL_CONTAINER_FIXED_ARRAY_H_
#include <algorithm>
#include <array>
#include <cassert>
#include <cstddef>
#include <initializer_list>
#include <iterator>
#include <limits>
#include <memory>
#include <new>
#include <type_traits>
#include "absl/algorithm/algorithm.h"
#include "absl/base/dynamic_annotations.h"
#include "absl/base/internal/throw_delegate.h"
#include "absl/base/macros.h"
#include "absl/base/optimization.h"
#include "absl/base/port.h"
namespace absl {
constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1);
// -----------------------------------------------------------------------------
// FixedArray
// -----------------------------------------------------------------------------
//
// A `FixedArray` provides a run-time fixed-size array, allocating small arrays
// inline for efficiency and correctness.
//
// Most users should not specify an `inline_elements` argument and let
// `FixedArray<>` automatically determine the number of elements
// to store inline based on `sizeof(T)`. If `inline_elements` is specified, the
// `FixedArray<>` implementation will inline arrays of
// length <= `inline_elements`.
//
// Note that a `FixedArray` constructed with a `size_type` argument will
// default-initialize its values by leaving trivially constructible types
// uninitialized (e.g. int, int[4], double), and others default-constructed.
// This matches the behavior of c-style arrays and `std::array`, but not
// `std::vector`.
//
// Note that `FixedArray` does not provide a public allocator; if it requires a
// heap allocation, it will do so with global `::operator new[]()` and
// `::operator delete[]()`, even if T provides class-scope overrides for these
// operators.
template <typename T, size_t inlined = kFixedArrayUseDefault>
class FixedArray {
static constexpr size_t kInlineBytesDefault = 256;
// std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17,
// but this seems to be mostly pedantic.
template <typename Iter>
using EnableIfForwardIterator = typename std::enable_if<
std::is_convertible<
typename std::iterator_traits<Iter>::iterator_category,
std::forward_iterator_tag>::value,
int>::type;
public:
// For playing nicely with stl:
using value_type = T;
using iterator = T*;
using const_iterator = const T*;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using reference = T&;
using const_reference = const T&;
using pointer = T*;
using const_pointer = const T*;
using difference_type = ptrdiff_t;
using size_type = size_t;
static constexpr size_type inline_elements =
inlined == kFixedArrayUseDefault
? kInlineBytesDefault / sizeof(value_type)
: inlined;
// Creates an array object that can store `n` elements.
// Note that trivially constructible elements will be uninitialized.
explicit FixedArray(size_type n) : rep_(n) {}
// Creates an array initialized with `n` copies of `val`.
FixedArray(size_type n, const value_type& val) : rep_(n, val) {}
// Creates an array initialized with the elements from the input
// range. The array's size will always be `std::distance(first, last)`.
// REQUIRES: Iter must be a forward_iterator or better.
template <typename Iter, EnableIfForwardIterator<Iter> = 0>
FixedArray(Iter first, Iter last) : rep_(first, last) {}
// Create the array from an initializer_list.
FixedArray(std::initializer_list<T> init_list)
: FixedArray(init_list.begin(), init_list.end()) {}
~FixedArray() {}
// Copy and move construction and assignment are deleted because (1) you can't
// copy or move an array, (2) assignment breaks the invariant that the size of
// a `FixedArray` never changes, and (3) there's no clear answer as to what
// should happen to a moved-from `FixedArray`.
FixedArray(const FixedArray&) = delete;
void operator=(const FixedArray&) = delete;
// FixedArray::size()
//
// Returns the length of the fixed array.
size_type size() const { return rep_.size(); }
// FixedArray::max_size()
//
// Returns the largest possible value of `std::distance(begin(), end())` for a
// `FixedArray<T>`. This is equivalent to the most possible addressable bytes
// over the number of bytes taken by T.
constexpr size_type max_size() const {
return std::numeric_limits<difference_type>::max() / sizeof(value_type);
}
// FixedArray::empty()
//
// Returns whether or not the fixed array is empty.
bool empty() const { return size() == 0; }
// FixedArray::memsize()
//
// Returns the memory size of the fixed array in bytes.
size_t memsize() const { return size() * sizeof(value_type); }
// FixedArray::data()
//
// Returns a const T* pointer to elements of the `FixedArray`. This pointer
// can be used to access (but not modify) the contained elements.
const_pointer data() const { return AsValue(rep_.begin()); }
// Overload of FixedArray::data() to return a T* pointer to elements of the
// fixed array. This pointer can be used to access and modify the contained
// elements.
pointer data() { return AsValue(rep_.begin()); }
// FixedArray::operator[]
//
// Returns a reference the ith element of the fixed array.
// REQUIRES: 0 <= i < size()
reference operator[](size_type i) {
assert(i < size());
return data()[i];
}
// Overload of FixedArray::operator()[] to return a const reference to the
// ith element of the fixed array.
// REQUIRES: 0 <= i < size()
const_reference operator[](size_type i) const {
assert(i < size());
return data()[i];
}
// FixedArray::at
//
// Bounds-checked access. Returns a reference to the ith element of the
// fiexed array, or throws std::out_of_range
reference at(size_type i) {
if (ABSL_PREDICT_FALSE(i >= size())) {
base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check");
}
return data()[i];
}
// Overload of FixedArray::at() to return a const reference to the ith element
// of the fixed array.
const_reference at(size_type i) const {
if (i >= size()) {
base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check");
}
return data()[i];
}
// FixedArray::front()
//
// Returns a reference to the first element of the fixed array.
reference front() { return *begin(); }
// Overload of FixedArray::front() to return a reference to the first element
// of a fixed array of const values.
const_reference front() const { return *begin(); }
// FixedArray::back()
//
// Returns a reference to the last element of the fixed array.
reference back() { return *(end() - 1); }
// Overload of FixedArray::back() to return a reference to the last element
// of a fixed array of const values.
const_reference back() const { return *(end() - 1); }
// FixedArray::begin()
//
// Returns an iterator to the beginning of the fixed array.
iterator begin() { return data(); }
// Overload of FixedArray::begin() to return a const iterator to the
// beginning of the fixed array.
const_iterator begin() const { return data(); }
// FixedArray::cbegin()
//
// Returns a const iterator to the beginning of the fixed array.
const_iterator cbegin() const { return begin(); }
// FixedArray::end()
//
// Returns an iterator to the end of the fixed array.
iterator end() { return data() + size(); }
// Overload of FixedArray::end() to return a const iterator to the end of the
// fixed array.
const_iterator end() const { return data() + size(); }
// FixedArray::cend()
//
// Returns a const iterator to the end of the fixed array.
const_iterator cend() const { return end(); }
// FixedArray::rbegin()
//
// Returns a reverse iterator from the end of the fixed array.
reverse_iterator rbegin() { return reverse_iterator(end()); }
// Overload of FixedArray::rbegin() to return a const reverse iterator from
// the end of the fixed array.
const_reverse_iterator rbegin() const {
return const_reverse_iterator(end());
}
// FixedArray::crbegin()
//
// Returns a const reverse iterator from the end of the fixed array.
const_reverse_iterator crbegin() const { return rbegin(); }
// FixedArray::rend()
//
// Returns a reverse iterator from the beginning of the fixed array.
reverse_iterator rend() { return reverse_iterator(begin()); }
// Overload of FixedArray::rend() for returning a const reverse iterator
// from the beginning of the fixed array.
const_reverse_iterator rend() const {
return const_reverse_iterator(begin());
}
// FixedArray::crend()
//
// Returns a reverse iterator from the beginning of the fixed array.
const_reverse_iterator crend() const { return rend(); }
// FixedArray::fill()
//
// Assigns the given `value` to all elements in the fixed array.
void fill(const T& value) { std::fill(begin(), end(), value); }
// Relational operators. Equality operators are elementwise using
// `operator==`, while order operators order FixedArrays lexicographically.
friend bool operator==(const FixedArray& lhs, const FixedArray& rhs) {
return absl::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}
friend bool operator!=(const FixedArray& lhs, const FixedArray& rhs) {
return !(lhs == rhs);
}
friend bool operator<(const FixedArray& lhs, const FixedArray& rhs) {
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(),
rhs.end());
}
friend bool operator>(const FixedArray& lhs, const FixedArray& rhs) {
return rhs < lhs;
}
friend bool operator<=(const FixedArray& lhs, const FixedArray& rhs) {
return !(rhs < lhs);
}
friend bool operator>=(const FixedArray& lhs, const FixedArray& rhs) {
return !(lhs < rhs);
}
private:
// HolderTraits
//
// Wrapper to hold elements of type T for the case where T is an array type.
// If 'T' is an array type, HolderTraits::type is a struct with a 'T v;'.
// Otherwise, HolderTraits::type is simply 'T'.
//
// Maintainer's Note: The simpler solution would be to simply wrap T in a
// struct whether it's an array or not: 'struct Holder { T v; };', but
// that causes some paranoid diagnostics to misfire about uses of data(),
// believing that 'data()' (aka '&rep_.begin().v') is a pointer to a single
// element, rather than the packed array that it really is.
// e.g.:
//
// FixedArray<char> buf(1);
// sprintf(buf.data(), "foo");
//
// error: call to int __builtin___sprintf_chk(etc...)
// will always overflow destination buffer [-Werror]
//
class HolderTraits {
template <typename U>
struct SelectImpl {
using type = U;
static pointer AsValue(type* p) { return p; }
};
// Partial specialization for elements of array type.
template <typename U, size_t N>
struct SelectImpl<U[N]> {
struct Holder { U v[N]; };
using type = Holder;
static pointer AsValue(type* p) { return &p->v; }
};
using Impl = SelectImpl<value_type>;
public:
using type = typename Impl::type;
static pointer AsValue(type *p) { return Impl::AsValue(p); }
// TODO(billydonahue): fix the type aliasing violation
// this assertion hints at.
static_assert(sizeof(type) == sizeof(value_type),
"Holder must be same size as value_type");
};
using Holder = typename HolderTraits::type;
static pointer AsValue(Holder *p) { return HolderTraits::AsValue(p); }
// InlineSpace
//
// Allocate some space, not an array of elements of type T, so that we can
// skip calling the T constructors and destructors for space we never use.
// How many elements should we store inline?
// a. If not specified, use a default of kInlineBytesDefault bytes (This is
// currently 256 bytes, which seems small enough to not cause stack overflow
// or unnecessary stack pollution, while still allowing stack allocation for
// reasonably long character arrays).
// b. Never use 0 length arrays (not ISO C++)
//
template <size_type N, typename = void>
class InlineSpace {
public:
Holder* data() { return reinterpret_cast<Holder*>(space_.data()); }
void AnnotateConstruct(size_t n) const { Annotate(n, true); }
void AnnotateDestruct(size_t n) const { Annotate(n, false); }
private:
#ifndef ADDRESS_SANITIZER
void Annotate(size_t, bool) const { }
#else
void Annotate(size_t n, bool creating) const {
if (!n) return;
const void* bot = &left_redzone_;
const void* beg = space_.data();
const void* end = space_.data() + n;
const void* top = &right_redzone_ + 1;
// args: (beg, end, old_mid, new_mid)
if (creating) {
ANNOTATE_CONTIGUOUS_CONTAINER(beg, top, top, end);
ANNOTATE_CONTIGUOUS_CONTAINER(bot, beg, beg, bot);
} else {
ANNOTATE_CONTIGUOUS_CONTAINER(beg, top, end, top);
ANNOTATE_CONTIGUOUS_CONTAINER(bot, beg, bot, beg);
}
}
#endif // ADDRESS_SANITIZER
using Buffer =
typename std::aligned_storage<sizeof(Holder), alignof(Holder)>::type;
ADDRESS_SANITIZER_REDZONE(left_redzone_);
std::array<Buffer, N> space_;
ADDRESS_SANITIZER_REDZONE(right_redzone_);
};
// specialization when N = 0.
template <typename U>
class InlineSpace<0, U> {
public:
Holder* data() { return nullptr; }
void AnnotateConstruct(size_t) const {}
void AnnotateDestruct(size_t) const {}
};
// Rep
//
// A const Rep object holds FixedArray's size and data pointer.
//
class Rep : public InlineSpace<inline_elements> {
public:
Rep(size_type n, const value_type& val) : n_(n), p_(MakeHolder(n)) {
std::uninitialized_fill_n(p_, n, val);
}
explicit Rep(size_type n) : n_(n), p_(MakeHolder(n)) {
// Loop optimizes to nothing for trivially constructible T.
for (Holder* p = p_; p != p_ + n; ++p)
// Note: no parens: default init only.
// Also note '::' to avoid Holder class placement new operator.
::new (static_cast<void*>(p)) Holder;
}
template <typename Iter>
Rep(Iter first, Iter last)
: n_(std::distance(first, last)), p_(MakeHolder(n_)) {
std::uninitialized_copy(first, last, AsValue(p_));
}
~Rep() {
// Destruction must be in reverse order.
// Loop optimizes to nothing for trivially destructible T.
for (Holder* p = end(); p != begin();) (--p)->~Holder();
if (IsAllocated(size())) {
::operator delete[](begin());
} else {
this->AnnotateDestruct(size());
}
}
Holder* begin() const { return p_; }
Holder* end() const { return p_ + n_; }
size_type size() const { return n_; }
private:
Holder* MakeHolder(size_type n) {
if (IsAllocated(n)) {
return Allocate(n);
} else {
this->AnnotateConstruct(n);
return this->data();
}
}
Holder* Allocate(size_type n) {
return static_cast<Holder*>(::operator new[](n * sizeof(Holder)));
}
bool IsAllocated(size_type n) const { return n > inline_elements; }
const size_type n_;
Holder* const p_;
};
// Data members
Rep rep_;
};
template <typename T, size_t N>
constexpr size_t FixedArray<T, N>::inline_elements;
template <typename T, size_t N>
constexpr size_t FixedArray<T, N>::kInlineBytesDefault;
} // namespace absl
#endif // ABSL_CONTAINER_FIXED_ARRAY_H_

View file

@ -0,0 +1,621 @@
// Copyright 2017 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/container/fixed_array.h"
#include <stdio.h>
#include <list>
#include <memory>
#include <numeric>
#include <stdexcept>
#include <string>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/internal/exception_testing.h"
#include "absl/memory/memory.h"
namespace {
// Helper routine to determine if a absl::FixedArray used stack allocation.
template <typename ArrayType>
static bool IsOnStack(const ArrayType& a) {
return a.size() <= ArrayType::inline_elements;
}
class ConstructionTester {
public:
ConstructionTester()
: self_ptr_(this),
value_(0) {
constructions++;
}
~ConstructionTester() {
assert(self_ptr_ == this);
self_ptr_ = nullptr;
destructions++;
}
// These are incremented as elements are constructed and destructed so we can
// be sure all elements are properly cleaned up.
static int constructions;
static int destructions;
void CheckConstructed() {
assert(self_ptr_ == this);
}
void set(int value) { value_ = value; }
int get() { return value_; }
private:
// self_ptr_ should always point to 'this' -- that's how we can be sure the
// constructor has been called.
ConstructionTester* self_ptr_;
int value_;
};
int ConstructionTester::constructions = 0;
int ConstructionTester::destructions = 0;
// ThreeInts will initialize its three ints to the value stored in
// ThreeInts::counter. The constructor increments counter so that each object
// in an array of ThreeInts will have different values.
class ThreeInts {
public:
ThreeInts() {
x_ = counter;
y_ = counter;
z_ = counter;
++counter;
}
static int counter;
int x_, y_, z_;
};
int ThreeInts::counter = 0;
TEST(FixedArrayTest, SmallObjects) {
// Small object arrays
{
// Short arrays should be on the stack
absl::FixedArray<int> array(4);
EXPECT_TRUE(IsOnStack(array));
}
{
// Large arrays should be on the heap
absl::FixedArray<int> array(1048576);
EXPECT_FALSE(IsOnStack(array));
}
{
// Arrays of <= default size should be on the stack
absl::FixedArray<int, 100> array(100);
EXPECT_TRUE(IsOnStack(array));
}
{
// Arrays of > default size should be on the stack
absl::FixedArray<int, 100> array(101);
EXPECT_FALSE(IsOnStack(array));
}
{
// Arrays with different size elements should use approximately
// same amount of stack space
absl::FixedArray<int> array1(0);
absl::FixedArray<char> array2(0);
EXPECT_LE(sizeof(array1), sizeof(array2)+100);
EXPECT_LE(sizeof(array2), sizeof(array1)+100);
}
{
// Ensure that vectors are properly constructed inside a fixed array.
absl::FixedArray<std::vector<int> > array(2);
EXPECT_EQ(0, array[0].size());
EXPECT_EQ(0, array[1].size());
}
{
// Regardless of absl::FixedArray implementation, check that a type with a
// low alignment requirement and a non power-of-two size is initialized
// correctly.
ThreeInts::counter = 1;
absl::FixedArray<ThreeInts> array(2);
EXPECT_EQ(1, array[0].x_);
EXPECT_EQ(1, array[0].y_);
EXPECT_EQ(1, array[0].z_);
EXPECT_EQ(2, array[1].x_);
EXPECT_EQ(2, array[1].y_);
EXPECT_EQ(2, array[1].z_);
}
}
TEST(FixedArrayTest, AtThrows) {
absl::FixedArray<int> a = {1, 2, 3};
EXPECT_EQ(a.at(2), 3);
ABSL_BASE_INTERNAL_EXPECT_FAIL(a.at(3), std::out_of_range,
"failed bounds check");
}
TEST(FixedArrayRelationalsTest, EqualArrays) {
for (int i = 0; i < 10; ++i) {
absl::FixedArray<int, 5> a1(i);
std::iota(a1.begin(), a1.end(), 0);
absl::FixedArray<int, 5> a2(a1.begin(), a1.end());
EXPECT_TRUE(a1 == a2);
EXPECT_FALSE(a1 != a2);
EXPECT_TRUE(a2 == a1);
EXPECT_FALSE(a2 != a1);
EXPECT_FALSE(a1 < a2);
EXPECT_FALSE(a1 > a2);
EXPECT_FALSE(a2 < a1);
EXPECT_FALSE(a2 > a1);
EXPECT_TRUE(a1 <= a2);
EXPECT_TRUE(a1 >= a2);
EXPECT_TRUE(a2 <= a1);
EXPECT_TRUE(a2 >= a1);
}
}
TEST(FixedArrayRelationalsTest, UnequalArrays) {
for (int i = 1; i < 10; ++i) {
absl::FixedArray<int, 5> a1(i);
std::iota(a1.begin(), a1.end(), 0);
absl::FixedArray<int, 5> a2(a1.begin(), a1.end());
--a2[i / 2];
EXPECT_FALSE(a1 == a2);
EXPECT_TRUE(a1 != a2);
EXPECT_FALSE(a2 == a1);
EXPECT_TRUE(a2 != a1);
EXPECT_FALSE(a1 < a2);
EXPECT_TRUE(a1 > a2);
EXPECT_TRUE(a2 < a1);
EXPECT_FALSE(a2 > a1);
EXPECT_FALSE(a1 <= a2);
EXPECT_TRUE(a1 >= a2);
EXPECT_TRUE(a2 <= a1);
EXPECT_FALSE(a2 >= a1);
}
}
template <int stack_elements>
static void TestArray(int n) {
SCOPED_TRACE(n);
SCOPED_TRACE(stack_elements);
ConstructionTester::constructions = 0;
ConstructionTester::destructions = 0;
{
absl::FixedArray<ConstructionTester, stack_elements> array(n);
EXPECT_THAT(array.size(), n);
EXPECT_THAT(array.memsize(), sizeof(ConstructionTester) * n);
EXPECT_THAT(array.begin() + n, array.end());
// Check that all elements were constructed
for (int i = 0; i < n; i++) {
array[i].CheckConstructed();
}
// Check that no other elements were constructed
EXPECT_THAT(ConstructionTester::constructions, n);
// Test operator[]
for (int i = 0; i < n; i++) {
array[i].set(i);
}
for (int i = 0; i < n; i++) {
EXPECT_THAT(array[i].get(), i);
EXPECT_THAT(array.data()[i].get(), i);
}
// Test data()
for (int i = 0; i < n; i++) {
array.data()[i].set(i + 1);
}
for (int i = 0; i < n; i++) {
EXPECT_THAT(array[i].get(), i+1);
EXPECT_THAT(array.data()[i].get(), i+1);
}
} // Close scope containing 'array'.
// Check that all constructed elements were destructed.
EXPECT_EQ(ConstructionTester::constructions,
ConstructionTester::destructions);
}
template <int elements_per_inner_array, int inline_elements>
static void TestArrayOfArrays(int n) {
SCOPED_TRACE(n);
SCOPED_TRACE(inline_elements);
SCOPED_TRACE(elements_per_inner_array);
ConstructionTester::constructions = 0;
ConstructionTester::destructions = 0;
{
using InnerArray = ConstructionTester[elements_per_inner_array];
// Heap-allocate the FixedArray to avoid blowing the stack frame.
auto array_ptr =
absl::make_unique<absl::FixedArray<InnerArray, inline_elements>>(n);
auto& array = *array_ptr;
ASSERT_EQ(array.size(), n);
ASSERT_EQ(array.memsize(),
sizeof(ConstructionTester) * elements_per_inner_array * n);
ASSERT_EQ(array.begin() + n, array.end());
// Check that all elements were constructed
for (int i = 0; i < n; i++) {
for (int j = 0; j < elements_per_inner_array; j++) {
(array[i])[j].CheckConstructed();
}
}
// Check that no other elements were constructed
ASSERT_EQ(ConstructionTester::constructions, n * elements_per_inner_array);
// Test operator[]
for (int i = 0; i < n; i++) {
for (int j = 0; j < elements_per_inner_array; j++) {
(array[i])[j].set(i * elements_per_inner_array + j);
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < elements_per_inner_array; j++) {
ASSERT_EQ((array[i])[j].get(), i * elements_per_inner_array + j);
ASSERT_EQ((array.data()[i])[j].get(), i * elements_per_inner_array + j);
}
}
// Test data()
for (int i = 0; i < n; i++) {
for (int j = 0; j < elements_per_inner_array; j++) {
(array.data()[i])[j].set((i + 1) * elements_per_inner_array + j);
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < elements_per_inner_array; j++) {
ASSERT_EQ((array[i])[j].get(),
(i + 1) * elements_per_inner_array + j);
ASSERT_EQ((array.data()[i])[j].get(),
(i + 1) * elements_per_inner_array + j);
}
}
} // Close scope containing 'array'.
// Check that all constructed elements were destructed.
EXPECT_EQ(ConstructionTester::constructions,
ConstructionTester::destructions);
}
TEST(IteratorConstructorTest, NonInline) {
int const kInput[] = { 2, 3, 5, 7, 11, 13, 17 };
absl::FixedArray<int, ABSL_ARRAYSIZE(kInput) - 1> const fixed(
kInput, kInput + ABSL_ARRAYSIZE(kInput));
ASSERT_EQ(ABSL_ARRAYSIZE(kInput), fixed.size());
for (size_t i = 0; i < ABSL_ARRAYSIZE(kInput); ++i) {
ASSERT_EQ(kInput[i], fixed[i]);
}
}
TEST(IteratorConstructorTest, Inline) {
int const kInput[] = { 2, 3, 5, 7, 11, 13, 17 };
absl::FixedArray<int, ABSL_ARRAYSIZE(kInput)> const fixed(
kInput, kInput + ABSL_ARRAYSIZE(kInput));
ASSERT_EQ(ABSL_ARRAYSIZE(kInput), fixed.size());
for (size_t i = 0; i < ABSL_ARRAYSIZE(kInput); ++i) {
ASSERT_EQ(kInput[i], fixed[i]);
}
}
TEST(IteratorConstructorTest, NonPod) {
char const* kInput[] =
{ "red", "orange", "yellow", "green", "blue", "indigo", "violet" };
absl::FixedArray<std::string> const fixed(kInput, kInput + ABSL_ARRAYSIZE(kInput));
ASSERT_EQ(ABSL_ARRAYSIZE(kInput), fixed.size());
for (size_t i = 0; i < ABSL_ARRAYSIZE(kInput); ++i) {
ASSERT_EQ(kInput[i], fixed[i]);
}
}
TEST(IteratorConstructorTest, FromEmptyVector) {
std::vector<int> const empty;
absl::FixedArray<int> const fixed(empty.begin(), empty.end());
EXPECT_EQ(0, fixed.size());
EXPECT_EQ(empty.size(), fixed.size());
}
TEST(IteratorConstructorTest, FromNonEmptyVector) {
int const kInput[] = { 2, 3, 5, 7, 11, 13, 17 };
std::vector<int> const items(kInput, kInput + ABSL_ARRAYSIZE(kInput));
absl::FixedArray<int> const fixed(items.begin(), items.end());
ASSERT_EQ(items.size(), fixed.size());
for (size_t i = 0; i < items.size(); ++i) {
ASSERT_EQ(items[i], fixed[i]);
}
}
TEST(IteratorConstructorTest, FromBidirectionalIteratorRange) {
int const kInput[] = { 2, 3, 5, 7, 11, 13, 17 };
std::list<int> const items(kInput, kInput + ABSL_ARRAYSIZE(kInput));
absl::FixedArray<int> const fixed(items.begin(), items.end());
EXPECT_THAT(fixed, testing::ElementsAreArray(kInput));
}
TEST(InitListConstructorTest, InitListConstruction) {
absl::FixedArray<int> fixed = {1, 2, 3};
EXPECT_THAT(fixed, testing::ElementsAreArray({1, 2, 3}));
}
TEST(FillConstructorTest, NonEmptyArrays) {
absl::FixedArray<int> stack_array(4, 1);
EXPECT_THAT(stack_array, testing::ElementsAreArray({1, 1, 1, 1}));
absl::FixedArray<int, 0> heap_array(4, 1);
EXPECT_THAT(stack_array, testing::ElementsAreArray({1, 1, 1, 1}));
}
TEST(FillConstructorTest, EmptyArray) {
absl::FixedArray<int> empty_fill(0, 1);
absl::FixedArray<int> empty_size(0);
EXPECT_EQ(empty_fill, empty_size);
}
TEST(FillConstructorTest, NotTriviallyCopyable) {
std::string str = "abcd";
absl::FixedArray<std::string> strings = {str, str, str, str};
absl::FixedArray<std::string> array(4, str);
EXPECT_EQ(array, strings);
}
TEST(FillConstructorTest, Disambiguation) {
absl::FixedArray<size_t> a(1, 2);
EXPECT_THAT(a, testing::ElementsAre(2));
}
TEST(FixedArrayTest, ManySizedArrays) {
std::vector<int> sizes;
for (int i = 1; i < 100; i++) sizes.push_back(i);
for (int i = 100; i <= 1000; i += 100) sizes.push_back(i);
for (int n : sizes) {
TestArray<0>(n);
TestArray<1>(n);
TestArray<64>(n);
TestArray<1000>(n);
}
}
TEST(FixedArrayTest, ManySizedArraysOfArraysOf1) {
for (int n = 1; n < 1000; n++) {
ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 0>(n)));
ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 1>(n)));
ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 64>(n)));
ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 1000>(n)));
}
}
TEST(FixedArrayTest, ManySizedArraysOfArraysOf2) {
for (int n = 1; n < 1000; n++) {
TestArrayOfArrays<2, 0>(n);
TestArrayOfArrays<2, 1>(n);
TestArrayOfArrays<2, 64>(n);
TestArrayOfArrays<2, 1000>(n);
}
}
// If value_type is put inside of a struct container,
// we might evoke this error in a hardened build unless data() is carefully
// written, so check on that.
// error: call to int __builtin___sprintf_chk(etc...)
// will always overflow destination buffer [-Werror]
TEST(FixedArrayTest, AvoidParanoidDiagnostics) {
absl::FixedArray<char, 32> buf(32);
sprintf(buf.data(), "foo"); // NOLINT(runtime/printf)
}
TEST(FixedArrayTest, TooBigInlinedSpace) {
struct TooBig {
char c[1 << 20];
}; // too big for even one on the stack
// Simulate the data members of absl::FixedArray, a pointer and a size_t.
struct Data {
TooBig* p;
size_t size;
};
// Make sure TooBig objects are not inlined for 0 or default size.
static_assert(sizeof(absl::FixedArray<TooBig, 0>) == sizeof(Data),
"0-sized absl::FixedArray should have same size as Data.");
static_assert(alignof(absl::FixedArray<TooBig, 0>) == alignof(Data),
"0-sized absl::FixedArray should have same alignment as Data.");
static_assert(sizeof(absl::FixedArray<TooBig>) == sizeof(Data),
"default-sized absl::FixedArray should have same size as Data");
static_assert(
alignof(absl::FixedArray<TooBig>) == alignof(Data),
"default-sized absl::FixedArray should have same alignment as Data.");
}
// PickyDelete EXPECTs its class-scope deallocation funcs are unused.
struct PickyDelete {
PickyDelete() {}
~PickyDelete() {}
void operator delete(void* p) {
EXPECT_TRUE(false) << __FUNCTION__;
::operator delete(p);
}
void operator delete[](void* p) {
EXPECT_TRUE(false) << __FUNCTION__;
::operator delete[](p);
}
};
TEST(FixedArrayTest, UsesGlobalAlloc) { absl::FixedArray<PickyDelete, 0> a(5); }
TEST(FixedArrayTest, Data) {
static const int kInput[] = { 2, 3, 5, 7, 11, 13, 17 };
absl::FixedArray<int> fa(std::begin(kInput), std::end(kInput));
EXPECT_EQ(fa.data(), &*fa.begin());
EXPECT_EQ(fa.data(), &fa[0]);
const absl::FixedArray<int>& cfa = fa;
EXPECT_EQ(cfa.data(), &*cfa.begin());
EXPECT_EQ(cfa.data(), &cfa[0]);
}
TEST(FixedArrayTest, Empty) {
absl::FixedArray<int> empty(0);
absl::FixedArray<int> inline_filled(1);
absl::FixedArray<int, 0> heap_filled(1);
EXPECT_TRUE(empty.empty());
EXPECT_FALSE(inline_filled.empty());
EXPECT_FALSE(heap_filled.empty());
}
TEST(FixedArrayTest, FrontAndBack) {
absl::FixedArray<int, 3 * sizeof(int)> inlined = {1, 2, 3};
EXPECT_EQ(inlined.front(), 1);
EXPECT_EQ(inlined.back(), 3);
absl::FixedArray<int, 0> allocated = {1, 2, 3};
EXPECT_EQ(allocated.front(), 1);
EXPECT_EQ(allocated.back(), 3);
absl::FixedArray<int> one_element = {1};
EXPECT_EQ(one_element.front(), one_element.back());
}
TEST(FixedArrayTest, ReverseIteratorInlined) {
absl::FixedArray<int, 5 * sizeof(int)> a = {0, 1, 2, 3, 4};
int counter = 5;
for (absl::FixedArray<int>::reverse_iterator iter = a.rbegin();
iter != a.rend(); ++iter) {
counter--;
EXPECT_EQ(counter, *iter);
}
EXPECT_EQ(counter, 0);
counter = 5;
for (absl::FixedArray<int>::const_reverse_iterator iter = a.rbegin();
iter != a.rend(); ++iter) {
counter--;
EXPECT_EQ(counter, *iter);
}
EXPECT_EQ(counter, 0);
counter = 5;
for (auto iter = a.crbegin(); iter != a.crend(); ++iter) {
counter--;
EXPECT_EQ(counter, *iter);
}
EXPECT_EQ(counter, 0);
}
TEST(FixedArrayTest, ReverseIteratorAllocated) {
absl::FixedArray<int, 0> a = {0, 1, 2, 3, 4};
int counter = 5;
for (absl::FixedArray<int>::reverse_iterator iter = a.rbegin();
iter != a.rend(); ++iter) {
counter--;
EXPECT_EQ(counter, *iter);
}
EXPECT_EQ(counter, 0);
counter = 5;
for (absl::FixedArray<int>::const_reverse_iterator iter = a.rbegin();
iter != a.rend(); ++iter) {
counter--;
EXPECT_EQ(counter, *iter);
}
EXPECT_EQ(counter, 0);
counter = 5;
for (auto iter = a.crbegin(); iter != a.crend(); ++iter) {
counter--;
EXPECT_EQ(counter, *iter);
}
EXPECT_EQ(counter, 0);
}
TEST(FixedArrayTest, Fill) {
absl::FixedArray<int, 5 * sizeof(int)> inlined(5);
int fill_val = 42;
inlined.fill(fill_val);
for (int i : inlined) EXPECT_EQ(i, fill_val);
absl::FixedArray<int, 0> allocated(5);
allocated.fill(fill_val);
for (int i : allocated) EXPECT_EQ(i, fill_val);
// It doesn't do anything, just make sure this compiles.
absl::FixedArray<int> empty(0);
empty.fill(fill_val);
}
#ifdef ADDRESS_SANITIZER
TEST(FixedArrayTest, AddressSanitizerAnnotations1) {
absl::FixedArray<int, 32> a(10);
int *raw = a.data();
raw[0] = 0;
raw[9] = 0;
EXPECT_DEATH(raw[-2] = 0, "container-overflow");
EXPECT_DEATH(raw[-1] = 0, "container-overflow");
EXPECT_DEATH(raw[10] = 0, "container-overflow");
EXPECT_DEATH(raw[31] = 0, "container-overflow");
}
TEST(FixedArrayTest, AddressSanitizerAnnotations2) {
absl::FixedArray<char, 17> a(12);
char *raw = a.data();
raw[0] = 0;
raw[11] = 0;
EXPECT_DEATH(raw[-7] = 0, "container-overflow");
EXPECT_DEATH(raw[-1] = 0, "container-overflow");
EXPECT_DEATH(raw[12] = 0, "container-overflow");
EXPECT_DEATH(raw[17] = 0, "container-overflow");
}
TEST(FixedArrayTest, AddressSanitizerAnnotations3) {
absl::FixedArray<uint64_t, 20> a(20);
uint64_t *raw = a.data();
raw[0] = 0;
raw[19] = 0;
EXPECT_DEATH(raw[-1] = 0, "container-overflow");
EXPECT_DEATH(raw[20] = 0, "container-overflow");
}
TEST(FixedArrayTest, AddressSanitizerAnnotations4) {
absl::FixedArray<ThreeInts> a(10);
ThreeInts *raw = a.data();
raw[0] = ThreeInts();
raw[9] = ThreeInts();
// Note: raw[-1] is pointing to 12 bytes before the container range. However,
// there is only a 8-byte red zone before the container range, so we only
// access the last 4 bytes of the struct to make sure it stays within the red
// zone.
EXPECT_DEATH(raw[-1].z_ = 0, "container-overflow");
EXPECT_DEATH(raw[10] = ThreeInts(), "container-overflow");
// The actual size of storage is kDefaultBytes=256, 21*12 = 252,
// so reading raw[21] should still trigger the correct warning.
EXPECT_DEATH(raw[21] = ThreeInts(), "container-overflow");
}
#endif // ADDRESS_SANITIZER
} // namespace

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,26 @@
// Copyright 2017 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/container/internal/test_instance_tracker.h"
namespace absl {
namespace test_internal {
int BaseCountedInstance::num_instances_ = 0;
int BaseCountedInstance::num_live_instances_ = 0;
int BaseCountedInstance::num_moves_ = 0;
int BaseCountedInstance::num_copies_ = 0;
int BaseCountedInstance::num_swaps_ = 0;
} // namespace test_internal
} // namespace absl

View file

@ -0,0 +1,220 @@
// Copyright 2017 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_
#define ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_
#include <cstdlib>
#include <ostream>
namespace absl {
namespace test_internal {
// A type that counts number of occurences of the type, the live occurrences of
// the type, as well as the number of copies, moves, and swaps that have
// occurred on the type. This is used as a base class for the copyable,
// copyable+movable, and movable types below that are used in actual tests. Use
// InstanceTracker in tests to track the number of instances.
class BaseCountedInstance {
public:
explicit BaseCountedInstance(int x) : value_(x) {
++num_instances_;
++num_live_instances_;
}
BaseCountedInstance(const BaseCountedInstance& x)
: value_(x.value_), is_live_(x.is_live_) {
++num_instances_;
if (is_live_) ++num_live_instances_;
++num_copies_;
}
BaseCountedInstance(BaseCountedInstance&& x)
: value_(x.value_), is_live_(x.is_live_) {
x.is_live_ = false;
++num_instances_;
++num_moves_;
}
~BaseCountedInstance() {
--num_instances_;
if (is_live_) --num_live_instances_;
}
BaseCountedInstance& operator=(const BaseCountedInstance& x) {
value_ = x.value_;
if (is_live_) --num_live_instances_;
is_live_ = x.is_live_;
if (is_live_) ++num_live_instances_;
++num_copies_;
return *this;
}
BaseCountedInstance& operator=(BaseCountedInstance&& x) {
value_ = x.value_;
if (is_live_) --num_live_instances_;
is_live_ = x.is_live_;
x.is_live_ = false;
++num_moves_;
return *this;
}
int value() const {
if (!is_live_) std::abort();
return value_;
}
friend std::ostream& operator<<(std::ostream& o,
const BaseCountedInstance& v) {
return o << "[value:" << v.value() << "]";
}
// Implementation of efficient swap() that counts swaps.
static void SwapImpl(
BaseCountedInstance& lhs, // NOLINT(runtime/references)
BaseCountedInstance& rhs) { // NOLINT(runtime/references)
using std::swap;
swap(lhs.value_, rhs.value_);
swap(lhs.is_live_, rhs.is_live_);
++BaseCountedInstance::num_swaps_;
}
private:
friend class InstanceTracker;
int value_;
// Indicates if the value is live, ie it hasn't been moved away from.
bool is_live_ = true;
// Number of instances.
static int num_instances_;
// Number of live instances (those that have not been moved away from.)
static int num_live_instances_;
// Number of times that BaseCountedInstance objects were moved.
static int num_moves_;
// Number of times that BaseCountedInstance objects were copied.
static int num_copies_;
// Number of times that BaseCountedInstance objects were swapped.
static int num_swaps_;
};
// Helper to track the BaseCountedInstance instance counters. Expects that the
// number of instances and live_instances are the same when it is constructed
// and when it is destructed.
class InstanceTracker {
public:
InstanceTracker()
: start_instances_(BaseCountedInstance::num_instances_),
start_live_instances_(BaseCountedInstance::num_live_instances_) {
ResetCopiesMovesSwaps();
}
~InstanceTracker() {
if (instances() != 0) std::abort();
if (live_instances() != 0) std::abort();
}
// Returns the number of BaseCountedInstance instances both containing valid
// values and those moved away from compared to when the InstanceTracker was
// constructed
int instances() const {
return BaseCountedInstance::num_instances_ - start_instances_;
}
// Returns the number of live BaseCountedInstance instances compared to when
// the InstanceTracker was constructed
int live_instances() const {
return BaseCountedInstance::num_live_instances_ - start_live_instances_;
}
// Returns the number of moves on BaseCountedInstance objects since
// construction or since the last call to ResetCopiesMovesSwaps().
int moves() const { return BaseCountedInstance::num_moves_ - start_moves_; }
// Returns the number of copies on BaseCountedInstance objects since
// construction or the last call to ResetCopiesMovesSwaps().
int copies() const {
return BaseCountedInstance::num_copies_ - start_copies_;
}
// Returns the number of swaps on BaseCountedInstance objects since
// construction or the last call to ResetCopiesMovesSwaps().
int swaps() const { return BaseCountedInstance::num_swaps_ - start_swaps_; }
// Resets the base values for moves, copies and swaps to the current values,
// so that subsequent Get*() calls for moves, copies and swaps will compare to
// the situation at the point of this call.
void ResetCopiesMovesSwaps() {
start_moves_ = BaseCountedInstance::num_moves_;
start_copies_ = BaseCountedInstance::num_copies_;
start_swaps_ = BaseCountedInstance::num_swaps_;
}
private:
int start_instances_;
int start_live_instances_;
int start_moves_;
int start_copies_;
int start_swaps_;
};
// Copyable, not movable.
class CopyableOnlyInstance : public BaseCountedInstance {
public:
explicit CopyableOnlyInstance(int x) : BaseCountedInstance(x) {}
CopyableOnlyInstance(const CopyableOnlyInstance& rhs) = default;
CopyableOnlyInstance& operator=(const CopyableOnlyInstance& rhs) = default;
friend void swap(CopyableOnlyInstance& lhs, CopyableOnlyInstance& rhs) {
BaseCountedInstance::SwapImpl(lhs, rhs);
}
static bool supports_move() { return false; }
};
// Copyable and movable.
class CopyableMovableInstance : public BaseCountedInstance {
public:
explicit CopyableMovableInstance(int x) : BaseCountedInstance(x) {}
CopyableMovableInstance(const CopyableMovableInstance& rhs) = default;
CopyableMovableInstance(CopyableMovableInstance&& rhs) = default;
CopyableMovableInstance& operator=(const CopyableMovableInstance& rhs) =
default;
CopyableMovableInstance& operator=(CopyableMovableInstance&& rhs) = default;
friend void swap(CopyableMovableInstance& lhs, CopyableMovableInstance& rhs) {
BaseCountedInstance::SwapImpl(lhs, rhs);
}
static bool supports_move() { return true; }
};
// Only movable, not default-constructible.
class MovableOnlyInstance : public BaseCountedInstance {
public:
explicit MovableOnlyInstance(int x) : BaseCountedInstance(x) {}
MovableOnlyInstance(MovableOnlyInstance&& other) = default;
MovableOnlyInstance& operator=(MovableOnlyInstance&& other) = default;
friend void swap(MovableOnlyInstance& lhs, MovableOnlyInstance& rhs) {
BaseCountedInstance::SwapImpl(lhs, rhs);
}
static bool supports_move() { return true; }
};
} // namespace test_internal
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_

View file

@ -0,0 +1,160 @@
// Copyright 2017 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/container/internal/test_instance_tracker.h"
#include "gtest/gtest.h"
namespace {
using absl::test_internal::CopyableMovableInstance;
using absl::test_internal::CopyableOnlyInstance;
using absl::test_internal::InstanceTracker;
using absl::test_internal::MovableOnlyInstance;
TEST(TestInstanceTracker, CopyableMovable) {
InstanceTracker tracker;
CopyableMovableInstance src(1);
EXPECT_EQ(1, src.value()) << src;
CopyableMovableInstance copy(src);
CopyableMovableInstance move(std::move(src));
EXPECT_EQ(1, tracker.copies());
EXPECT_EQ(1, tracker.moves());
EXPECT_EQ(0, tracker.swaps());
EXPECT_EQ(3, tracker.instances());
EXPECT_EQ(2, tracker.live_instances());
tracker.ResetCopiesMovesSwaps();
CopyableMovableInstance copy_assign(1);
copy_assign = copy;
CopyableMovableInstance move_assign(1);
move_assign = std::move(move);
EXPECT_EQ(1, tracker.copies());
EXPECT_EQ(1, tracker.moves());
EXPECT_EQ(0, tracker.swaps());
EXPECT_EQ(5, tracker.instances());
EXPECT_EQ(3, tracker.live_instances());
tracker.ResetCopiesMovesSwaps();
{
using std::swap;
swap(move_assign, copy);
swap(copy, move_assign);
EXPECT_EQ(2, tracker.swaps());
EXPECT_EQ(0, tracker.copies());
EXPECT_EQ(0, tracker.moves());
EXPECT_EQ(5, tracker.instances());
EXPECT_EQ(3, tracker.live_instances());
}
}
TEST(TestInstanceTracker, CopyableOnly) {
InstanceTracker tracker;
CopyableOnlyInstance src(1);
EXPECT_EQ(1, src.value()) << src;
CopyableOnlyInstance copy(src);
CopyableOnlyInstance copy2(std::move(src)); // NOLINT
EXPECT_EQ(2, tracker.copies());
EXPECT_EQ(0, tracker.moves());
EXPECT_EQ(3, tracker.instances());
EXPECT_EQ(3, tracker.live_instances());
tracker.ResetCopiesMovesSwaps();
CopyableOnlyInstance copy_assign(1);
copy_assign = copy;
CopyableOnlyInstance copy_assign2(1);
copy_assign2 = std::move(copy2); // NOLINT
EXPECT_EQ(2, tracker.copies());
EXPECT_EQ(0, tracker.moves());
EXPECT_EQ(5, tracker.instances());
EXPECT_EQ(5, tracker.live_instances());
tracker.ResetCopiesMovesSwaps();
{
using std::swap;
swap(src, copy);
swap(copy, src);
EXPECT_EQ(2, tracker.swaps());
EXPECT_EQ(0, tracker.copies());
EXPECT_EQ(0, tracker.moves());
EXPECT_EQ(5, tracker.instances());
EXPECT_EQ(5, tracker.live_instances());
}
}
TEST(TestInstanceTracker, MovableOnly) {
InstanceTracker tracker;
MovableOnlyInstance src(1);
EXPECT_EQ(1, src.value()) << src;
MovableOnlyInstance move(std::move(src));
MovableOnlyInstance move_assign(2);
move_assign = std::move(move);
EXPECT_EQ(3, tracker.instances());
EXPECT_EQ(1, tracker.live_instances());
EXPECT_EQ(2, tracker.moves());
EXPECT_EQ(0, tracker.copies());
tracker.ResetCopiesMovesSwaps();
{
using std::swap;
MovableOnlyInstance other(2);
swap(move_assign, other);
swap(other, move_assign);
EXPECT_EQ(2, tracker.swaps());
EXPECT_EQ(0, tracker.copies());
EXPECT_EQ(0, tracker.moves());
EXPECT_EQ(4, tracker.instances());
EXPECT_EQ(2, tracker.live_instances());
}
}
TEST(TestInstanceTracker, ExistingInstances) {
CopyableMovableInstance uncounted_instance(1);
CopyableMovableInstance uncounted_live_instance(
std::move(uncounted_instance));
InstanceTracker tracker;
EXPECT_EQ(0, tracker.instances());
EXPECT_EQ(0, tracker.live_instances());
EXPECT_EQ(0, tracker.copies());
{
CopyableMovableInstance instance1(1);
EXPECT_EQ(1, tracker.instances());
EXPECT_EQ(1, tracker.live_instances());
EXPECT_EQ(0, tracker.copies());
EXPECT_EQ(0, tracker.moves());
{
InstanceTracker tracker2;
CopyableMovableInstance instance2(instance1);
CopyableMovableInstance instance3(std::move(instance2));
EXPECT_EQ(3, tracker.instances());
EXPECT_EQ(2, tracker.live_instances());
EXPECT_EQ(1, tracker.copies());
EXPECT_EQ(1, tracker.moves());
EXPECT_EQ(2, tracker2.instances());
EXPECT_EQ(1, tracker2.live_instances());
EXPECT_EQ(1, tracker2.copies());
EXPECT_EQ(1, tracker2.moves());
}
EXPECT_EQ(1, tracker.instances());
EXPECT_EQ(1, tracker.live_instances());
EXPECT_EQ(1, tracker.copies());
EXPECT_EQ(1, tracker.moves());
}
EXPECT_EQ(0, tracker.instances());
EXPECT_EQ(0, tracker.live_instances());
EXPECT_EQ(1, tracker.copies());
EXPECT_EQ(1, tracker.moves());
}
} // namespace