#ifndef BASE_CONTAINERS_SMALL_MAP_H_
#define BASE_CONTAINERS_SMALL_MAP_H_
#include <stddef.h>
#include <array>
#include <limits>
#include <map>
#include <memory>
#include <new>
#include <type_traits>
#include <utility>
#include "base/check.h"
#include "base/check_op.h"
#include "base/containers/adapters.h"
#include "base/containers/span.h"
#include "base/memory/stack_allocated.h"
#include "base/numerics/safe_conversions.h"
#include "base/types/to_address.h"
inline constexpr size_t kUsingFullMapSentinel = …;
namespace base {
namespace internal {
template <typename NormalMap>
class small_map_default_init { … };
template <typename M>
struct has_key_equal { … };
template <typename M> const bool has_key_equal<M>::value;
template <typename M, bool has_key_equal_value>
struct select_equal_key { … };
select_equal_key<M, true>;
}
template <typename NormalMap,
size_t kArraySize = 4,
typename EqualKey = typename internal::select_equal_key<
NormalMap,
internal::has_key_equal<NormalMap>::value>::equal_key,
typename MapInit = internal::small_map_default_init<NormalMap>>
class small_map {
static_assert(kArraySize > 0, "Initial size must be greater than 0");
static_assert(kArraySize != kUsingFullMapSentinel,
"Initial size out of range");
public:
using key_type = NormalMap::key_type;
using data_type = NormalMap::mapped_type;
using mapped_type = NormalMap::mapped_type;
using value_type = NormalMap::value_type;
using key_equal = EqualKey;
constexpr small_map() : … { … }
constexpr explicit small_map(const MapInit& functor) : … { … }
constexpr small_map(const small_map& src) { … }
constexpr void operator=(const small_map& src) { … }
~small_map() { … }
union ArrayElement {
ArrayElement() {}
~ArrayElement() {}
value_type value;
};
class const_iterator;
class iterator {
STACK_ALLOCATED();
using map_iterator = NormalMap::iterator;
using array_iterator = span<ArrayElement>::iterator;
public:
using iterator_category = map_iterator::iterator_category;
using value_type = map_iterator::value_type;
using difference_type = map_iterator::difference_type;
using pointer = map_iterator::pointer;
using reference = map_iterator::reference;
iterator() = default;
constexpr iterator& operator++() {
if (has_array_iter()) {
++array_iter_;
} else {
++map_iter_;
}
return *this;
}
constexpr iterator operator++(int ) {
iterator result(*this);
++(*this);
return result;
}
constexpr value_type* operator->() const {
return has_array_iter() ? std::addressof(array_iter_->value)
: std::addressof(*map_iter_);
}
constexpr value_type& operator*() const {
return has_array_iter() ? array_iter_->value : *map_iter_;
}
constexpr bool operator==(const iterator& other) const {
if (has_array_iter()) {
return array_iter_ == other.array_iter_;
} else {
return !other.has_array_iter() && map_iter_ == other.map_iter_;
}
}
private:
friend class small_map;
friend class const_iterator;
constexpr explicit iterator(const array_iterator& init)
: array_iter_(init) {}
constexpr explicit iterator(const map_iterator& init) : map_iter_(init) {}
constexpr bool has_array_iter() const {
return base::to_address(array_iter_) != nullptr;
}
array_iterator array_iter_;
map_iterator map_iter_;
};
class const_iterator {
STACK_ALLOCATED();
using map_iterator = NormalMap::const_iterator;
using array_iterator = span<const ArrayElement>::iterator;
public:
using iterator_category = map_iterator::iterator_category;
using value_type = map_iterator::value_type;
using difference_type = map_iterator::difference_type;
using pointer = map_iterator::pointer;
using reference = map_iterator::reference;
const_iterator() = default;
constexpr const_iterator(const iterator& other)
: array_iter_(other.array_iter_), map_iter_(other.map_iter_) {}
constexpr const_iterator& operator++() {
if (has_array_iter()) {
++array_iter_;
} else {
++map_iter_;
}
return *this;
}
constexpr const_iterator operator++(int ) {
const_iterator result(*this);
++(*this);
return result;
}
constexpr const value_type* operator->() const {
return has_array_iter() ? std::addressof(array_iter_->value)
: std::addressof(*map_iter_);
}
constexpr const value_type& operator*() const {
return has_array_iter() ? array_iter_->value : *map_iter_;
}
constexpr bool operator==(const const_iterator& other) const {
if (has_array_iter()) {
return array_iter_ == other.array_iter_;
}
return !other.has_array_iter() && map_iter_ == other.map_iter_;
}
private:
friend class small_map;
constexpr explicit const_iterator(const array_iterator& init)
: array_iter_(init) {}
constexpr explicit const_iterator(const map_iterator& init)
: map_iter_(init) {}
constexpr bool has_array_iter() const {
return base::to_address(array_iter_) != nullptr;
}
array_iterator array_iter_;
map_iterator map_iter_;
};
constexpr iterator find(const key_type& key) { … }
constexpr const_iterator find(const key_type& key) const { … }
constexpr data_type& operator[](const key_type& key)
requires(std::is_default_constructible_v<data_type>)
{ … }
constexpr std::pair<iterator, bool> insert(const value_type& x) { … }
template <class InputIterator>
constexpr void insert(InputIterator f, InputIterator l) { … }
template <typename... Args>
constexpr std::pair<iterator, bool> emplace(Args&&... args) { … }
constexpr iterator begin() { … }
constexpr const_iterator begin() const { … }
constexpr iterator end() { … }
constexpr const_iterator end() const { … }
constexpr void clear() { … }
constexpr iterator erase(const iterator& position) { … }
constexpr size_t erase(const key_type& key) { … }
constexpr size_t count(const key_type& key) const { … }
constexpr size_t size() const { … }
constexpr bool empty() const { … }
constexpr bool UsingFullMap() const { … }
constexpr NormalMap* map() { … }
constexpr const NormalMap* map() const { … }
private:
size_t size_ = 0u;
MapInit functor_;
using ArrayMap = std::array<ArrayElement, kArraySize>;
union {
ArrayMap array_;
NormalMap map_;
};
constexpr span<ArrayElement> sized_array_span() { … }
constexpr span<const ArrayElement> sized_array_span() const { … }
constexpr void ConvertToRealMap() { … }
constexpr void InitEmpty() { … }
constexpr void InitFrom(const small_map& src) { … }
constexpr void Destroy() { … }
};
}
#endif