blob: 0e51cf225c111c67b15c3bda141b88115ae3c86a [file]
/* Copyright (c) 2026 The Khronos Group Inc.
* Copyright (c) 2026 Valve Corporation
* Copyright (c) 2026 LunarG, Inc.
*
* 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.
*/
#pragma once
#include "containers/array_range_map.h"
#include "containers/range_map.h"
#include <variant>
namespace sparse_container {
using IndexType = uint64_t;
// Adds "small map optimization" to vvl::range_map.
// Note that N must be < uint8_t max
template <typename T, size_t N>
class SmallRangeMap {
using ArrayMap = array_range_map<IndexType, T, vvl::range<IndexType>, N>;
using ArrayMapIterator = typename ArrayMap::iterator;
using ArrayMapConstIterator = typename ArrayMap::const_iterator;
using RangeMap = range_map<IndexType, T>;
using RangeMapIterator = typename RangeMap::iterator;
using RangeMapConstIterator = typename RangeMap::const_iterator;
public:
using index_type = IndexType;
using key_type = vvl::range<IndexType>;
using mapped_type = T;
using value_type = std::pair<const key_type, mapped_type>;
template <typename Value, typename ArrayIt, typename RangeIt>
class IteratorImpl {
public:
Value* operator->() const {
if (is_array_it_) {
return array_it_.operator->();
} else {
return range_it_.operator->();
}
}
Value& operator*() const {
if (is_array_it_) {
return array_it_.operator*();
} else {
return range_it_.operator*();
}
}
IteratorImpl& operator++() {
if (is_array_it_) {
array_it_.operator++();
} else {
range_it_.operator++();
}
return *this;
}
IteratorImpl& operator--() {
if (is_array_it_) {
array_it_.operator--();
} else {
range_it_.operator--();
}
return *this;
}
IteratorImpl& operator=(const IteratorImpl& other) {
is_array_it_ = other.is_array_it_;
array_it_ = other.array_it_;
range_it_ = other.range_it_;
return *this;
}
bool operator==(const IteratorImpl& other) const {
// It's enough just to compare both iterators.
return array_it_ == other.array_it_ && range_it_ == other.range_it_;
}
bool operator!=(const IteratorImpl& other) const { return !(*this == other); }
IteratorImpl() = default;
IteratorImpl(const IteratorImpl& other) = default;
IteratorImpl(const ArrayIt& it) : is_array_it_(true), array_it_(it) {}
IteratorImpl(const RangeIt& it) : is_array_it_(false), range_it_(it) {}
private:
friend SmallRangeMap;
bool is_array_it_ = false;
ArrayIt array_it_;
RangeIt range_it_;
};
using iterator = IteratorImpl<value_type, ArrayMapIterator, RangeMapIterator>;
// TODO change const iterator to derived class if iterator -> const_iterator constructor is needed
using const_iterator = IteratorImpl<const value_type, ArrayMapConstIterator, RangeMapConstIterator>;
iterator begin() {
if (UsesArrayMap()) {
return iterator(GetArrayMap().begin());
} else {
return iterator(GetRangeMap().begin());
}
}
const_iterator cbegin() const {
if (UsesArrayMap()) {
return const_iterator(GetArrayMap().begin());
} else {
return const_iterator(GetRangeMap().begin());
}
}
const_iterator begin() const { return cbegin(); }
iterator end() {
if (UsesArrayMap()) {
return iterator(GetArrayMap().end());
} else {
return iterator(GetRangeMap().end());
}
}
const_iterator cend() const {
if (UsesArrayMap()) {
return const_iterator(GetArrayMap().end());
} else {
return const_iterator(GetRangeMap().end());
}
}
const_iterator end() const { return cend(); }
iterator find(const key_type& key) {
if (UsesArrayMap()) {
return iterator(GetArrayMap().find(key));
} else {
return iterator(GetRangeMap().find(key));
}
}
const_iterator find(const key_type& key) const {
if (UsesArrayMap()) {
return const_iterator(GetArrayMap().find(key));
} else {
return const_iterator(GetRangeMap().find(key));
}
}
iterator find(const index_type& index) {
if (UsesArrayMap()) {
return iterator(GetArrayMap().find(index));
} else {
return iterator(GetRangeMap().find(index));
}
}
const_iterator find(const index_type& index) const {
if (UsesArrayMap()) {
return const_iterator(GetArrayMap().find(index));
} else {
return const_iterator(GetRangeMap().find(index));
}
}
// TODO -- this is supposed to be a const_iterator, which is constructable from an iterator
void insert(const iterator& hint, const value_type& value) {
if (UsesArrayMap()) {
assert(hint.is_array_it_);
GetArrayMap().insert(hint.array_it_, value);
} else {
assert(!hint.is_array_it_);
GetRangeMap().insert(hint.range_it_, value);
}
}
iterator lower_bound(const key_type& key) {
if (UsesArrayMap()) {
return iterator(GetArrayMap().lower_bound(key));
} else {
return iterator(GetRangeMap().lower_bound(key));
}
}
const_iterator lower_bound(const key_type& key) const {
if (UsesArrayMap()) {
return const_iterator(GetArrayMap().lower_bound(key));
} else {
return const_iterator(GetRangeMap().lower_bound(key));
}
}
template <typename Value>
iterator overwrite_range(const iterator& lower, Value&& value) {
if (UsesArrayMap()) {
assert(lower.is_array_it_);
return GetArrayMap().overwrite_range(lower.array_it_, std::forward<Value>(value));
} else {
assert(!lower.is_array_it_);
return GetRangeMap().overwrite_range(lower.range_it_, std::forward<Value>(value));
}
}
// With power comes responsibility (🕷). You can get to the underlying maps, s.t. in inner loops, the "SmallMode" checks can be
// avoided per call, just be sure and Get the correct one.
const ArrayMap& GetArrayMap() const {
assert(UsesArrayMap());
return std::get<ArrayMap>(map_);
}
ArrayMap& GetArrayMap() {
assert(UsesArrayMap());
return std::get<ArrayMap>(map_);
}
const RangeMap& GetRangeMap() const {
assert(!UsesArrayMap());
return std::get<RangeMap>(map_);
}
RangeMap& GetRangeMap() {
assert(!UsesArrayMap());
return std::get<RangeMap>(map_);
}
SmallRangeMap() = delete;
SmallRangeMap(index_type limit) {
if (limit <= N) {
map_ = ArrayMap(limit);
} else {
map_ = RangeMap();
}
}
bool empty() const {
if (UsesArrayMap()) {
return GetArrayMap().empty();
} else {
return GetRangeMap().empty();
}
}
size_t size() const {
if (UsesArrayMap()) {
return GetArrayMap().size();
} else {
return GetRangeMap().size();
}
}
bool UsesArrayMap() const { return std::holds_alternative<ArrayMap>(map_); }
private:
std::variant<ArrayMap, RangeMap> map_;
};
} // namespace sparse_container