Container System 0.1.0
High-performance C++20 type-safe container framework with SIMD-accelerated serialization
Loading...
Searching...
No Matches
value.cpp
Go to the documentation of this file.
1/*****************************************************************************
2BSD 3-Clause License
3
4Copyright (c) 2024, 🍀☀🌕🌥 🌊
5All rights reserved.
6*****************************************************************************/
7
8#include "value.h"
10#include <sstream>
11#include <iomanip>
12#include <cstring>
13#include <stdexcept>
14#include <set>
15
16namespace kcenon::container
17{
18 namespace detail {
19 // Thread-local set for detecting circular references during serialization
20 thread_local std::set<const void*> serializing_containers;
21
23 const void* ptr;
25
26 explicit circular_ref_guard(const thread_safe_container* container)
27 : ptr(container), inserted(false) {
28 if (ptr) {
29 auto result = serializing_containers.insert(ptr);
30 inserted = result.second;
31 }
32 }
33
35 if (inserted && ptr) {
37 }
38 }
39
40 bool is_circular() const { return ptr && !inserted; }
41 };
42 }
43 // ============================================================================
44 // array_variant implementation
45 // ============================================================================
46
48 values.reserve(other.values.size());
49 for (const auto& val : other.values) {
50 if (val) {
51 values.push_back(std::make_shared<value>(*val));
52 }
53 }
54 }
55
57 if (this != &other) {
58 values.clear();
59 values.reserve(other.values.size());
60 for (const auto& val : other.values) {
61 if (val) {
62 values.push_back(std::make_shared<value>(*val));
63 }
64 }
65 }
66 return *this;
67 }
68
69 bool array_variant::operator==(const array_variant& other) const {
70 if (values.size() != other.values.size()) return false;
71 for (size_t i = 0; i < values.size(); ++i) {
72 if (!values[i] && !other.values[i]) continue;
73 if (!values[i] || !other.values[i]) return false;
74 if (*values[i] != *other.values[i]) return false;
75 }
76 return true;
77 }
78
79 bool array_variant::operator<(const array_variant& other) const {
80 return values.size() < other.values.size();
81 }
82
83 // ============================================================================
84 // value implementation
85 // ============================================================================
86
87 value::value(std::string_view name,
88 value_types type,
89 const std::vector<uint8_t>& raw_data)
90 : name_(name), data_(std::in_place_index<0>)
91 {
92 // Construct variant from legacy type and raw data
93 size_t offset = 0;
94 if (!deserialize_data(*this, type, raw_data, offset)) {
95 // Failed to deserialize, reset to null
96 data_.emplace<0>();
97 }
98 }
99
100 value::value(const value& other)
101 : name_(other.name_)
102 {
103 std::shared_lock lock(other.mutex_);
104 data_ = other.data_;
105 }
106
107 value::value(value&& other) noexcept
108 : name_(other.name_)
109 {
110 std::unique_lock lock(other.mutex_);
111 data_ = std::move(other.data_);
112 read_count_ = other.read_count_.load();
113 write_count_ = other.write_count_.load();
114 other.read_count_ = 0;
115 other.write_count_ = 0;
116 }
117
118 value& value::operator=(const value& other) {
119 if (this != &other) {
120 std::unique_lock lock(mutex_);
121 std::shared_lock other_lock(other.mutex_);
122 data_ = other.data_;
123 write_count_.fetch_add(1, std::memory_order_relaxed);
124 }
125 return *this;
126 }
127
128 value& value::operator=(value&& other) noexcept {
129 if (this != &other) {
130 std::unique_lock lock(mutex_);
131 std::unique_lock other_lock(other.mutex_);
132 data_ = std::move(other.data_);
133 write_count_.fetch_add(1, std::memory_order_relaxed);
134 }
135 return *this;
136 }
137
138 value_types value::type() const {
139 std::shared_lock lock(mutex_);
140 // Direct mapping: variant index == value_types enum value
141 // Positions 6-9 now use unaliased fundamental types (long, unsigned long,
142 // long long, unsigned long long) so direct index cast is always correct.
143 return static_cast<value_types>(data_.index());
144 }
145
146 std::string value::to_string() const {
147 return visit([](auto&& value) -> std::string {
148 using T = std::decay_t<decltype(value)>;
149
150 if constexpr (std::is_same_v<T, std::monostate>) {
151 return "null";
152 }
153 else if constexpr (std::is_same_v<T, bool>) {
154 return value ? "true" : "false";
155 }
156 else if constexpr (std::is_same_v<T, std::vector<uint8_t>>) {
157 std::ostringstream oss;
158 oss << std::hex << std::setfill('0');
159 for (auto byte : value) {
160 oss << std::setw(2) << static_cast<int>(byte);
161 }
162 return oss.str();
163 }
164 else if constexpr (std::is_arithmetic_v<T>) {
165 return std::to_string(value);
166 }
167 else if constexpr (std::is_same_v<T, std::string>) {
168 return value;
169 }
170 else if constexpr (std::is_same_v<T, std::shared_ptr<thread_safe_container>>) {
171 return value ? value->to_json() : "null";
172 }
173 else if constexpr (std::is_same_v<T, array_variant>) {
174 std::string result = "[";
175 for (size_t i = 0; i < value.values.size(); ++i) {
176 if (i > 0) result += ",";
177 result += value.values[i] ? value.values[i]->to_string() : "null";
178 }
179 result += "]";
180 return result;
181 }
182 return "";
183 });
184 }
185
186 std::string value::to_json() const {
187 auto var_name = name();
188 auto var_type = type();
189
190 return visit([var_name, var_type](auto&& value) -> std::string {
191 using T = std::decay_t<decltype(value)>;
192
193 // Manual JSON header formatting (no std::format dependency)
194 std::string result = "{\"name\":\"" + std::string(var_name) +
195 "\",\"type\":" + std::to_string(static_cast<int>(var_type)) +
196 ",\"value\":";
197
198 if constexpr (std::is_same_v<T, std::monostate>) {
199 result += "null";
200 }
201 else if constexpr (std::is_same_v<T, bool>) {
202 result += value ? "true" : "false";
203 }
204 else if constexpr (std::is_same_v<T, std::vector<uint8_t>>) {
205 result += "\"";
206 std::ostringstream oss;
207 oss << std::hex << std::setfill('0');
208 for (auto byte : value) {
209 oss << std::setw(2) << static_cast<int>(byte);
210 }
211 result += oss.str();
212 result += "\"";
213 }
214 else if constexpr (std::is_arithmetic_v<T>) {
215 result += std::to_string(value);
216 }
217 else if constexpr (std::is_same_v<T, std::string>) {
218 // Escape string for JSON
219 result += "\"";
220 for (char c : value) {
221 switch (c) {
222 case '"': result += "\\\""; break;
223 case '\\': result += "\\\\"; break;
224 case '\b': result += "\\b"; break;
225 case '\f': result += "\\f"; break;
226 case '\n': result += "\\n"; break;
227 case '\r': result += "\\r"; break;
228 case '\t': result += "\\t"; break;
229 default:
230 if (c >= 0x20 && c <= 0x7E) {
231 result += c;
232 } else {
233 // Manual Unicode escape formatting (no std::format dependency)
234 char buf[7]; // "\uXXXX" + null terminator
235 std::snprintf(buf, sizeof(buf), "\\u%04x", static_cast<unsigned char>(c));
236 result += buf;
237 }
238 }
239 }
240 result += "\"";
241 }
242 else if constexpr (std::is_same_v<T, std::shared_ptr<thread_safe_container>>) {
243 result += value ? value->to_json() : "null";
244 }
245 else if constexpr (std::is_same_v<T, array_variant>) {
246 result += "[";
247 for (size_t i = 0; i < value.values.size(); ++i) {
248 if (i > 0) result += ",";
249 result += value.values[i] ? value.values[i]->to_json() : "null";
250 }
251 result += "]";
252 }
253
254 result += "}";
255 return result;
256 });
257 }
258
259 void value::serialize_data(std::vector<uint8_t>& result) const {
260 visit([&result](auto&& val) {
261 using T = std::decay_t<decltype(val)>;
262
263 if constexpr (std::is_same_v<T, std::monostate>) {
264 // Null val: no data
265 }
266 else if constexpr (std::is_same_v<T, bool>) {
267 result.push_back(val ? 1 : 0);
268 }
269 else if constexpr (std::is_same_v<T, std::vector<uint8_t>>) {
270 // Bytes: [length:4][data:length]
271 uint32_t size = static_cast<uint32_t>(val.size());
272 result.insert(result.end(),
273 reinterpret_cast<const uint8_t*>(&size),
274 reinterpret_cast<const uint8_t*>(&size) + sizeof(size));
275 result.insert(result.end(), val.begin(), val.end());
276 }
277 else if constexpr (std::is_arithmetic_v<T> && !std::is_same_v<T, bool>) {
278 // Numeric: raw bytes
279 result.insert(result.end(),
280 reinterpret_cast<const uint8_t*>(&val),
281 reinterpret_cast<const uint8_t*>(&val) + sizeof(T));
282 }
283 else if constexpr (std::is_same_v<T, std::string>) {
284 // String: [length:4][UTF-8 data:length]
285 uint32_t size = static_cast<uint32_t>(val.size());
286 result.insert(result.end(),
287 reinterpret_cast<const uint8_t*>(&size),
288 reinterpret_cast<const uint8_t*>(&size) + sizeof(size));
289 result.insert(result.end(), val.begin(), val.end());
290 }
291 else if constexpr (std::is_same_v<T, std::shared_ptr<thread_safe_container>>) {
292 // Container: [serialized_size:4][serialized_data:size]
293 if (val) {
294 // Check for circular reference
295 detail::circular_ref_guard guard(val.get());
296 if (guard.is_circular()) {
297 // Circular reference detected - serialize as null container
298 uint32_t size = 0;
299 result.insert(result.end(),
300 reinterpret_cast<const uint8_t*>(&size),
301 reinterpret_cast<const uint8_t*>(&size) + sizeof(size));
302 } else {
303 auto container_data = val->serialize();
304 uint32_t size = static_cast<uint32_t>(container_data.size());
305 result.insert(result.end(),
306 reinterpret_cast<const uint8_t*>(&size),
307 reinterpret_cast<const uint8_t*>(&size) + sizeof(size));
308 result.insert(result.end(), container_data.begin(), container_data.end());
309 }
310 } else {
311 uint32_t size = 0;
312 result.insert(result.end(),
313 reinterpret_cast<const uint8_t*>(&size),
314 reinterpret_cast<const uint8_t*>(&size) + sizeof(size));
315 }
316 }
317 else if constexpr (std::is_same_v<T, array_variant>) {
318 // Array: [count:4][value1_serialized][value2_serialized]...
319 uint32_t count = static_cast<uint32_t>(val.values.size());
320 result.insert(result.end(),
321 reinterpret_cast<const uint8_t*>(&count),
322 reinterpret_cast<const uint8_t*>(&count) + sizeof(count));
323
324 for (const auto& elem : val.values) {
325 if (elem) {
326 auto elem_data = elem->serialize();
327 result.insert(result.end(), elem_data.begin(), elem_data.end());
328 } else {
329 // Null element: serialize as null_value with empty name
330 kcenon::container::value null_elem("");
331 auto null_data = null_elem.serialize();
332 result.insert(result.end(), null_data.begin(), null_data.end());
333 }
334 }
335 }
336 });
337 }
338
339 std::vector<uint8_t> value::serialize() const {
340 std::vector<uint8_t> result;
341
342 // Header: [name_length:4][name:UTF-8][type:1]
343 uint32_t name_len = static_cast<uint32_t>(name_.size());
344 result.insert(result.end(),
345 reinterpret_cast<const uint8_t*>(&name_len),
346 reinterpret_cast<const uint8_t*>(&name_len) + sizeof(name_len));
347 result.insert(result.end(), name_.begin(), name_.end());
348
349 // Type byte: use value_types enum value (0-15)
350 uint8_t type_byte = static_cast<uint8_t>(type());
351 result.push_back(type_byte);
352
353 // Data: type-specific serialization
354 serialize_data(result);
355
356 return result;
357 }
358
360 value_types type,
361 const std::vector<uint8_t>& data,
362 size_t& offset) {
363 switch (type) {
364 case value_types::null_value:
365 result.data_.emplace<0>(); // null is at index 0
366 return true;
367
368 case value_types::bool_value:
369 if (offset >= data.size()) return false;
370 result.data_ = (data[offset++] != 0);
371 return true;
372
373 case value_types::short_value: {
374 if (offset + sizeof(int16_t) > data.size()) return false;
375 int16_t value;
376 std::memcpy(&value, data.data() + offset, sizeof(value));
377 offset += sizeof(value);
378 result.data_ = value;
379 return true;
380 }
381
382 case value_types::ushort_value: {
383 if (offset + sizeof(uint16_t) > data.size()) return false;
384 uint16_t value;
385 std::memcpy(&value, data.data() + offset, sizeof(value));
386 offset += sizeof(value);
387 result.data_ = value;
388 return true;
389 }
390
391 case value_types::int_value: {
392 if (offset + sizeof(int32_t) > data.size()) return false;
393 int32_t value;
394 std::memcpy(&value, data.data() + offset, sizeof(value));
395 offset += sizeof(value);
396 result.data_ = value;
397 return true;
398 }
399
400 case value_types::uint_value: {
401 if (offset + sizeof(uint32_t) > data.size()) return false;
402 uint32_t value;
403 std::memcpy(&value, data.data() + offset, sizeof(value));
404 offset += sizeof(value);
405 result.data_ = value;
406 return true;
407 }
408
409 case value_types::long_value: {
410 if (offset + sizeof(int64_t) > data.size()) return false;
411 int64_t value;
412 std::memcpy(&value, data.data() + offset, sizeof(value));
413 offset += sizeof(value);
414 result.data_ = value;
415 return true;
416 }
417
418 case value_types::llong_value: {
419 if (offset + sizeof(int64_t) > data.size()) return false;
420#if defined(_MSC_VER) && _MSC_VER < 1900
421 // Old MSVC (2013-): int64_t is __int64, long long is separate
422 long long value;
423 std::memcpy(&value, data.data() + offset, sizeof(value));
424 offset += sizeof(value);
425 result.data_ = value; // Stored at index 8 (llong_value slot)
426#else
427 // Modern platforms (macOS, Linux, MSVC 2015+): int64_t == long long
428 // llong_value uses same storage as long_value (int64_t)
429 int64_t value;
430 std::memcpy(&value, data.data() + offset, sizeof(value));
431 offset += sizeof(value);
432 result.data_ = value; // Stored at index 6 (long_value slot)
433#endif
434 return true;
435 }
436
437 case value_types::ulong_value: {
438 if (offset + sizeof(uint64_t) > data.size()) return false;
439 uint64_t value;
440 std::memcpy(&value, data.data() + offset, sizeof(value));
441 offset += sizeof(value);
442 result.data_ = value;
443 return true;
444 }
445
446 case value_types::ullong_value: {
447 if (offset + sizeof(uint64_t) > data.size()) return false;
448#if defined(_MSC_VER) && _MSC_VER < 1900
449 // Old MSVC (2013-): uint64_t is unsigned __int64, unsigned long long is separate
450 unsigned long long value;
451 std::memcpy(&value, data.data() + offset, sizeof(value));
452 offset += sizeof(value);
453 result.data_ = value; // Stored at index 9 (ullong_value slot)
454#else
455 // Modern platforms (macOS, Linux, MSVC 2015+): uint64_t == unsigned long long
456 // ullong_value uses same storage as ulong_value (uint64_t)
457 uint64_t value;
458 std::memcpy(&value, data.data() + offset, sizeof(value));
459 offset += sizeof(value);
460 result.data_ = value; // Stored at index 7 (ulong_value slot)
461#endif
462 return true;
463 }
464
465 case value_types::float_value: {
466 if (offset + sizeof(float) > data.size()) return false;
467 float value;
468 std::memcpy(&value, data.data() + offset, sizeof(value));
469 offset += sizeof(value);
470 result.data_ = value;
471 return true;
472 }
473
474 case value_types::double_value: {
475 if (offset + sizeof(double) > data.size()) return false;
476 double value;
477 std::memcpy(&value, data.data() + offset, sizeof(value));
478 offset += sizeof(value);
479 result.data_ = value;
480 return true;
481 }
482
483 case value_types::bytes_value: {
484 if (offset + sizeof(uint32_t) > data.size()) return false;
485 uint32_t size;
486 std::memcpy(&size, data.data() + offset, sizeof(size));
487 offset += sizeof(size);
488 if (offset + size > data.size()) return false;
489 std::vector<uint8_t> bytes(data.begin() + offset,
490 data.begin() + offset + size);
491 offset += size;
492 result.data_ = std::move(bytes);
493 return true;
494 }
495
496 case value_types::string_value: {
497 if (offset + sizeof(uint32_t) > data.size()) return false;
498 uint32_t size;
499 std::memcpy(&size, data.data() + offset, sizeof(size));
500 offset += sizeof(size);
501 if (offset + size > data.size()) return false;
502 std::string str(data.begin() + offset, data.begin() + offset + size);
503 offset += size;
504 result.data_ = std::move(str);
505 return true;
506 }
507
508 case value_types::container_value: {
509 if (offset + sizeof(uint32_t) > data.size()) return false;
510 uint32_t size;
511 std::memcpy(&size, data.data() + offset, sizeof(size));
512 offset += sizeof(size);
513 if (size == 0) {
514 result.data_ = std::shared_ptr<thread_safe_container>(nullptr);
515 return true;
516 }
517 if (offset + size > data.size()) return false;
518 std::vector<uint8_t> container_data(data.begin() + offset,
519 data.begin() + offset + size);
520 offset += size;
521 auto container = thread_safe_container::deserialize(container_data);
522 result.data_ = container;
523 return true;
524 }
525
526 case value_types::array_value: {
527 if (offset + sizeof(uint32_t) > data.size()) return false;
528 uint32_t count;
529 std::memcpy(&count, data.data() + offset, sizeof(count));
530 offset += sizeof(count);
531
532 array_variant arr;
533 arr.values.reserve(count);
534
535 for (uint32_t i = 0; i < count; ++i) {
536 auto elem = deserialize(std::vector<uint8_t>(data.begin() + offset, data.end()));
537 if (!elem) return false;
538
539 // Update offset based on elem's serialized size
540 auto elem_size = elem->serialize().size();
541 offset += elem_size;
542
543 arr.values.push_back(std::make_shared<value>(std::move(*elem)));
544 }
545
546 result.data_ = std::move(arr);
547 return true;
548 }
549
550 default:
551 return false;
552 }
553 }
554
555 std::optional<value> value::deserialize(const std::vector<uint8_t>& data) {
556 if (data.size() < sizeof(uint32_t) + 1) {
557 return std::nullopt; // Too small: need at least name_len + type
558 }
559
560 size_t offset = 0;
561
562 // Read name length
563 uint32_t name_len;
564 std::memcpy(&name_len, data.data() + offset, sizeof(name_len));
565 offset += sizeof(name_len);
566
567 if (offset + name_len + 1 > data.size()) {
568 return std::nullopt; // Invalid name length
569 }
570
571 // Read name
572 std::string name(data.begin() + offset, data.begin() + offset + name_len);
573 offset += name_len;
574
575 // Read type byte
576 uint8_t type_byte = data[offset++];
577 value_types type = static_cast<value_types>(type_byte);
578
579 // Validate type range
580 if (type_byte > 15) {
581 return std::nullopt; // Invalid type
582 }
583
584 // Create result with name
586
587 // Deserialize data based on type
588 if (!deserialize_data(result, type, data, offset)) {
589 return std::nullopt;
590 }
591
592 return result;
593 }
594
595 bool value::operator==(const value& other) const {
596 if (this == &other) return true;
597 std::scoped_lock lock(mutex_, other.mutex_);
598 return name_ == other.name_ && data_ == other.data_;
599 }
600
601 bool value::operator<(const value& other) const {
602 if (this == &other) return false;
603 std::scoped_lock lock(mutex_, other.mutex_);
604 if (name_ != other.name_) return name_ < other.name_;
605 return data_ < other.data_;
606 }
607
608} // namespace kcenon::container
Thread-safe container with lock optimization.
static std::shared_ptr< thread_safe_container > deserialize(const std::vector< uint8_t > &data)
Deserialize from binary.
Enhanced type-safe value with perfect legacy compatibility.
Definition value.h:129
ValueVariant data_
Definition value.h:313
static bool deserialize_data(value &result, value_types type, const std::vector< uint8_t > &data, size_t &offset)
Deserialize data portion based on type.
Definition value.cpp:359
std::string to_json() const
Convert to JSON representation.
Definition value.cpp:186
value_types type() const
Get the value_types enum (NOT variant::index()!)
Definition value.cpp:138
bool operator==(const value &other) const
Comparison operators (thread-safe)
Definition value.cpp:595
value & operator=(const value &other)
Copy assignment (thread-safe)
Definition value.cpp:118
std::string to_string() const
Convert to string representation.
Definition value.cpp:146
std::shared_mutex mutex_
Definition value.h:314
const std::string name_
Definition value.h:312
std::string_view name() const noexcept
Get the name of this value (lock-free, immutable)
Definition value.h:177
value()
Default constructor - creates null value.
Definition value.h:134
void serialize_data(std::vector< uint8_t > &result) const
Serialize data portion based on type.
Definition value.cpp:259
bool operator<(const value &other) const
Definition value.cpp:601
static std::optional< value > deserialize(const std::vector< uint8_t > &data)
Deserialize from binary format (legacy compatible)
Definition value.cpp:555
std::atomic< size_t > write_count_
Definition value.h:316
auto visit(Visitor &&vis) const
Apply visitor to contained value (const)
Definition value.h:237
std::vector< uint8_t > serialize() const
Serialize to binary format (legacy compatible)
Definition value.cpp:339
thread_local std::set< const void * > serializing_containers
Definition value.cpp:20
Recursive array type for variant.
Definition value.h:45
bool operator==(const array_variant &other) const
Definition value.cpp:69
std::vector< std::shared_ptr< value > > values
Definition value.h:46
array_variant & operator=(const array_variant &other)
Definition value.cpp:56
bool operator<(const array_variant &other) const
Definition value.cpp:79
circular_ref_guard(const thread_safe_container *container)
Definition value.cpp:26