16#include <unordered_map>
25using PineMapBare = std::remove_cv_t<std::remove_reference_t<T>>;
29 std::is_integral_v<PineMapBare<T>> ||
30 std::is_floating_point_v<PineMapBare<T>> ||
31 std::is_enum_v<PineMapBare<T>> ||
32 std::is_same_v<PineMapBare<T>, std::string>;
36 !std::is_reference_v<T> &&
37 (std::is_arithmetic_v<PineMapBare<T>> ||
38 std::is_enum_v<PineMapBare<T>> ||
39 std::is_same_v<PineMapBare<T>, std::string>);
41template <
typename Key,
bool = std::is_
floating_po
int_v<PineMapBare<Key>>>
44 noexcept(
noexcept(lhs == rhs)) {
49template <
typename Key>
52 return (std::isnan(lhs) && std::isnan(rhs)) || lhs == rhs;
56template <
typename Key,
bool = std::is_
floating_po
int_v<PineMapBare<Key>>>
59 noexcept(
noexcept(std::hash<Key>{}(key))) {
60 return std::hash<Key>{}(key);
64template <
typename Key>
67 if (std::isnan(key)) {
70 return static_cast<std::size_t
>(UINT64_C(0x9e3779b97f4a7c15));
74 if (key ==
static_cast<Key
>(0)) key =
static_cast<Key
>(0);
75 return std::hash<Key>{}(key);
93template <
typename Key,
typename Value>
96 "PineMap key must be a Pine fundamental or enum type");
98 using Entry = std::pair<Key, Value>;
99 using EntryList = std::list<Entry>;
100 using EntryIterator =
typename EntryList::iterator;
101 using Index = std::unordered_map<Key, EntryIterator,
105 static_assert(
noexcept(std::declval<EntryList&>().swap(
106 std::declval<EntryList&>())),
107 "PineMap ordered storage must support no-throw swap");
108 static_assert(
noexcept(std::declval<Index&>().swap(
109 std::declval<Index&>())),
110 "PineMap index must support no-throw swap");
112 static constexpr const char* kNaIdError =
113 "map operation on na ID";
114 static constexpr const char* kInvalidSnapshotError =
115 "map restore from invalid snapshot";
116 static constexpr const char* kCapacityError =
117 "map cannot contain more than 50000 key-value pairs";
118 static constexpr const char* kIndexInvariantError =
119 "map key index invariant violation";
127 Storage(
const Storage& other)
128 : entries(other.entries),
129 index(0, other.index.hash_function(), other.index.key_eq()) {
133 Storage&
operator=(
const Storage& other) {
134 if (
this == &other)
return *
this;
135 Storage replacement(other);
140 Storage(Storage&&) =
default;
143 void rebuild_index() {
145 replacement.reserve(entries.size());
146 for (
auto it = entries.begin(); it != entries.end(); ++it) {
147 const auto inserted = replacement.emplace(it->first, it).second;
149 throw std::runtime_error(kIndexInvariantError);
152 index.swap(replacement);
155 void swap(Storage& other)
noexcept {
159 entries.swap(other.entries);
160 index.swap(other.index);
183 "PineMap::Snapshot supports primitive Pine values only; "
184 "UDT and collection handles require recursive codegen checkpointing");
186 std::shared_ptr<Storage> identity_;
189 Snapshot(std::shared_ptr<Storage> identity,
const Storage& state)
190 : identity_(std::move(identity)), state_(state) {}
213 if (
this != &other) storage_ = other.storage_;
218 return PineMap(std::make_shared<Storage>());
221 [[nodiscard]]
bool is_na() const noexcept {
return !storage_; }
225 Value
put(Key key, Value value) {
226 Storage& storage = require_storage();
227 auto found = storage.index.find(key);
228 if (found != storage.index.end()) {
229 Value previous = found->second->second;
230 found->second->second = std::move(value);
234 if (storage.entries.size() >=
static_cast<std::size_t
>(
max_pairs)) {
235 throw std::runtime_error(kCapacityError);
238 storage.entries.emplace_back(std::move(key), std::move(value));
239 auto entry = std::prev(storage.entries.end());
241 const auto inserted = storage.index.emplace(entry->first, entry).second;
243 throw std::runtime_error(kIndexInvariantError);
248 storage.entries.pop_back();
254 [[nodiscard]] Value
get(
const Key& key)
const {
255 const Storage& storage = require_storage();
256 auto found = storage.index.find(key);
258 return found->second->second;
264 Storage& storage = require_storage();
265 auto found = storage.index.find(key);
268 Value previous = found->second->second;
269 storage.entries.erase(found->second);
270 storage.index.erase(found);
274 [[nodiscard]]
bool contains(
const Key& key)
const {
275 const Storage& storage = require_storage();
276 return storage.index.find(key) != storage.index.end();
279 [[nodiscard]]
int size()
const {
280 const Storage& storage = require_storage();
281 if (storage.entries.size() >
282 static_cast<std::size_t
>(std::numeric_limits<int>::max())) {
283 throw std::overflow_error(
"map.size: result exceeds int range");
285 return static_cast<int>(storage.entries.size());
289 Storage& storage = require_storage();
290 storage.index.clear();
291 storage.entries.clear();
297 Storage& target_storage = require_storage();
298 const Storage& source_storage =
source.require_storage();
299 if (storage_ ==
source.storage_)
return;
301 std::size_t new_keys = 0;
302 for (
const auto& entry : source_storage.entries) {
303 if (target_storage.index.find(entry.first) ==
304 target_storage.index.end()) {
308 const auto available =
static_cast<std::size_t
>(
max_pairs) -
309 target_storage.entries.size();
310 if (new_keys > available) {
314 throw std::runtime_error(kCapacityError);
317 for (
const auto& entry : source_storage.entries) {
318 (void)
put(entry.first, entry.second);
322 [[nodiscard]] std::vector<Key>
keys()
const {
323 const Storage& storage = require_storage();
324 std::vector<Key> result;
325 result.reserve(storage.entries.size());
326 for (
const auto& entry : storage.entries) {
327 result.push_back(entry.first);
332 [[nodiscard]] std::vector<Value>
values()
const {
333 const Storage& storage = require_storage();
334 std::vector<Value> result;
335 result.reserve(storage.entries.size());
336 for (
const auto& entry : storage.entries) {
337 result.push_back(entry.second);
348 return PineMap(std::make_shared<Storage>(require_storage()));
351 template <
typename T = Value,
354 std::is_same_v<T, Value>,
int> = 0>
356 const Storage& storage = require_storage();
360 template <
typename T = Value,
363 std::is_same_v<T, Value>,
int> = 0>
366 throw std::runtime_error(kInvalidSnapshotError);
371 Storage replacement(
snapshot.state_);
372 snapshot.identity_->swap(replacement);
377 [[nodiscard]] Storage& require_storage() {
378 if (!storage_)
throw std::runtime_error(kNaIdError);
382 [[nodiscard]]
const Storage& require_storage()
const {
383 if (!storage_)
throw std::runtime_error(kNaIdError);
387 explicit PineMap(std::shared_ptr<Storage> storage)
388 : storage_(std::move(storage)) {}
390 std::shared_ptr<Storage> storage_;
393template <
typename Key,
typename Value>
Snapshot(const Snapshot &)=default
Snapshot & operator=(Snapshot &&)=default
Snapshot & operator=(const Snapshot &)=default
Snapshot(Snapshot &&)=default
Value get(const Key &key) const
void put_all(const PineMap &source)
Snapshot snapshot() const
std::vector< Value > values() const
PineMap & operator=(PineMap &&other) noexcept
bool is_na() const noexcept
std::vector< Key > keys() const
static constexpr bool snapshot_supported
PineMap() noexcept=default
bool contains(const Key &key) const
Value remove(const Key &key)
void restore(const Snapshot &snapshot)
PineMap & operator=(const PineMap &) noexcept=default
static constexpr int max_pairs
Value put(Key key, Value value)
constexpr bool is_pine_map_snapshot_value_v
std::remove_cv_t< std::remove_reference_t< T > > PineMapBare
constexpr bool is_pine_map_key_v
constexpr bool is_pine_map_snapshot_value_v
bool is_na(const Line &h)
bool operator()(Key lhs, Key rhs) const noexcept
bool operator()(const Key &lhs, const Key &rhs) const noexcept(noexcept(lhs==rhs))
std::size_t operator()(Key key) const noexcept
std::size_t operator()(const Key &key) const noexcept(noexcept(std::hash< Key >{}(key)))