-
Notifications
You must be signed in to change notification settings - Fork 22
/
mpack.h
3567 lines (3032 loc) · 111 KB
/
mpack.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* The MIT License (MIT)
*
* Copyright (c) 2015 Nicholas Fraser
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
/*
* This is the MPack 0.5.1 amalgamation package.
*
* http://github.com/ludocode/mpack
*/
#ifndef MPACK_H
#define MPACK_H 1
#define MPACK_AMALGAMATED 1
#include "mpack-config.h"
/* mpack-platform.h */
/**
* @file
*
* Abstracts all platform-specific code from MPack. This contains
* implementations of standard C functions when libc is not available,
* as well as wrappers to library functions.
*/
#ifndef MPACK_PLATFORM_H
#define MPACK_PLATFORM_H 1
#if defined(WIN32) && MPACK_INTERNAL
#define _CRT_SECURE_NO_WARNINGS 1
#endif
/* #include "mpack-config.h" */
/* For now, nothing in here should be seen by Doxygen. */
/** @cond */
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS 1
#endif
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS 1
#endif
#ifndef __STDC_CONSTANT_MACROS
#define __STDC_CONSTANT_MACROS 1
#endif
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <inttypes.h>
#include <limits.h>
#if MPACK_STDLIB
#include <string.h>
#include <stdlib.h>
#endif
#if MPACK_STDIO
#include <stdio.h>
#endif
#if MPACK_SETJMP
#include <setjmp.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define MPACK_UNUSED(var) ((void)(var))
#if MPACK_AMALGAMATED
#define MPACK_INTERNAL_STATIC static
#else
#define MPACK_INTERNAL_STATIC
#endif
#define MPACK_STRINGIFY_IMPL(arg) #arg
#define MPACK_STRINGIFY(arg) MPACK_STRINGIFY_IMPL(arg)
/* Some compiler-specific keywords and builtins */
#if defined(__GNUC__) || defined(__clang__)
#define MPACK_UNREACHABLE __builtin_unreachable()
#define MPACK_NORETURN(fn) fn __attribute__((noreturn))
#define MPACK_ALWAYS_INLINE __attribute__((always_inline)) static inline
#elif _MSC_VER
#define MPACK_UNREACHABLE __assume(0)
#define MPACK_NORETURN(fn) __declspec(noreturn) fn
#define MPACK_ALWAYS_INLINE __forceinline static
#else
#define MPACK_UNREACHABLE ((void)0)
#define MPACK_NORETURN(fn) fn
#define MPACK_ALWAYS_INLINE static inline
#endif
/*
* Here we define mpack_assert() and mpack_break(). They both work like a normal
* assertion function in debug mode, causing a trap or abort. However, on some platforms
* you can safely resume execution from mpack_break(), whereas mpack_assert() is
* always fatal.
*
* In release mode, mpack_assert() is converted to an assurance to the compiler
* that the expression cannot be false (via e.g. __assume() or __builtin_unreachable())
* to improve optimization where supported. There is thus no point in "safely" handling
* the case of this being false. Writing mpack_assert(0) rarely makes sense;
* the compiler will throw away any code after it. If at any time an mpack_assert()
* is not true, the behaviour is undefined. This also means the expression is
* evaluated even in release.
*
* mpack_break() on the other hand is compiled to nothing in release. It is
* used in situations where we want to highlight a programming error as early as
* possible (in the debugger), but we still handle the situation safely if it
* happens in release to avoid producing incorrect results (such as in
* MPACK_WRITE_TRACKING.) It does not take an expression to test because it
* belongs in a safe-handling block after its failing condition has been tested.
*
* If stdio is available, we can add a format string describing the error, and
* on some compilers we can declare it noreturn to get correct results from static
* analysis tools. Note that the format string and arguments are not evaluated unless
* the assertion is hit.
*
* Note that any arguments to mpack_assert() beyond the first are only evaluated
* if the expression is false (and are never evaluated in release.)
*
* mpack_assert_fail() and mpack_break_hit() are defined separately
* because assert is noreturn and break isn't. This distinction is very
* important for static analysis tools to give correct results.
*/
#if MPACK_DEBUG
MPACK_NORETURN(void mpack_assert_fail(const char* message));
#if MPACK_STDIO
MPACK_NORETURN(void mpack_assert_fail_format(const char* format, ...));
#define mpack_assert_fail_at(line, file, expr, ...) \
mpack_assert_fail_format("mpack assertion failed at " file ":" #line "\n" expr "\n" __VA_ARGS__)
#else
#define mpack_assert_fail_at(line, file, ...) \
mpack_assert_fail("mpack assertion failed at " file ":" #line )
#endif
#define mpack_assert_fail_pos(line, file, expr, ...) mpack_assert_fail_at(line, file, expr, __VA_ARGS__)
#define mpack_assert(expr, ...) ((!(expr)) ? mpack_assert_fail_pos(__LINE__, __FILE__, #expr, __VA_ARGS__) : (void)0)
void mpack_break_hit(const char* message);
#if MPACK_STDIO
void mpack_break_hit_format(const char* format, ...);
#define mpack_break_hit_at(line, file, ...) \
mpack_break_hit_format("mpack breakpoint hit at " file ":" #line "\n" __VA_ARGS__)
#else
#define mpack_break_hit_at(line, file, ...) \
mpack_break_hit("mpack breakpoint hit at " file ":" #line )
#endif
#define mpack_break_hit_pos(line, file, ...) mpack_break_hit_at(line, file, __VA_ARGS__)
#define mpack_break(...) mpack_break_hit_pos(__LINE__, __FILE__, __VA_ARGS__)
#else
#define mpack_assert(expr, ...) ((!(expr)) ? MPACK_UNREACHABLE, (void)0 : (void)0)
#define mpack_break(...) ((void)0)
#endif
#if MPACK_STDLIB
#define mpack_memset memset
#define mpack_memcpy memcpy
#define mpack_memmove memmove
#define mpack_memcmp memcmp
#define mpack_strlen strlen
#else
void* mpack_memset(void *s, int c, size_t n);
void* mpack_memcpy(void *s1, const void *s2, size_t n);
void* mpack_memmove(void *s1, const void *s2, size_t n);
int mpack_memcmp(const void* s1, const void* s2, size_t n);
size_t mpack_strlen(const char *s);
#endif
/* Debug logging */
#if 0
#define mpack_log(...) printf(__VA_ARGS__);
#else
#define mpack_log(...) ((void)0)
#endif
/* Make sure our configuration makes sense */
#if defined(MPACK_MALLOC) && !defined(MPACK_FREE)
#error "MPACK_MALLOC requires MPACK_FREE."
#endif
#if !defined(MPACK_MALLOC) && defined(MPACK_FREE)
#error "MPACK_FREE requires MPACK_MALLOC."
#endif
#if MPACK_READ_TRACKING && (!defined(MPACK_READER) || !MPACK_READER)
#error "MPACK_READ_TRACKING requires MPACK_READER."
#endif
#if MPACK_WRITE_TRACKING && (!defined(MPACK_WRITER) || !MPACK_WRITER)
#error "MPACK_WRITE_TRACKING requires MPACK_WRITER."
#endif
#ifndef MPACK_MALLOC
#if MPACK_STDIO
#error "MPACK_STDIO requires preprocessor definitions for MPACK_MALLOC and MPACK_FREE."
#endif
#if MPACK_READ_TRACKING
#error "MPACK_READ_TRACKING requires preprocessor definitions for MPACK_MALLOC and MPACK_FREE."
#endif
#if MPACK_WRITE_TRACKING
#error "MPACK_WRITE_TRACKING requires preprocessor definitions for MPACK_MALLOC and MPACK_FREE."
#endif
#endif
/* Implement realloc if unavailable */
#ifdef MPACK_MALLOC
#ifdef MPACK_REALLOC
static inline void* mpack_realloc(void* old_ptr, size_t used_size, size_t new_size) {
MPACK_UNUSED(used_size);
return MPACK_REALLOC(old_ptr, new_size);
}
#else
void* mpack_realloc(void* old_ptr, size_t used_size, size_t new_size);
#endif
#endif
/**
* @}
*/
#ifdef __cplusplus
}
#endif
/** @endcond */
#endif
/* mpack-common.h */
/**
* @file
*
* Defines types and functions shared by the MPack reader and writer.
*/
#ifndef MPACK_COMMON_H
#define MPACK_COMMON_H 1
/* #include "mpack-platform.h" */
/** @cond */
#ifndef MPACK_STACK_SIZE
#define MPACK_STACK_SIZE 4096
#endif
/** @endcond */
/* Version information */
#define MPACK_VERSION_MAJOR 0 /**< The major version number of MPack. */
#define MPACK_VERSION_MINOR 5 /**< The minor version number of MPack. */
#define MPACK_VERSION_PATCH 1 /**< The patch version number of MPack. */
/** A number containing the version number of MPack for comparison purposes. */
#define MPACK_VERSION ((MPACK_VERSION_MAJOR * 10000) + \
(MPACK_VERSION_MINOR * 100) + MPACK_VERSION_PATCH)
/** A macro to test for a minimum version of MPack. */
#define MPACK_VERSION_AT_LEAST(major, minor, patch) \
(MPACK_VERSION >= (((major) * 10000) + ((minor) * 100) + (patch)))
/** @cond */
#if (MPACK_VERSION_PATCH > 0)
#define MPACK_VERSION_STRING_BASE \
MPACK_STRINGIFY(MPACK_VERSION_MAJOR) "." \
MPACK_STRINGIFY(MPACK_VERSION_MINOR) "." \
MPACK_STRINGIFY(MPACK_VERSION_PATCH)
#else
#define MPACK_VERSION_STRING_BASE \
MPACK_STRINGIFY(MPACK_VERSION_MAJOR) "." \
MPACK_STRINGIFY(MPACK_VERSION_MINOR)
#endif
/** @endcond */
/**
* @def MPACK_VERSION_STRING
* @hideinitializer
*
* A string containing the MPack version.
*/
#if MPACK_AMALGAMATED
#define MPACK_VERSION_STRING MPACK_VERSION_STRING_BASE
#else
#define MPACK_VERSION_STRING MPACK_VERSION_STRING_BASE "dev"
#endif
/**
* @def MPACK_LIBRARY_STRING
* @hideinitializer
*
* A string describing MPack, containing the library name, version and debug mode.
*/
#if MPACK_DEBUG
#define MPACK_LIBRARY_STRING "MPack " MPACK_VERSION_STRING "-debug"
#else
#define MPACK_LIBRARY_STRING "MPack " MPACK_VERSION_STRING
#endif
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup common Common Elements
*
* Contains types and functions shared by both the encoding and decoding
* portions of MPack.
*
* @{
*/
/**
* Error states for MPack objects.
*
* When a reader, writer, or tree is in an error state, all subsequent calls
* are ignored and their return values are nil/zero. You should check whether
* the source is in an error state before using such values.
*/
typedef enum mpack_error_t {
mpack_ok = 0, /**< No error. */
mpack_error_io = 2, /**< The reader or writer failed to fill or flush, or some other file or socket error occurred. */
mpack_error_invalid, /**< The data read is not valid MessagePack. */
mpack_error_type, /**< The type or value range did not match what was expected by the caller. */
mpack_error_too_big, /**< A read or write was bigger than the maximum size allowed for that operation. */
mpack_error_memory, /**< An allocation failure occurred. */
mpack_error_bug, /**< The MPack API was used incorrectly. (This will always assert in debug mode.) */
mpack_error_data, /**< The contained data is not valid. */
} mpack_error_t;
/**
* Converts an mpack error to a string. This function returns an empty
* string when MPACK_DEBUG is not set.
*/
const char* mpack_error_to_string(mpack_error_t error);
/**
* Defines the type of a MessagePack tag.
*/
typedef enum mpack_type_t {
mpack_type_nil = 1, /**< A null value. */
mpack_type_bool, /**< A boolean (true or false.) */
mpack_type_float, /**< A 32-bit IEEE 754 floating point number. */
mpack_type_double, /**< A 64-bit IEEE 754 floating point number. */
mpack_type_int, /**< A 64-bit signed integer. */
mpack_type_uint, /**< A 64-bit unsigned integer. */
mpack_type_str, /**< A string. */
mpack_type_bin, /**< A chunk of binary data. */
mpack_type_ext, /**< A typed MessagePack extension object containing a chunk of binary data. */
mpack_type_array, /**< An array of MessagePack objects. */
mpack_type_map, /**< An ordered map of key/value pairs of MessagePack objects. */
} mpack_type_t;
/**
* Converts an mpack type to a string. This function returns an empty
* string when MPACK_DEBUG is not set.
*/
const char* mpack_type_to_string(mpack_type_t type);
/**
* An MPack tag is a MessagePack object header. It is a variant type representing
* any kind of object, and includes the value of that object when it is not a
* compound type (i.e. boolean, integer, float.)
*
* If the type is compound (str, bin, ext, array or map), the embedded data is
* stored separately.
*/
typedef struct mpack_tag_t {
mpack_type_t type; /**< The type of value. */
int8_t exttype; /**< The extension type if the type is @ref mpack_type_ext. */
/** The value for non-compound types. */
union
{
bool b; /**< The value if the type is bool. */
float f; /**< The value if the type is float. */
double d; /**< The value if the type is double. */
int64_t i; /**< The value if the type is signed int. */
uint64_t u; /**< The value if the type is unsigned int. */
uint32_t l; /**< The number of bytes if the type is str, bin or ext. */
/** The element count if the type is an array, or the number of
key/value pairs if the type is map. */
uint32_t n;
} v;
} mpack_tag_t;
/** Generates a nil tag. */
static inline mpack_tag_t mpack_tag_nil(void) {
mpack_tag_t ret;
mpack_memset(&ret, 0, sizeof(ret));
ret.type = mpack_type_nil;
return ret;
}
/** Generates a signed int tag. */
static inline mpack_tag_t mpack_tag_int(int64_t value) {
mpack_tag_t ret;
mpack_memset(&ret, 0, sizeof(ret));
ret.type = mpack_type_int;
ret.v.i = value;
return ret;
}
/** Generates an unsigned int tag. */
static inline mpack_tag_t mpack_tag_uint(uint64_t value) {
mpack_tag_t ret;
mpack_memset(&ret, 0, sizeof(ret));
ret.type = mpack_type_uint;
ret.v.u = value;
return ret;
}
/** Generates a bool tag. */
static inline mpack_tag_t mpack_tag_bool(bool value) {
mpack_tag_t ret;
mpack_memset(&ret, 0, sizeof(ret));
ret.type = mpack_type_bool;
ret.v.b = value;
return ret;
}
/** Generates a float tag. */
static inline mpack_tag_t mpack_tag_float(float value) {
mpack_tag_t ret;
mpack_memset(&ret, 0, sizeof(ret));
ret.type = mpack_type_float;
ret.v.f = value;
return ret;
}
/** Generates a double tag. */
static inline mpack_tag_t mpack_tag_double(double value) {
mpack_tag_t ret;
mpack_memset(&ret, 0, sizeof(ret));
ret.type = mpack_type_double;
ret.v.d = value;
return ret;
}
/**
* Compares two tags with an arbitrary fixed ordering. Returns 0 if the tags are
* equal, a negative integer if left comes before right, or a positive integer
* otherwise.
*
* See mpack_tag_equal() for information on when tags are considered
* to be equal.
*
* The ordering is not guaranteed to be preserved across mpack versions; do not
* rely on it in serialized data.
*/
int mpack_tag_cmp(mpack_tag_t left, mpack_tag_t right);
/**
* Compares two tags for equality. Tags are considered equal if the types are compatible
* and the values (for non-compound types) are equal.
*
* The field width of variable-width fields is ignored (and in fact is not stored
* in a tag), and positive numbers in signed integers are considered equal to their
* unsigned counterparts. So for example the value 1 stored as a positive fixint
* is equal to the value 1 stored in a 64-bit unsigned integer field.
*
* The "extension type" of an extension object is considered part of the value
* and much match exactly.
*
* Floating point numbers are compared bit-for-bit, not using the language's operator==.
*/
static inline bool mpack_tag_equal(mpack_tag_t left, mpack_tag_t right) {
return mpack_tag_cmp(left, right) == 0;
}
/**
* @}
*/
/* Helpers for fetching an arbitrarily sized int from a memory
* location, regardless of endianness or alignment. */
/** @cond */
MPACK_ALWAYS_INLINE uint8_t mpack_load_native_u8(const char* p) {
return (uint8_t)p[0];
}
MPACK_ALWAYS_INLINE uint16_t mpack_load_native_u16(const char* p) {
return (uint16_t)((((uint16_t)(uint8_t)p[0]) << 8) |
((uint16_t)(uint8_t)p[1]));
}
MPACK_ALWAYS_INLINE uint32_t mpack_load_native_u32(const char* p) {
return (((uint32_t)(uint8_t)p[0]) << 24) |
(((uint32_t)(uint8_t)p[1]) << 16) |
(((uint32_t)(uint8_t)p[2]) << 8) |
((uint32_t)(uint8_t)p[3]);
}
MPACK_ALWAYS_INLINE uint64_t mpack_load_native_u64(const char* p) {
return (((uint64_t)(uint8_t)p[0]) << 56) |
(((uint64_t)(uint8_t)p[1]) << 48) |
(((uint64_t)(uint8_t)p[2]) << 40) |
(((uint64_t)(uint8_t)p[3]) << 32) |
(((uint64_t)(uint8_t)p[4]) << 24) |
(((uint64_t)(uint8_t)p[5]) << 16) |
(((uint64_t)(uint8_t)p[6]) << 8) |
((uint64_t)(uint8_t)p[7]);
}
/** @endcond */
#if MPACK_READ_TRACKING || MPACK_WRITE_TRACKING
/* Tracks the write state of compound elements (maps, arrays, */
/* strings, binary blobs and extension types) */
/** @cond */
typedef struct mpack_track_element_t {
mpack_type_t type;
uint64_t left; // we need 64-bit because (2 * INT32_MAX) elements can be stored in a map
} mpack_track_element_t;
typedef struct mpack_track_t {
size_t count;
size_t capacity;
mpack_track_element_t* elements;
} mpack_track_t;
#if MPACK_INTERNAL
MPACK_INTERNAL_STATIC mpack_error_t mpack_track_init(mpack_track_t* track);
MPACK_INTERNAL_STATIC mpack_error_t mpack_track_grow(mpack_track_t* track);
// These look like some overly large inline functions, but really
// they are mostly asserts. They boil down to just a few checks
// and assignments.
static inline mpack_error_t mpack_track_push(mpack_track_t* track, mpack_type_t type, uint64_t count) {
mpack_assert(track->elements, "null track elements!");
// maps have twice the number of elements (key/value pairs)
if (type == mpack_type_map)
count *= 2;
// grow if needed
if (track->count == track->capacity) {
mpack_error_t error = mpack_track_grow(track);
if (error != mpack_ok)
return error;
}
// insert new track
track->elements[track->count].type = type;
track->elements[track->count].left = count;
++track->count;
return mpack_ok;
}
static inline mpack_error_t mpack_track_pop(mpack_track_t* track, mpack_type_t type) {
mpack_assert(track->elements, "null track elements!");
if (track->count == 0) {
mpack_break("attempting to close a %s but nothing was opened!", mpack_type_to_string(type));
return mpack_error_bug;
}
mpack_track_element_t* element = &track->elements[track->count - 1];
if (element->type != type) {
mpack_break("attempting to close a %s but the open element is a %s!",
mpack_type_to_string(type), mpack_type_to_string(element->type));
return mpack_error_bug;
}
if (element->left != 0) {
mpack_break("attempting to close a %s but there are %" PRIu64 " %s left",
mpack_type_to_string(type), element->left,
(type == mpack_type_map || type == mpack_type_array) ? "elements" : "bytes");
return mpack_error_bug;
}
--track->count;
return mpack_ok;
}
static inline mpack_error_t mpack_track_element(mpack_track_t* track, bool read) {
MPACK_UNUSED(read);
mpack_assert(track->elements, "null track elements!");
// if there are no open elements, that's fine, we can read elements at will
if (track->count == 0)
return mpack_ok;
mpack_track_element_t* element = &track->elements[track->count - 1];
if (element->type != mpack_type_map && element->type != mpack_type_array) {
mpack_break("elements cannot be %s within an %s", read ? "read" : "written",
mpack_type_to_string(element->type));
return mpack_error_bug;
}
if (element->left == 0) {
mpack_break("too many elements %s for %s", read ? "read" : "written",
mpack_type_to_string(element->type));
return mpack_error_bug;
}
--element->left;
return mpack_ok;
}
static inline mpack_error_t mpack_track_bytes(mpack_track_t* track, bool read, uint64_t count) {
MPACK_UNUSED(read);
mpack_assert(track->elements, "null track elements!");
if (track->count == 0) {
mpack_break("bytes cannot be %s with no open bin, str or ext", read ? "read" : "written");
return mpack_error_bug;
}
mpack_track_element_t* element = &track->elements[track->count - 1];
if (element->type == mpack_type_map || element->type == mpack_type_array) {
mpack_break("bytes cannot be %s within an %s", read ? "read" : "written",
mpack_type_to_string(element->type));
return mpack_error_bug;
}
if (element->left < count) {
mpack_break("too many bytes %s for %s", read ? "read" : "written",
mpack_type_to_string(element->type));
return mpack_error_bug;
}
element->left -= count;
return mpack_ok;
}
static inline mpack_error_t mpack_track_check_empty(mpack_track_t* track) {
if (track->count != 0) {
mpack_assert(0, "unclosed %s", mpack_type_to_string(track->elements[0].type));
return mpack_error_bug;
}
return mpack_ok;
}
static inline mpack_error_t mpack_track_destroy(mpack_track_t* track, bool cancel) {
mpack_error_t error = cancel ? mpack_ok : mpack_track_check_empty(track);
MPACK_FREE(track->elements);
track->elements = NULL;
return error;
}
#endif
/** @endcond */
#endif
#if MPACK_INTERNAL
/* The below code is from Bjoern Hoehrmann's Flexible and Economical */
/* UTF-8 decoder, modified to make it static and add the mpack prefix. */
/* Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de> */
/* See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. */
#define MPACK_UTF8_ACCEPT 0
#define MPACK_UTF8_REJECT 12
static const uint8_t mpack_utf8d[] = {
/* The first part of the table maps bytes to character classes that */
/* to reduce the size of the transition table and create bitmasks. */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
/* The second part is a transition table that maps a combination */
/* of a state of the automaton and a character class to a state. */
0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
12,36,12,12,12,12,12,12,12,12,12,12,
};
static inline
uint32_t mpack_utf8_decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
uint32_t type = mpack_utf8d[byte];
*codep = (*state != MPACK_UTF8_ACCEPT) ?
(byte & 0x3fu) | (*codep << 6) :
(0xff >> type) & (byte);
*state = mpack_utf8d[256 + *state + type];
return *state;
}
#endif
#ifdef __cplusplus
}
#endif
#endif
/* mpack-writer.h */
/**
* @file
*
* Declares the MPack Writer.
*/
#ifndef MPACK_WRITER_H
#define MPACK_WRITER_H 1
/* #include "mpack-common.h" */
#if MPACK_WRITER
#ifdef __cplusplus
extern "C" {
#endif
#if MPACK_WRITE_TRACKING
struct mpack_track_t;
#endif
/**
* @defgroup writer Write API
*
* The MPack Write API encodes structured data of a fixed (hardcoded) schema to MessagePack.
*
* @{
*/
/**
* A buffered MessagePack encoder.
*
* The encoder wraps an existing buffer and, optionally, a flush function.
* This allows efficiently encoding to an in-memory buffer or to a stream.
*
* All write operations are synchronous; they will block until the
* data is fully written, or an error occurs.
*/
typedef struct mpack_writer_t mpack_writer_t;
/**
* The mpack writer's flush function to flush the buffer to the output stream.
* It should flag an appropriate error on the writer if flushing fails.
* Keep in mind that flagging an error may longjmp.
*
* The specified context for callbacks is at writer->context.
*/
typedef void (*mpack_flush_t)(mpack_writer_t* writer, const char* buffer, size_t count);
/**
* A teardown function to be called when the writer is destroyed.
*/
typedef void (*mpack_writer_teardown_t)(mpack_writer_t* writer);
struct mpack_writer_t {
mpack_flush_t flush; /* Function to write bytes to the output stream */
mpack_writer_teardown_t teardown; /* Function to teardown the context on destroy */
void* context; /* Context for writer callbacks */
char* buffer; /* Byte buffer */
size_t size; /* Size of the buffer */
size_t used; /* How many bytes have been written into the buffer */
mpack_error_t error; /* Error state */
#if MPACK_SETJMP
/* Optional jump target in case of error (pointer because it's
* very large and may be unused) */
jmp_buf* jump_env;
#endif
#if MPACK_WRITE_TRACKING
mpack_track_t track; /* Stack of map/array/str/bin/ext writes */
#endif
};
/**
* @name Core Writer Functions
* @{
*/
/**
* Initializes an mpack writer with the given buffer. The writer
* does not assume ownership of the buffer.
*
* Trying to write past the end of the buffer will result in mpack_error_io unless
* a flush function is set with mpack_writer_set_flush(). To use the data without
* flushing, call mpack_writer_buffer_used() to determine the number of bytes
* written.
*
* @param writer The MPack writer.
* @param buffer The buffer into which to write mpack data.
* @param size The size of the buffer.
*/
void mpack_writer_init(mpack_writer_t* writer, char* buffer, size_t size);
#ifdef MPACK_MALLOC
/**
* Initializes an mpack writer using a growable buffer.
*
* The data is placed in the given data pointer if and when the writer
* is destroyed without error. The data should be freed with MPACK_FREE().
* The data pointer is NULL during writing, and will remain NULL
* if an error occurs.
*
* mpack_error_memory is raised if the buffer fails to grow.
*
* @param writer The MPack writer.
* @param data Where to place the allocated data.
* @param size Where to write the size of the data.
*/
void mpack_writer_init_growable(mpack_writer_t* writer, char** data, size_t* size);
#endif
/**
* Initializes an mpack writer directly into an error state. Use this if you
* are writing a wrapper to mpack_writer_init() which can fail its setup.
*/
void mpack_writer_init_error(mpack_writer_t* writer, mpack_error_t error);
#if MPACK_STDIO
/**
* Initializes an mpack writer that writes to a file.
*/
void mpack_writer_init_file(mpack_writer_t* writer, const char* filename);
#endif
/**
* @def mpack_writer_init_stack(writer, flush, context)
* @hideinitializer
*
* Initializes an mpack writer using stack space.
*/
#define mpack_writer_init_stack_line_ex(line, writer) \
char mpack_buf_##line[MPACK_STACK_SIZE]; \
mpack_writer_init(writer, mpack_buf_##line, sizeof(mpack_buf_##line))
#define mpack_writer_init_stack_line(line, writer) \
mpack_writer_init_stack_line_ex(line, writer)
#define mpack_writer_init_stack(writer) \
mpack_writer_init_stack_line(__LINE__, (writer))
#if MPACK_SETJMP
/**
* @hideinitializer
*
* Registers a jump target in case of error.
*
* If the writer is in an error state, 1 is returned when this is called. Otherwise
* 0 is returned when this is called, and when the first error occurs, control flow
* will jump to the point where this was called, resuming as though it returned 1.
* This ensures an error handling block runs exactly once in case of error.
*
* A writer that jumps still needs to be destroyed. You must call
* mpack_writer_destroy() in your jump handler after getting the final error state.
*
* The argument may be evaluated multiple times.
*
* @returns 0 if the writer is not in an error state; 1 if and when an error occurs.
* @see mpack_writer_destroy()
*/
#define MPACK_WRITER_SETJMP(writer) \
(mpack_assert((writer)->jump_env == NULL, "already have a jump set!"), \
((writer)->error != mpack_ok) ? 1 : \
!((writer)->jump_env = (jmp_buf*)MPACK_MALLOC(sizeof(jmp_buf))) ? \
((writer)->error = mpack_error_memory, 1) : \
(setjmp(*(writer)->jump_env)))
/**
* Clears a jump target. Subsequent write errors will not cause the writer to
* jump.
*/
static inline void mpack_writer_clearjmp(mpack_writer_t* writer) {
if (writer->jump_env)
MPACK_FREE(writer->jump_env);
writer->jump_env = NULL;
}
#endif
/**
* Cleans up the mpack writer, flushing any buffered bytes to the
* underlying stream, if any. Returns the final error state of the
* writer in case an error occurred flushing. Causes an assert if
* there are any unclosed compound types in tracking mode.
*
* Note that if a jump handler is set, a writer may jump during destroy if it
* fails to flush any remaining data. In this case the writer will not be fully
* destroyed; you can still get the error state, and you must call destroy as
* usual in the jump handler.
*/
mpack_error_t mpack_writer_destroy(mpack_writer_t* writer);
/**
* Cleans up the mpack writer, discarding any open writes and unflushed data.
*
* Use this to cancel writing in the middle of writing a document (for example
* in case an error occurred.) This should be used instead of mpack_writer_destroy()
* because the former will assert in tracking mode if there are any unclosed
* compound types.
*/
void mpack_writer_destroy_cancel(mpack_writer_t* writer);
/**
* Sets the custom pointer to pass to the writer callbacks, such as flush
* or teardown.
*
* @param writer The MPack writer.
* @param context User data to pass to the writer callbacks.
*/
static inline void mpack_writer_set_context(mpack_writer_t* writer, void* context) {
writer->context = context;
}
/**
* Sets the flush function to write out the data when the buffer is full.
*
* If no flush function is used, trying to write past the end of the
* buffer will result in mpack_error_io.
*
* This should normally be used with mpack_writer_set_context() to register
* a custom pointer to pass to the flush function.
*
* @param writer The MPack writer.
* @param flush The function to write out data from the buffer.
*/
static inline void mpack_writer_set_flush(mpack_writer_t* writer, mpack_flush_t flush) {
mpack_assert(writer->size != 0, "cannot use flush function without a writeable buffer!");
writer->flush = flush;
}
/**
* Sets the teardown function to call when the writer is destroyed.
*
* This should normally be used with mpack_writer_set_context() to register
* a custom pointer to pass to the teardown function.
*