Container System 0.1.0
High-performance C++20 type-safe container framework with SIMD-accelerated serialization
Loading...
Searching...
No Matches
msgpack_serializer.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2021, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
6#include "../container.h"
8
9namespace kcenon::container
10{
11
12#if CONTAINER_HAS_RESULT
13
14namespace
15{
16 // Helper function to serialize a value_container to msgpack
17 // This allows recursive serialization of nested containers
18 std::vector<uint8_t> serialize_container_to_msgpack(const value_container& container);
19
20 void write_value_to_encoder(msgpack_encoder& encoder, const optimized_value& unit)
21 {
22 switch (unit.type)
23 {
24 case value_types::null_value:
25 encoder.write_nil();
26 break;
27
28 case value_types::bool_value:
29 encoder.write_bool(std::get<bool>(unit.data));
30 break;
31
32 case value_types::short_value:
33 encoder.write_int(std::get<short>(unit.data));
34 break;
35
36 case value_types::ushort_value:
37 encoder.write_uint(std::get<unsigned short>(unit.data));
38 break;
39
40 case value_types::int_value:
41 encoder.write_int(std::get<int>(unit.data));
42 break;
43
44 case value_types::uint_value:
45 encoder.write_uint(std::get<unsigned int>(unit.data));
46 break;
47
48 case value_types::long_value:
49 encoder.write_int(std::get<long>(unit.data));
50 break;
51
52 case value_types::ulong_value:
53 encoder.write_uint(std::get<unsigned long>(unit.data));
54 break;
55
56 case value_types::llong_value:
57 encoder.write_int(std::get<long long>(unit.data));
58 break;
59
60 case value_types::ullong_value:
61 encoder.write_uint(std::get<unsigned long long>(unit.data));
62 break;
63
64 case value_types::float_value:
65 encoder.write_float(std::get<float>(unit.data));
66 break;
67
68 case value_types::double_value:
69 encoder.write_double(std::get<double>(unit.data));
70 break;
71
72 case value_types::string_value:
73 encoder.write_string(std::get<std::string>(unit.data));
74 break;
75
76 case value_types::bytes_value:
77 encoder.write_binary(std::get<std::vector<uint8_t>>(unit.data));
78 break;
79
80 case value_types::container_value:
81 {
82 // Nested container: serialize recursively
83 auto nested = std::get<std::shared_ptr<value_container>>(unit.data);
84 if (nested)
85 {
86 auto nested_data = serialize_container_to_msgpack(*nested);
87 encoder.write_binary(nested_data);
88 }
89 else
90 {
91 encoder.write_nil();
92 }
93 }
94 break;
95
96 case value_types::array_value:
97 {
98 auto arr = std::get<std::shared_ptr<array_values>>(unit.data);
99 if (arr && !arr->items.empty())
100 {
101 encoder.write_array_header(static_cast<uint32_t>(arr->items.size()));
102 for (const auto& item : arr->items)
103 {
104 // Create a temporary optimized_value to reuse write_value_to_encoder
105 optimized_value temp;
106 temp.data = item;
107 temp.type = static_cast<value_types>(item.index());
108 write_value_to_encoder(encoder, temp);
109 }
110 }
111 else
112 {
113 encoder.write_array_header(0);
114 }
115 }
116 break;
117
118 default:
119 encoder.write_nil();
120 break;
121 }
122 }
123
124 std::vector<uint8_t> serialize_container_to_msgpack(const value_container& container)
125 {
126 // Get container data through public accessors
127 auto message_type = container.message_type();
128 auto target_id = container.target_id();
129 auto target_sub_id = container.target_sub_id();
130 auto source_id = container.source_id();
131 auto source_sub_id = container.source_sub_id();
132 auto version = container.version();
133 auto values = container.get_variant_values();
134
135 msgpack_encoder encoder;
136
137 // Estimate buffer size: header (~100 bytes) + values
138 encoder.reserve(200 + values.size() * 32);
139
140 // MessagePack structure:
141 // {
142 // "header": { ... },
143 // "values": { ... }
144 // }
145
146 // Write outer map with 2 entries (header + values)
147 encoder.write_map_header(2);
148
149 // Write "header" key
150 encoder.write_string("header");
151
152 // Calculate header entries count
153 size_t header_count = 2; // message_type and version are always present
154 if (message_type != "data_container")
155 {
156 header_count += 4; // target_id, target_sub_id, source_id, source_sub_id
157 }
158
159 // Write header map
160 encoder.write_map_header(header_count);
161
162 if (message_type != "data_container")
163 {
164 encoder.write_string("target_id");
165 encoder.write_string(target_id);
166
167 encoder.write_string("target_sub_id");
168 encoder.write_string(target_sub_id);
169
170 encoder.write_string("source_id");
171 encoder.write_string(source_id);
172
173 encoder.write_string("source_sub_id");
174 encoder.write_string(source_sub_id);
175 }
176
177 encoder.write_string("message_type");
178 encoder.write_string(message_type);
179
180 encoder.write_string("version");
181 encoder.write_string(version);
182
183 // Write "values" key
184 encoder.write_string("values");
185
186 // Write values map
187 encoder.write_map_header(values.size());
188
189 for (const auto& unit : values)
190 {
191 // Write key
192 encoder.write_string(unit.name);
193
194 // Write value based on type
195 write_value_to_encoder(encoder, unit);
196 }
197
198 return encoder.finish();
199 }
200} // anonymous namespace
201
202kcenon::common::Result<std::vector<uint8_t>>
203msgpack_serializer::serialize(const value_container& container) const noexcept
204{
205 try
206 {
207 auto data = serialize_container_to_msgpack(container);
208 return kcenon::common::ok(std::move(data));
209 }
210 catch (const std::bad_alloc&)
211 {
212 return kcenon::common::Result<std::vector<uint8_t>>(
213 kcenon::common::error_info{
214 -2,
215 "Memory allocation failed during MessagePack serialization",
216 "msgpack_serializer"});
217 }
218 catch (const std::exception& e)
219 {
220 return kcenon::common::Result<std::vector<uint8_t>>(
221 kcenon::common::error_info{
222 -1,
223 std::string("MessagePack serialization failed: ") + e.what(),
224 "msgpack_serializer"});
225 }
226}
227#endif
228
229} // namespace kcenon::container