/src/glaze/include/glaze/msgpack/common.hpp
Line | Count | Source |
1 | | // Glaze Library |
2 | | // For the license information refer to glaze.hpp |
3 | | |
4 | | #pragma once |
5 | | |
6 | | #include <bit> |
7 | | #include <cstdint> |
8 | | #include <cstring> |
9 | | #include <limits> |
10 | | #include <vector> |
11 | | |
12 | | #include "glaze/core/context.hpp" |
13 | | #include "glaze/core/error_category.hpp" |
14 | | #include "glaze/util/dump.hpp" |
15 | | #include "glaze/util/inline.hpp" |
16 | | |
17 | | namespace glz::msgpack |
18 | | { |
19 | | // Core MessagePack marker bytes |
20 | | inline constexpr uint8_t nil = 0xC0; |
21 | | inline constexpr uint8_t bool_false = 0xC2; |
22 | | inline constexpr uint8_t bool_true = 0xC3; |
23 | | |
24 | | inline constexpr uint8_t bin8 = 0xC4; |
25 | | inline constexpr uint8_t bin16 = 0xC5; |
26 | | inline constexpr uint8_t bin32 = 0xC6; |
27 | | |
28 | | inline constexpr uint8_t ext8 = 0xC7; |
29 | | inline constexpr uint8_t ext16 = 0xC8; |
30 | | inline constexpr uint8_t ext32 = 0xC9; |
31 | | |
32 | | inline constexpr uint8_t float32 = 0xCA; |
33 | | inline constexpr uint8_t float64 = 0xCB; |
34 | | |
35 | | inline constexpr uint8_t uint8 = 0xCC; |
36 | | inline constexpr uint8_t uint16 = 0xCD; |
37 | | inline constexpr uint8_t uint32 = 0xCE; |
38 | | inline constexpr uint8_t uint64 = 0xCF; |
39 | | |
40 | | inline constexpr uint8_t int8 = 0xD0; |
41 | | inline constexpr uint8_t int16 = 0xD1; |
42 | | inline constexpr uint8_t int32 = 0xD2; |
43 | | inline constexpr uint8_t int64 = 0xD3; |
44 | | |
45 | | inline constexpr uint8_t fixext1 = 0xD4; |
46 | | inline constexpr uint8_t fixext2 = 0xD5; |
47 | | inline constexpr uint8_t fixext4 = 0xD6; |
48 | | inline constexpr uint8_t fixext8 = 0xD7; |
49 | | inline constexpr uint8_t fixext16 = 0xD8; |
50 | | |
51 | | inline constexpr uint8_t str8 = 0xD9; |
52 | | inline constexpr uint8_t str16 = 0xDA; |
53 | | inline constexpr uint8_t str32 = 0xDB; |
54 | | |
55 | | inline constexpr uint8_t array16 = 0xDC; |
56 | | inline constexpr uint8_t array32 = 0xDD; |
57 | | |
58 | | inline constexpr uint8_t map16 = 0xDE; |
59 | | inline constexpr uint8_t map32 = 0xDF; |
60 | | |
61 | | inline constexpr uint8_t positive_fixint_mask = 0x80; |
62 | | inline constexpr uint8_t negative_fixint_mask = 0xE0; |
63 | | |
64 | | inline constexpr uint8_t fixmap_mask = 0xF0; |
65 | | inline constexpr uint8_t fixmap_bits = 0x80; |
66 | | |
67 | | inline constexpr uint8_t fixarray_mask = 0xF0; |
68 | | inline constexpr uint8_t fixarray_bits = 0x90; |
69 | | |
70 | | inline constexpr uint8_t fixstr_mask = 0xE0; |
71 | | inline constexpr uint8_t fixstr_bits = 0xA0; |
72 | | |
73 | | GLZ_ALWAYS_INLINE constexpr bool is_positive_fixint(uint8_t tag) noexcept |
74 | 6.23k | { |
75 | 6.23k | return (tag & positive_fixint_mask) == 0; |
76 | 6.23k | } |
77 | | |
78 | | GLZ_ALWAYS_INLINE constexpr bool is_negative_fixint(uint8_t tag) noexcept |
79 | 4.64k | { |
80 | 4.64k | return (tag & negative_fixint_mask) == negative_fixint_mask; |
81 | 4.64k | } |
82 | | |
83 | 0 | GLZ_ALWAYS_INLINE constexpr bool is_fixmap(uint8_t tag) noexcept { return (tag & fixmap_mask) == fixmap_bits; } |
84 | | |
85 | 5.86k | GLZ_ALWAYS_INLINE constexpr bool is_fixarray(uint8_t tag) noexcept { return (tag & fixarray_mask) == fixarray_bits; } |
86 | | |
87 | 1.89k | GLZ_ALWAYS_INLINE constexpr bool is_fixstr(uint8_t tag) noexcept { return (tag & fixstr_mask) == fixstr_bits; } |
88 | | |
89 | | template <class B> |
90 | | GLZ_ALWAYS_INLINE void dump_uint8(uint8_t value, B& b, size_t& ix) noexcept(not vector_like<B>) |
91 | 125 | { |
92 | 125 | dump(static_cast<std::byte>(value), b, ix); |
93 | 125 | } |
94 | | |
95 | | template <class B> |
96 | | GLZ_ALWAYS_INLINE void dump_uint16(uint16_t value, B& b, size_t& ix) noexcept(not vector_like<B>) |
97 | 539 | { |
98 | 539 | dump(static_cast<std::byte>(value >> 8), b, ix); |
99 | 539 | dump(static_cast<std::byte>(value & 0xFF), b, ix); |
100 | 539 | } |
101 | | |
102 | | template <class B> |
103 | | GLZ_ALWAYS_INLINE void dump_uint32(uint32_t value, B& b, size_t& ix) noexcept(not vector_like<B>) |
104 | 733 | { |
105 | 733 | dump(static_cast<std::byte>(value >> 24), b, ix); |
106 | 733 | dump(static_cast<std::byte>((value >> 16) & 0xFF), b, ix); |
107 | 733 | dump(static_cast<std::byte>((value >> 8) & 0xFF), b, ix); |
108 | 733 | dump(static_cast<std::byte>(value & 0xFF), b, ix); |
109 | 733 | } |
110 | | |
111 | | template <class B> |
112 | | GLZ_ALWAYS_INLINE void dump_uint64(uint64_t value, B& b, size_t& ix) noexcept(not vector_like<B>) |
113 | 901 | { |
114 | 901 | dump(static_cast<std::byte>(value >> 56), b, ix); |
115 | 901 | dump(static_cast<std::byte>((value >> 48) & 0xFF), b, ix); |
116 | 901 | dump(static_cast<std::byte>((value >> 40) & 0xFF), b, ix); |
117 | 901 | dump(static_cast<std::byte>((value >> 32) & 0xFF), b, ix); |
118 | 901 | dump(static_cast<std::byte>((value >> 24) & 0xFF), b, ix); |
119 | 901 | dump(static_cast<std::byte>((value >> 16) & 0xFF), b, ix); |
120 | 901 | dump(static_cast<std::byte>((value >> 8) & 0xFF), b, ix); |
121 | 901 | dump(static_cast<std::byte>(value & 0xFF), b, ix); |
122 | 901 | } |
123 | | |
124 | | template <class B> |
125 | | GLZ_ALWAYS_INLINE void dump_float32(float value, B& b, size_t& ix) noexcept(not vector_like<B>) |
126 | 27 | { |
127 | 27 | const uint32_t bits = std::bit_cast<uint32_t>(value); |
128 | 27 | dump_uint32(bits, b, ix); |
129 | 27 | } |
130 | | |
131 | | template <class B> |
132 | | GLZ_ALWAYS_INLINE void dump_float64(double value, B& b, size_t& ix) noexcept(not vector_like<B>) |
133 | 33 | { |
134 | 33 | const uint64_t bits = std::bit_cast<uint64_t>(value); |
135 | 33 | dump_uint64(bits, b, ix); |
136 | 33 | } |
137 | | |
138 | | template <class It> |
139 | | GLZ_ALWAYS_INLINE bool read_uint8(is_context auto& ctx, It& it, const It& end, uint8_t& out) noexcept |
140 | 273 | { |
141 | 273 | if (it >= end) [[unlikely]] { |
142 | 12 | ctx.error = error_code::unexpected_end; |
143 | 12 | return false; |
144 | 12 | } |
145 | 261 | out = static_cast<uint8_t>(*it); |
146 | 261 | ++it; |
147 | 261 | return true; |
148 | 273 | } |
149 | | |
150 | | template <class It> |
151 | | GLZ_ALWAYS_INLINE bool read_uint16(is_context auto& ctx, It& it, const It& end, uint16_t& out) noexcept |
152 | 1.18k | { |
153 | 1.18k | if ((end - it) < 2) [[unlikely]] { |
154 | 81 | ctx.error = error_code::unexpected_end; |
155 | 81 | return false; |
156 | 81 | } |
157 | 1.10k | const auto b0 = static_cast<uint8_t>(it[0]); |
158 | 1.10k | const auto b1 = static_cast<uint8_t>(it[1]); |
159 | 1.10k | it += 2; |
160 | 1.10k | out = (uint16_t(b0) << 8) | uint16_t(b1); |
161 | 1.10k | return true; |
162 | 1.18k | } |
163 | | |
164 | | template <class It> |
165 | | GLZ_ALWAYS_INLINE bool read_uint32(is_context auto& ctx, It& it, const It& end, uint32_t& out) noexcept |
166 | 1.42k | { |
167 | 1.42k | if ((end - it) < 4) [[unlikely]] { |
168 | 188 | ctx.error = error_code::unexpected_end; |
169 | 188 | return false; |
170 | 188 | } |
171 | 1.23k | uint32_t value = 0; |
172 | 1.23k | value |= uint32_t(static_cast<uint8_t>(it[0])) << 24; |
173 | 1.23k | value |= uint32_t(static_cast<uint8_t>(it[1])) << 16; |
174 | 1.23k | value |= uint32_t(static_cast<uint8_t>(it[2])) << 8; |
175 | 1.23k | value |= uint32_t(static_cast<uint8_t>(it[3])); |
176 | 1.23k | it += 4; |
177 | 1.23k | out = value; |
178 | 1.23k | return true; |
179 | 1.42k | } |
180 | | |
181 | | template <class It> |
182 | | GLZ_ALWAYS_INLINE bool read_uint64(is_context auto& ctx, It& it, const It& end, uint64_t& out) noexcept |
183 | 1.49k | { |
184 | 1.49k | if ((end - it) < 8) [[unlikely]] { |
185 | 167 | ctx.error = error_code::unexpected_end; |
186 | 167 | return false; |
187 | 167 | } |
188 | 1.32k | uint64_t value = 0; |
189 | 1.32k | value |= uint64_t(static_cast<uint8_t>(it[0])) << 56; |
190 | 1.32k | value |= uint64_t(static_cast<uint8_t>(it[1])) << 48; |
191 | 1.32k | value |= uint64_t(static_cast<uint8_t>(it[2])) << 40; |
192 | 1.32k | value |= uint64_t(static_cast<uint8_t>(it[3])) << 32; |
193 | 1.32k | value |= uint64_t(static_cast<uint8_t>(it[4])) << 24; |
194 | 1.32k | value |= uint64_t(static_cast<uint8_t>(it[5])) << 16; |
195 | 1.32k | value |= uint64_t(static_cast<uint8_t>(it[6])) << 8; |
196 | 1.32k | value |= uint64_t(static_cast<uint8_t>(it[7])); |
197 | 1.32k | it += 8; |
198 | 1.32k | out = value; |
199 | 1.32k | return true; |
200 | 1.49k | } |
201 | | |
202 | | template <class It> |
203 | | GLZ_ALWAYS_INLINE bool read_ext_header(is_context auto& ctx, uint8_t tag, It& it, const It& end, size_t& length, |
204 | | int8_t& type) noexcept |
205 | | { |
206 | | switch (tag) { |
207 | | case fixext1: |
208 | | length = 1; |
209 | | break; |
210 | | case fixext2: |
211 | | length = 2; |
212 | | break; |
213 | | case fixext4: |
214 | | length = 4; |
215 | | break; |
216 | | case fixext8: |
217 | | length = 8; |
218 | | break; |
219 | | case fixext16: |
220 | | length = 16; |
221 | | break; |
222 | | case ext8: { |
223 | | uint8_t len8{}; |
224 | | if (!read_uint8(ctx, it, end, len8)) { |
225 | | return false; |
226 | | } |
227 | | length = len8; |
228 | | break; |
229 | | } |
230 | | case ext16: { |
231 | | uint16_t len16{}; |
232 | | if (!read_uint16(ctx, it, end, len16)) { |
233 | | return false; |
234 | | } |
235 | | length = len16; |
236 | | break; |
237 | | } |
238 | | case ext32: { |
239 | | uint32_t len32{}; |
240 | | if (!read_uint32(ctx, it, end, len32)) { |
241 | | return false; |
242 | | } |
243 | | length = len32; |
244 | | break; |
245 | | } |
246 | | default: |
247 | | ctx.error = error_code::syntax_error; |
248 | | return false; |
249 | | } |
250 | | |
251 | | uint8_t type_byte{}; |
252 | | if (!read_uint8(ctx, it, end, type_byte)) { |
253 | | return false; |
254 | | } |
255 | | type = static_cast<int8_t>(type_byte); |
256 | | |
257 | | if (static_cast<size_t>(end - it) < length) [[unlikely]] { |
258 | | ctx.error = error_code::unexpected_end; |
259 | | return false; |
260 | | } |
261 | | |
262 | | return true; |
263 | | } |
264 | | |
265 | | template <class It> |
266 | | GLZ_ALWAYS_INLINE bool read_float32(is_context auto& ctx, It& it, const It& end, float& out) noexcept |
267 | 74 | { |
268 | 74 | uint32_t bits{}; |
269 | 74 | if (!read_uint32(ctx, it, end, bits)) { |
270 | 8 | return false; |
271 | 8 | } |
272 | 66 | out = std::bit_cast<float>(bits); |
273 | 66 | return true; |
274 | 74 | } |
275 | | |
276 | | template <class It> |
277 | | GLZ_ALWAYS_INLINE bool read_float64(is_context auto& ctx, It& it, const It& end, double& out) noexcept |
278 | 86 | { |
279 | 86 | uint64_t bits{}; |
280 | 86 | if (!read_uint64(ctx, it, end, bits)) { |
281 | 16 | return false; |
282 | 16 | } |
283 | 70 | out = std::bit_cast<double>(bits); |
284 | 70 | return true; |
285 | 86 | } |
286 | | |
287 | | template <class It> |
288 | | GLZ_ALWAYS_INLINE bool skip_bytes(is_context auto& ctx, It& it, const It& end, size_t n) noexcept |
289 | | { |
290 | | if (static_cast<size_t>(end - it) < n) [[unlikely]] { |
291 | | ctx.error = error_code::unexpected_end; |
292 | | return false; |
293 | | } |
294 | | it += n; |
295 | | return true; |
296 | | } |
297 | | |
298 | | template <class It> |
299 | | GLZ_ALWAYS_INLINE bool read_str_length(is_context auto& ctx, uint8_t tag, It& it, const It& end, |
300 | | size_t& out) noexcept |
301 | 1.89k | { |
302 | 1.89k | if (is_fixstr(tag)) { |
303 | 616 | out = tag & 0x1F; |
304 | 616 | return true; |
305 | 616 | } |
306 | 1.27k | switch (tag) { |
307 | 136 | case str8: { |
308 | 136 | uint8_t len{}; |
309 | 136 | if (!read_uint8(ctx, it, end, len)) { |
310 | 1 | return false; |
311 | 1 | } |
312 | 135 | out = len; |
313 | 135 | return true; |
314 | 136 | } |
315 | 305 | case str16: { |
316 | 305 | uint16_t len{}; |
317 | 305 | if (!read_uint16(ctx, it, end, len)) { |
318 | 5 | return false; |
319 | 5 | } |
320 | 300 | out = len; |
321 | 300 | return true; |
322 | 305 | } |
323 | 220 | case str32: { |
324 | 220 | uint32_t len{}; |
325 | 220 | if (!read_uint32(ctx, it, end, len)) { |
326 | 17 | return false; |
327 | 17 | } |
328 | 203 | out = len; |
329 | 203 | return true; |
330 | 220 | } |
331 | 613 | default: |
332 | 613 | ctx.error = error_code::syntax_error; |
333 | 613 | return false; |
334 | 1.27k | } |
335 | 1.27k | } |
336 | | template <class Range> |
337 | | inline constexpr bool binary_range_v = contiguous_byte_range<std::remove_cvref_t<Range>>; |
338 | | |
339 | | struct ext |
340 | | { |
341 | | int8_t type{}; |
342 | | std::vector<std::byte> data{}; |
343 | | |
344 | | ext() = default; |
345 | | |
346 | 0 | ext(int8_t t, std::vector<std::byte> payload) : type(t), data(std::move(payload)) {} |
347 | | |
348 | 0 | ext(int8_t t, std::initializer_list<std::byte> payload) : type(t), data(payload) {} |
349 | | |
350 | 0 | [[nodiscard]] bool empty() const noexcept { return data.empty(); } |
351 | | |
352 | | bool operator==(const ext&) const = default; |
353 | | }; |
354 | | |
355 | | // MessagePack timestamp extension (type -1) |
356 | | // Supports the three timestamp formats defined in the MessagePack spec: |
357 | | // - Timestamp 32: fixext 4, seconds only (uint32) |
358 | | // - Timestamp 64: fixext 8, nanoseconds (30-bit) + seconds (34-bit) |
359 | | // - Timestamp 96: ext 8 with 12 bytes, nanoseconds (uint32) + seconds (int64) |
360 | | inline constexpr int8_t timestamp_type = -1; |
361 | | |
362 | | struct timestamp |
363 | | { |
364 | | int64_t seconds{}; |
365 | | uint32_t nanoseconds{}; |
366 | | |
367 | | timestamp() = default; |
368 | | |
369 | 0 | timestamp(int64_t sec, uint32_t nsec = 0) : seconds(sec), nanoseconds(nsec) {} |
370 | | |
371 | | bool operator==(const timestamp&) const = default; |
372 | | auto operator<=>(const timestamp&) const = default; |
373 | | }; |
374 | | |
375 | | template <class It> |
376 | | GLZ_ALWAYS_INLINE bool read_bin_length(is_context auto& ctx, uint8_t tag, It& it, const It& end, |
377 | | size_t& out) noexcept |
378 | | { |
379 | | switch (tag) { |
380 | | case bin8: { |
381 | | uint8_t len{}; |
382 | | if (!read_uint8(ctx, it, end, len)) { |
383 | | return false; |
384 | | } |
385 | | out = len; |
386 | | return true; |
387 | | } |
388 | | case bin16: { |
389 | | uint16_t len{}; |
390 | | if (!read_uint16(ctx, it, end, len)) { |
391 | | return false; |
392 | | } |
393 | | out = len; |
394 | | return true; |
395 | | } |
396 | | case bin32: { |
397 | | uint32_t len{}; |
398 | | if (!read_uint32(ctx, it, end, len)) { |
399 | | return false; |
400 | | } |
401 | | out = len; |
402 | | return true; |
403 | | } |
404 | | default: |
405 | | ctx.error = error_code::syntax_error; |
406 | | return false; |
407 | | } |
408 | | } |
409 | | |
410 | | template <class It> |
411 | | GLZ_ALWAYS_INLINE bool read_array_length(is_context auto& ctx, uint8_t tag, It& it, const It& end, |
412 | | size_t& out) noexcept |
413 | 5.86k | { |
414 | 5.86k | if (is_fixarray(tag)) { |
415 | 4.88k | out = tag & 0x0F; |
416 | 4.88k | return true; |
417 | 4.88k | } |
418 | 975 | switch (tag) { |
419 | 135 | case array16: { |
420 | 135 | uint16_t len{}; |
421 | 135 | if (!read_uint16(ctx, it, end, len)) { |
422 | 21 | return false; |
423 | 21 | } |
424 | 114 | out = len; |
425 | 114 | return true; |
426 | 135 | } |
427 | 99 | case array32: { |
428 | 99 | uint32_t len{}; |
429 | 99 | if (!read_uint32(ctx, it, end, len)) { |
430 | 35 | return false; |
431 | 35 | } |
432 | 64 | out = len; |
433 | 64 | return true; |
434 | 99 | } |
435 | 741 | default: |
436 | 741 | ctx.error = error_code::syntax_error; |
437 | 741 | return false; |
438 | 975 | } |
439 | 975 | } |
440 | | |
441 | | template <class It> |
442 | | GLZ_ALWAYS_INLINE bool read_map_length(is_context auto& ctx, uint8_t tag, It& it, const It& end, |
443 | | size_t& out) noexcept |
444 | | { |
445 | | if (is_fixmap(tag)) { |
446 | | out = tag & 0x0F; |
447 | | return true; |
448 | | } |
449 | | switch (tag) { |
450 | | case map16: { |
451 | | uint16_t len{}; |
452 | | if (!read_uint16(ctx, it, end, len)) { |
453 | | return false; |
454 | | } |
455 | | out = len; |
456 | | return true; |
457 | | } |
458 | | case map32: { |
459 | | uint32_t len{}; |
460 | | if (!read_uint32(ctx, it, end, len)) { |
461 | | return false; |
462 | | } |
463 | | out = len; |
464 | | return true; |
465 | | } |
466 | | default: |
467 | | ctx.error = error_code::syntax_error; |
468 | | return false; |
469 | | } |
470 | | } |
471 | | } |