#ifndef ABSL_CONTAINER_FIXED_ARRAY_H_
#define ABSL_CONTAINER_FIXED_ARRAY_H_
#include <algorithm>
#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/attributes.h"
#include "absl/base/config.h"
#include "absl/base/dynamic_annotations.h"
#include "absl/base/internal/iterator_traits.h"
#include "absl/base/macros.h"
#include "absl/base/optimization.h"
#include "absl/base/port.h"
#include "absl/base/throw_delegate.h"
#include "absl/container/internal/compressed_tuple.h"
#include "absl/hash/internal/weakly_mixed_integer.h"
#include "absl/memory/memory.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1);
template <typename T, size_t N = kFixedArrayUseDefault,
typename A = std::allocator<T>>
class ABSL_ATTRIBUTE_WARN_UNUSED FixedArray {
static_assert(!std::is_array<T>::value || std::extent<T>::value > 0,
"Arrays with unknown bounds cannot be used with FixedArray.");
static constexpr size_t kInlineBytesDefault = 256;
using AllocatorTraits = std::allocator_traits<A>;
template <typename Iterator>
using EnableIfInputIterator =
std::enable_if_t<base_internal::IsAtLeastInputIterator<Iterator>::value>;
static constexpr bool NoexceptCopyable() {
return std::is_nothrow_copy_constructible<StorageElement>::value &&
absl::allocator_is_nothrow<allocator_type>::value;
}
static constexpr bool NoexceptMovable() {
return std::is_nothrow_move_constructible<StorageElement>::value &&
absl::allocator_is_nothrow<allocator_type>::value;
}
static constexpr bool DefaultConstructorIsNonTrivial() {
return !absl::is_trivially_default_constructible<StorageElement>::value;
}
public:
using allocator_type = typename AllocatorTraits::allocator_type;
using value_type = typename AllocatorTraits::value_type;
using pointer = typename AllocatorTraits::pointer;
using const_pointer = typename AllocatorTraits::const_pointer;
using reference = value_type&;
using const_reference = const value_type&;
using size_type = typename AllocatorTraits::size_type;
using difference_type = typename AllocatorTraits::difference_type;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
static constexpr size_type inline_elements =
(N == kFixedArrayUseDefault ? kInlineBytesDefault / sizeof(value_type)
: static_cast<size_type>(N));
FixedArray(const FixedArray& other) noexcept(NoexceptCopyable())
: FixedArray(other,
AllocatorTraits::select_on_container_copy_construction(
other.storage_.alloc())) {}
FixedArray(const FixedArray& other,
const allocator_type& a) noexcept(NoexceptCopyable())
: FixedArray(other.begin(), other.end(), a) {}
FixedArray(FixedArray&& other) noexcept(NoexceptMovable())
: FixedArray(std::move(other), other.storage_.alloc()) {}
FixedArray(FixedArray&& other,
const allocator_type& a) noexcept(NoexceptMovable())
: FixedArray(std::make_move_iterator(other.begin()),
std::make_move_iterator(other.end()), a) {}
explicit FixedArray(size_type n, const allocator_type& a = allocator_type())
: storage_(n, a) {
if (DefaultConstructorIsNonTrivial()) {
memory_internal::ConstructRange(storage_.alloc(), storage_.begin(),
storage_.end());
}
}
FixedArray(size_type n, const value_type& val,
const allocator_type& a = allocator_type())
: storage_(n, a) {
memory_internal::ConstructRange(storage_.alloc(), storage_.begin(),
storage_.end(), val);
}
FixedArray(std::initializer_list<value_type> init_list,
const allocator_type& a = allocator_type())
: FixedArray(init_list.begin(), init_list.end(), a) {}
template <typename Iterator, EnableIfInputIterator<Iterator>* = nullptr>
FixedArray(Iterator first, Iterator last,
const allocator_type& a = allocator_type())
: storage_(std::distance(first, last), a) {
memory_internal::CopyRange(storage_.alloc(), storage_.begin(), first, last);
}
~FixedArray() noexcept {
for (auto* cur = storage_.begin(); cur != storage_.end(); ++cur) {
AllocatorTraits::destroy(storage_.alloc(), cur);
}
}
void operator=(FixedArray&&) = delete;
void operator=(const FixedArray&) = delete;
size_type size() const { return storage_.size(); }
constexpr size_type max_size() const {
return (std::numeric_limits<difference_type>::max)() / sizeof(value_type);
}
bool empty() const { return size() == 0; }
size_t memsize() const { return size() * sizeof(value_type); }
const_pointer data() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return AsValueType(storage_.begin());
}
pointer data() ABSL_ATTRIBUTE_LIFETIME_BOUND {
return AsValueType(storage_.begin());
}
reference operator[](size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND {
ABSL_HARDENING_ASSERT(i < size());
return data()[i];
}
const_reference operator[](size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND {
ABSL_HARDENING_ASSERT(i < size());
return data()[i];
}
reference at(size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND {
if (ABSL_PREDICT_FALSE(i >= size())) {
ThrowStdOutOfRange("FixedArray::at failed bounds check");
}
return data()[i];
}
const_reference at(size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND {
if (ABSL_PREDICT_FALSE(i >= size())) {
ThrowStdOutOfRange("FixedArray::at failed bounds check");
}
return data()[i];
}
reference front() ABSL_ATTRIBUTE_LIFETIME_BOUND {
ABSL_HARDENING_ASSERT(!empty());
return data()[0];
}
const_reference front() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
ABSL_HARDENING_ASSERT(!empty());
return data()[0];
}
reference back() ABSL_ATTRIBUTE_LIFETIME_BOUND {
ABSL_HARDENING_ASSERT(!empty());
return data()[size() - 1];
}
const_reference back() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
ABSL_HARDENING_ASSERT(!empty());
return data()[size() - 1];
}
iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return data(); }
const_iterator begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { return data(); }
const_iterator cbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return begin();
}
iterator end() ABSL_ATTRIBUTE_LIFETIME_BOUND { return data() + size(); }
const_iterator end() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return data() + size();
}
const_iterator cend() const ABSL_ATTRIBUTE_LIFETIME_BOUND { return end(); }
reverse_iterator rbegin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
return reverse_iterator(end());
}
const_reverse_iterator rbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return const_reverse_iterator(end());
}
const_reverse_iterator crbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return rbegin();
}
reverse_iterator rend() ABSL_ATTRIBUTE_LIFETIME_BOUND {
return reverse_iterator(begin());
}
const_reverse_iterator rend() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return const_reverse_iterator(begin());
}
const_reverse_iterator crend() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return rend();
}
void fill(const value_type& val) { std::fill(begin(), end(), val); }
friend bool operator==(const FixedArray& lhs, const FixedArray& rhs) {
return std::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);
}
template <typename H>
friend H AbslHashValue(H h, const FixedArray& v) {
return H::combine_contiguous(std::move(h), v.data(), v.size());
}
private:
template <typename OuterT, typename InnerT = absl::remove_extent_t<OuterT>,
size_t InnerN = std::extent<OuterT>::value>
struct StorageElementWrapper {
InnerT array[InnerN];
};
using StorageElement =
absl::conditional_t<std::is_array<value_type>::value,
StorageElementWrapper<value_type>, value_type>;
static pointer AsValueType(pointer ptr) { return ptr; }
static pointer AsValueType(StorageElementWrapper<value_type>* ptr) {
return std::addressof(ptr->array);
}
static_assert(sizeof(StorageElement) == sizeof(value_type), "");
static_assert(alignof(StorageElement) == alignof(value_type), "");
class NonEmptyInlinedStorage {
public:
StorageElement* data() { return reinterpret_cast<StorageElement*>(buff_); }
void AnnotateConstruct(size_type n);
void AnnotateDestruct(size_type n);
#ifdef ABSL_HAVE_ADDRESS_SANITIZER
void* RedzoneBegin() { return &redzone_begin_; }
void* RedzoneEnd() { return &redzone_end_ + 1; }
#endif
private:
ABSL_ADDRESS_SANITIZER_REDZONE(redzone_begin_);
alignas(StorageElement) unsigned char buff_[sizeof(
StorageElement[inline_elements])];
ABSL_ADDRESS_SANITIZER_REDZONE(redzone_end_);
};
class EmptyInlinedStorage {
public:
StorageElement* data() { return nullptr; }
void AnnotateConstruct(size_type) {}
void AnnotateDestruct(size_type) {}
};
using InlinedStorage =
absl::conditional_t<inline_elements == 0, EmptyInlinedStorage,
NonEmptyInlinedStorage>;
class Storage : public InlinedStorage {
public:
Storage(size_type n, const allocator_type& a)
: size_alloc_(n, a), data_(InitializeData()) {}
~Storage() noexcept {
if (UsingInlinedStorage(size())) {
InlinedStorage::AnnotateDestruct(size());
} else {
AllocatorTraits::deallocate(alloc(), AsValueType(begin()), size());
}
}
size_type size() const { return size_alloc_.template get<0>(); }
StorageElement* begin() const { return data_; }
StorageElement* end() const { return begin() + size(); }
allocator_type& alloc() { return size_alloc_.template get<1>(); }
const allocator_type& alloc() const {
return size_alloc_.template get<1>();
}
private:
static bool UsingInlinedStorage(size_type n) {
return n <= inline_elements;
}
#ifdef ABSL_HAVE_ADDRESS_SANITIZER
ABSL_ATTRIBUTE_NOINLINE
#endif StorageElement* InitializeData() {
if (UsingInlinedStorage(size())) {
InlinedStorage::AnnotateConstruct(size());
return InlinedStorage::data();
} else {
return reinterpret_cast<StorageElement*>(
AllocatorTraits::allocate(alloc(), size()));
}
}
container_internal::CompressedTuple<size_type, allocator_type> size_alloc_;
StorageElement* data_;
};
Storage storage_;
};
template <typename T, size_t N, typename A>
void FixedArray<T, N, A>::NonEmptyInlinedStorage::AnnotateConstruct(
typename FixedArray<T, N, A>::size_type n) {
#ifdef ABSL_HAVE_ADDRESS_SANITIZER
if (!n) return;
ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), RedzoneEnd(),
data() + n);
ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), data(),
RedzoneBegin());
#endif static_cast<void>(n); }
template <typename T, size_t N, typename A>
void FixedArray<T, N, A>::NonEmptyInlinedStorage::AnnotateDestruct(
typename FixedArray<T, N, A>::size_type n) {
#ifdef ABSL_HAVE_ADDRESS_SANITIZER
if (!n) return;
ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), data() + n,
RedzoneEnd());
ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), RedzoneBegin(),
data());
#endif static_cast<void>(n); }
ABSL_NAMESPACE_END
}
#endif