Container System 0.1.0
High-performance C++20 type-safe container framework with SIMD-accelerated serialization
Loading...
Searching...
No Matches
thread_safe_container.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2024, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
7#include <algorithm>
8#include <cstring>
9
10namespace kcenon::container
11{
12 using namespace utility_module;
13
15 std::initializer_list<std::pair<std::string, value>> init)
16 {
17 for (const auto& [key, value] : init) {
18 values_.emplace(key, value);
19 }
20 }
21
23 {
24 std::shared_lock other_lock(other.mutex_);
25 values_ = other.values_;
26 read_count_ = other.read_count_.load();
27 write_count_ = other.write_count_.load();
30 }
31
33 {
34 std::unique_lock other_lock(other.mutex_);
35 values_ = std::move(other.values_);
36 read_count_ = other.read_count_.load();
37 write_count_ = other.write_count_.load();
38 bulk_read_count_ = other.bulk_read_count_.load();
39 bulk_write_count_ = other.bulk_write_count_.load();
40
41 other.read_count_ = 0;
42 other.write_count_ = 0;
43 other.bulk_read_count_ = 0;
44 other.bulk_write_count_ = 0;
45 }
46
48 {
49 if (this != &other) {
50 // Acquire both locks atomically to prevent deadlock
51 // std::scoped_lock ensures locks are acquired in a consistent order
52 std::scoped_lock lock(mutex_, other.mutex_);
53 values_ = other.values_;
54 write_count_.fetch_add(1, std::memory_order_relaxed);
55 }
56 return *this;
57 }
58
60 {
61 if (this != &other) {
62 // Acquire both locks atomically to prevent deadlock
63 // std::scoped_lock ensures locks are acquired in a consistent order
64 std::scoped_lock lock(mutex_, other.mutex_);
65 values_ = std::move(other.values_);
66 write_count_.fetch_add(1, std::memory_order_relaxed);
67 }
68 return *this;
69 }
70
71 std::optional<value> thread_safe_container::get(std::string_view key) const
72 {
73 std::shared_lock lock(mutex_);
74 read_count_.fetch_add(1, std::memory_order_relaxed);
75
76 auto it = values_.find(std::string(key));
77 if (it != values_.end()) {
78 return it->second;
79 }
80 return std::nullopt;
81 }
82
83 void thread_safe_container::set(std::string_view key, value value)
84 {
85 std::unique_lock lock(mutex_);
86 write_count_.fetch_add(1, std::memory_order_relaxed);
87
88 values_[std::string(key)] = std::move(value);
89 }
90
91 bool thread_safe_container::remove(std::string_view key)
92 {
93 std::unique_lock lock(mutex_);
94 write_count_.fetch_add(1, std::memory_order_relaxed);
95
96 auto it = values_.find(std::string(key));
97 if (it != values_.end()) {
98 values_.erase(it);
99 return true;
100 }
101 return false;
102 }
103
105 {
106 std::unique_lock lock(mutex_);
107 write_count_.fetch_add(1, std::memory_order_relaxed);
108 values_.clear();
109 }
110
112 {
113 std::shared_lock lock(mutex_);
114 return values_.size();
115 }
116
118 {
119 std::shared_lock lock(mutex_);
120 return values_.empty();
121 }
122
123 bool thread_safe_container::contains(std::string_view key) const
124 {
125 std::shared_lock lock(mutex_);
126 read_count_.fetch_add(1, std::memory_order_relaxed);
127 return values_.find(std::string(key)) != values_.end();
128 }
129
130 std::vector<std::string> thread_safe_container::keys() const
131 {
132 std::shared_lock lock(mutex_);
133 read_count_.fetch_add(1, std::memory_order_relaxed);
134
135 std::vector<std::string> result;
136 result.reserve(values_.size());
137
138 for (const auto& [key, value] : values_) {
139 result.push_back(key);
140 }
141
142 return result;
143 }
144
146 {
147 std::unique_lock lock(mutex_);
148 write_count_.fetch_add(1, std::memory_order_relaxed);
149 values_[std::string(val.name())] = val;
150 }
151
152 std::optional<value> thread_safe_container::get_variant(const std::string& key) const
153 {
154 std::shared_lock lock(mutex_);
155 read_count_.fetch_add(1, std::memory_order_relaxed);
156
157 auto it = values_.find(key);
158 if (it != values_.end()) {
159 return it->second;
160 }
161 return std::nullopt;
162 }
163
164 void thread_safe_container::set_container(const std::string& key,
165 std::shared_ptr<thread_safe_container> container)
166 {
167 std::unique_lock lock(mutex_);
168 write_count_.fetch_add(1, std::memory_order_relaxed);
169 values_[key] = value(key, std::move(container));
170 }
171
172 std::shared_ptr<thread_safe_container> thread_safe_container::get_container(
173 const std::string& key) const
174 {
175 std::shared_lock lock(mutex_);
176 read_count_.fetch_add(1, std::memory_order_relaxed);
177
178 auto it = values_.find(key);
179 if (it != values_.end()) {
180 return it->second.get<std::shared_ptr<thread_safe_container>>().value_or(nullptr);
181 }
182 return nullptr;
183 }
184
186 const value& expected,
187 const value& desired)
188 {
189 std::unique_lock lock(mutex_);
190 write_count_.fetch_add(1, std::memory_order_relaxed);
191
192 auto it = values_.find(std::string(key));
193 if (it != values_.end()) {
194 if (it->second == expected) {
195 it->second = desired;
196 return true;
197 }
198 }
199 return false;
200 }
201
203 {
204 std::shared_lock lock(mutex_);
205 return {
206 read_count_.load(std::memory_order_relaxed),
207 write_count_.load(std::memory_order_relaxed),
208 bulk_read_count_.load(std::memory_order_relaxed),
209 bulk_write_count_.load(std::memory_order_relaxed),
210 values_.size()
211 };
212 }
213
215 {
216 std::shared_lock lock(mutex_);
217 read_count_.fetch_add(1, std::memory_order_relaxed);
218
219 std::string result = "{";
220 bool first = true;
221
222 for (const auto& [key, value] : values_) {
223 if (!first) {
224 result += ",";
225 }
226 first = false;
227
228 // Escape key for JSON
229 result += "\"";
230 for (char c : key) {
231 switch (c) {
232 case '"': result += "\\\""; break;
233 case '\\': result += "\\\\"; break;
234 case '\n': result += "\\n"; break;
235 case '\r': result += "\\r"; break;
236 case '\t': result += "\\t"; break;
237 default: result += c;
238 }
239 }
240 result += "\":";
241 result += value.to_json();
242 }
243
244 result += "}";
245 return result;
246 }
247
248 std::vector<uint8_t> thread_safe_container::serialize() const
249 {
250 std::shared_lock lock(mutex_);
251 read_count_.fetch_add(1, std::memory_order_relaxed);
252
253 std::vector<uint8_t> result;
254
255 // Header: number of entries (4 bytes)
256 uint32_t count = static_cast<uint32_t>(values_.size());
257 result.insert(result.end(),
258 reinterpret_cast<const uint8_t*>(&count),
259 reinterpret_cast<const uint8_t*>(&count) + sizeof(count));
260
261 // Serialize each key-value pair
262 for (const auto& [key, value] : values_) {
263 // Key length and key
264 uint32_t key_len = static_cast<uint32_t>(key.size());
265 result.insert(result.end(),
266 reinterpret_cast<const uint8_t*>(&key_len),
267 reinterpret_cast<const uint8_t*>(&key_len) + sizeof(key_len));
268 result.insert(result.end(), key.begin(), key.end());
269
270 // Value serialization
271 auto value_data = value.serialize();
272 uint32_t value_len = static_cast<uint32_t>(value_data.size());
273 result.insert(result.end(),
274 reinterpret_cast<const uint8_t*>(&value_len),
275 reinterpret_cast<const uint8_t*>(&value_len) + sizeof(value_len));
276 result.insert(result.end(), value_data.begin(), value_data.end());
277 }
278
279 return result;
280 }
281
282 std::shared_ptr<thread_safe_container> thread_safe_container::deserialize(
283 const std::vector<uint8_t>& data)
284 {
285 if (data.size() < sizeof(uint32_t)) {
286 return nullptr;
287 }
288
289 auto container = std::make_shared<thread_safe_container>();
290 size_t offset = 0;
291
292 // Read number of entries
293 uint32_t count;
294 std::memcpy(&count, data.data() + offset, sizeof(count));
295 offset += sizeof(count);
296
297 // Read each key-value pair
298 for (uint32_t i = 0; i < count; ++i) {
299 if (offset + sizeof(uint32_t) > data.size()) {
300 return nullptr;
301 }
302
303 // Read key
304 uint32_t key_len;
305 std::memcpy(&key_len, data.data() + offset, sizeof(key_len));
306 offset += sizeof(key_len);
307
308 if (offset + key_len + sizeof(uint32_t) > data.size()) {
309 return nullptr;
310 }
311
312 std::string key(data.begin() + offset, data.begin() + offset + key_len);
313 offset += key_len;
314
315 // Read value length
316 uint32_t value_len;
317 std::memcpy(&value_len, data.data() + offset, sizeof(value_len));
318 offset += sizeof(value_len);
319
320 if (offset + value_len > data.size()) {
321 return nullptr;
322 }
323
324 // Deserialize value
325 std::vector<uint8_t> value_data(data.begin() + offset,
326 data.begin() + offset + value_len);
327 offset += value_len;
328
329 auto value_opt = value::deserialize(value_data);
330 if (value_opt) {
331 container->values_[key] = std::move(*value_opt);
332 }
333 }
334
335 return container;
336 }
337
339 {
340 std::unique_lock lock(mutex_);
341 write_count_.fetch_add(1, std::memory_order_relaxed);
342
343 auto it = values_.find(key);
344 if (it == values_.end()) {
345 auto [new_it, inserted] = values_.emplace(key, value(key));
346 return new_it->second;
347 }
348 return it->second;
349 }
350
351 // snapshot_reader implementation
353 {
354 auto new_snapshot = std::make_shared<thread_safe_container::value_map>(
355 container_->bulk_read([](const auto& map) { return map; })
356 );
357
358 std::unique_lock lock(snapshot_mutex_);
359 snapshot_ = new_snapshot;
360 }
361
362} // namespace kcenon::container
void update_snapshot()
Update snapshot from container.
std::shared_ptr< thread_safe_container > container_
Thread-safe container with lock optimization.
std::optional< value > get(std::string_view key) const
Get value by key (thread-safe read)
bool contains(std::string_view key) const
Check if key exists.
thread_safe_container & operator=(const thread_safe_container &other)
Copy assignment (thread-safe)
std::shared_ptr< thread_safe_container > get_container(const std::string &key) const
Get a nested container.
value & operator[](const std::string &key)
Array-style access (creates if not exists)
size_t size() const
Get the number of values.
thread_safe_container()=default
Default constructor.
bool empty() const
Check if container is empty.
std::vector< std::string > keys() const
Get all keys.
static std::shared_ptr< thread_safe_container > deserialize(const std::vector< uint8_t > &data)
Deserialize from binary.
std::optional< value > get_variant(const std::string &key) const
Get a value as variant value.
std::string to_json() const
Serialize container to JSON.
void set_variant(const value &val)
Set a value directly using variant value.
bool remove(std::string_view key)
Remove value by key.
void set(std::string_view key, value value)
Set value for key (thread-safe write)
void set_container(const std::string &key, std::shared_ptr< thread_safe_container > container)
Set a nested container.
std::vector< uint8_t > serialize() const
Serialize container to binary.
bool compare_exchange(std::string_view key, const value &expected, const value &desired)
Atomic compare and swap.
Enhanced type-safe value with perfect legacy compatibility.
Definition value.h:129
std::string to_json() const
Convert to JSON representation.
Definition value.cpp:186
std::string_view name() const noexcept
Get the name of this value (lock-free, immutable)
Definition value.h:177
static std::optional< value > deserialize(const std::vector< uint8_t > &data)
Deserialize from binary format (legacy compatible)
Definition value.cpp:555
std::vector< uint8_t > serialize() const
Serialize to binary format (legacy compatible)
Definition value.cpp:339