Initial Commit
This commit is contained in:
commit
c2e7548296
238 changed files with 65475 additions and 0 deletions
124
absl/container/BUILD.bazel
Normal file
124
absl/container/BUILD.bazel
Normal 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),
|
||||
)
|
||||
493
absl/container/fixed_array.h
Normal file
493
absl/container/fixed_array.h
Normal 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_
|
||||
621
absl/container/fixed_array_test.cc
Normal file
621
absl/container/fixed_array_test.cc
Normal 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
|
||||
1330
absl/container/inlined_vector.h
Normal file
1330
absl/container/inlined_vector.h
Normal file
File diff suppressed because it is too large
Load diff
1593
absl/container/inlined_vector_test.cc
Normal file
1593
absl/container/inlined_vector_test.cc
Normal file
File diff suppressed because it is too large
Load diff
26
absl/container/internal/test_instance_tracker.cc
Normal file
26
absl/container/internal/test_instance_tracker.cc
Normal 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
|
||||
220
absl/container/internal/test_instance_tracker.h
Normal file
220
absl/container/internal/test_instance_tracker.h
Normal 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_
|
||||
160
absl/container/internal/test_instance_tracker_test.cc
Normal file
160
absl/container/internal/test_instance_tracker_test.cc
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue