Coverage Report

Created: 2026-08-31 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}