1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2017-2022, The LegacyClonk Team and contributors
6 *
7 * Distributed under the terms of the ISC license; see accompanying file
8 * "COPYING" for details.
9 *
10 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11 * See accompanying file "TRADEMARK" for details.
12 *
13 * To redistribute this file separately, substitute the full license texts
14 * for the above references.
15 */
16
17#include "C4ValueHash.h"
18#include "C4StringTable.h"
19
20#include <optional>
21#include <stdexcept>
22
23C4ValueHash::C4ValueHash() { }
24
25C4ValueHash::C4ValueHash(const C4ValueHash &other)
26{
27 *this = other;
28}
29
30C4ValueHash::~C4ValueHash()
31{
32 clear();
33}
34
35void C4ValueHash::CompileFunc(StdCompiler *pComp)
36{
37 pComp->Value(rStruct: mkSTLMapAdapt(map&: *this));
38}
39
40void C4ValueHash::DenumeratePointers()
41{
42 for (auto [key, value] : *this)
43 {
44 const_cast<C4Value &>(key).DenumeratePointer();
45 value.DenumeratePointer();
46 }
47}
48
49void C4ValueHash::removeValue(C4Value *value)
50{
51 const auto erase = [this](const auto &it)
52 {
53 keyOrder.erase(it->second.keyOrderIterator);
54 map.erase(it);
55 };
56 bool found = false;
57 auto keyIt = map.end();
58 for (auto it = map.begin(); it != map.end(); )
59 {
60 if (&it->first == value)
61 {
62 keyIt = it;
63 emptyValues.push_front(val: it->second.value);
64 }
65 else if (it->second.value == value)
66 {
67 erase(it++);
68 found = true;
69 continue;
70 }
71 ++it;
72 }
73 if (found) emptyValues.push_front(val: value);
74 if (keyIt != map.end()) erase(keyIt);
75}
76
77bool C4ValueHash::contains(const C4Value &key) const
78{
79 return map.find(x: key) != map.end();
80}
81
82void C4ValueHash::clear()
83{
84 for (auto &[key, value] : map) delete value.value;
85 map.clear();
86 for (auto &value : emptyValues) delete value;
87 emptyValues.clear();
88}
89
90C4ValueHash &C4ValueHash::operator=(const C4ValueHash &other)
91{
92 for (const auto key : other.keyOrder)
93 {
94 (*this)[*key].Set(*other.map.at(k: *key).value);
95 }
96 return *this;
97}
98
99bool C4ValueHash::operator==(const C4ValueHash &other) const
100{
101 if (other.size() != size()) return false;
102
103 for (const auto &it : map)
104 {
105 if (!other.contains(key: it.first) || other[it.first] != *it.second.value)
106 return false;
107 }
108
109 return true;
110}
111
112bool C4ValueHash::hasIndex(const C4Value &key) const
113{
114 return contains(key);
115}
116
117C4Value &C4ValueHash::operator[](const C4Value &key)
118{
119 try
120 {
121 return *map.at(k: key).value;
122 }
123 catch (const std::out_of_range &)
124 {
125 C4Value *value;
126 if (emptyValues.empty()) value = C4Value::OfMap(map: this);
127 else
128 {
129 value = emptyValues.front();
130 emptyValues.pop_front();
131 }
132
133 const auto &inserted = map.emplace(args: std::piecewise_construct, args: std::forward_as_tuple(args: key, args: this), args: std::forward_as_tuple(args: MapEntry{.value: value, .keyOrderIterator: {}})).first;
134 inserted->second.keyOrderIterator = keyOrder.insert(position: keyOrder.end(), x: &inserted->first);
135 return *value;
136 }
137}
138
139const C4Value &C4ValueHash::operator[](const C4Value &key) const
140{
141 try
142 {
143 return *map.at(k: key).value;
144 }
145 catch (const std::out_of_range &)
146 {
147 return C4VNull;
148 }
149}
150
151C4ValueHash::Iterator C4ValueHash::begin()
152{
153 return Iterator(this, keyOrder.begin(), keyOrder.end());
154}
155
156C4ValueHash::Iterator C4ValueHash::end()
157{
158 return Iterator(this, keyOrder.end(), keyOrder.end());
159}
160
161C4ValueHash::Iterator::Iterator(C4ValueHash *map, C4ValueHash::Iterator::iterator_type it, C4ValueHash::Iterator::iterator_type end) : it(it), end(end), map(map)
162{
163 update();
164}
165
166void C4ValueHash::Iterator::update()
167{
168 if (it != end)
169 {
170 current.emplace(args: **it, args&: (*map)[**it]);
171 }
172 else
173 {
174 current.reset();
175 }
176}
177
178C4ValueHash::Iterator &C4ValueHash::Iterator::operator++()
179{
180 ++it;
181 update();
182 return *this;
183}
184
185C4ValueHash::Iterator::pair_type &C4ValueHash::Iterator::operator*()
186{
187 return *current;
188}
189
190bool C4ValueHash::Iterator::operator==(const C4ValueHash::Iterator &other) const
191{
192 return it == other.it;
193}
194