forked from DaveGamble/cJSON
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcJSON.h
More file actions
421 lines (347 loc) · 17.1 KB
/
Copy pathcJSON.h
File metadata and controls
421 lines (347 loc) · 17.1 KB
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
#pragma once
#if not defined(__WINDOWS__) and (defined(WIN32) or defined(WIN64) or defined(_MSC_VER) or defined(_WIN32))
#define __WINDOWS__
#endif
#ifdef __WINDOWS__
/*
When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
IMPORT_SYMBOLS - Define this if you want to dllimport symbol
For *nix builds that support visibility attribute, you can define similar behavior by
setting default visibility to hidden by adding
-fvisibility=hidden (for gcc)
or
-xldscope=hidden (for sun cc)
to CFLAGS
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
*/
#define CJSON_CDECL __cdecl
#define CJSON_STDCALL __stdcall
// export symbols by default, this is necessary for copy pasting the C++ and header file
#if not defined(CJSON_HIDE_SYMBOLS) and not defined(CJSON_IMPORT_SYMBOLS) and not defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_EXPORT_SYMBOLS
#endif
#ifdef CJSON_HIDE_SYMBOLS
#define CJSON_PUBLIC(type) type CJSON_STDCALL
#elifdef CJSON_EXPORT_SYMBOLS
#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
#elifdef CJSON_IMPORT_SYMBOLS
#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
#endif
#else // not __WINDOWS__
#define CJSON_CDECL
#define CJSON_STDCALL
#if (defined(__GNUC__) or defined(__SUNPRO_CC) or defined (__SUNPRO_C)) and defined(CJSON_API_VISIBILITY)
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
#else
#define CJSON_PUBLIC(type) type
#endif
#endif
#include <array>
#include <ranges>
#include <concepts>
#include <expected>
#include <optional>
#include <functional>
#include <string_view>
namespace cjson {
// project version:
constexpr auto VERSION_MAJOR = 1;
constexpr auto VERSION_MINOR = 7;
constexpr auto VERSION_PATCH = 19;
// CJSON Types:
constexpr auto INVALID = (0);
constexpr auto FALSE = (1 << 0);
constexpr auto TRUE = (1 << 1);
constexpr auto NONE = (1 << 2);
constexpr auto NUMBER = (1 << 3);
constexpr auto STRING = (1 << 4);
constexpr auto ARRAY = (1 << 5);
constexpr auto OBJECT = (1 << 6);
constexpr auto RAW = (1 << 7); // raw json
constexpr auto IS_REFERENCE = (1 << 8);
constexpr auto STRING_IS_CONST = (1 << 9);
struct Error {
const unsigned char* json;
size_t position;
};
enum class ParseError {
CORRUPTED_JSON,
PARAM_IS_NULL,
MEMORY_FAIL,
PARSE_FAIL,
};
enum class AddError {
INVALID_PARAM,
PARAM_IS_NULL,
SELF_IS_NULL,
ADD_FAILED,
};
enum class DetachError {
INVALID_PARAM,
SELF_IS_NULL,
};
enum class AssignError {
CIRCULAR_LIMIT,
ASSIGN_FAILED,
PARAM_IS_NULL,
COPY_IS_NULL,
SELF_IS_NULL,
OVERLAPPING,
WRONG_TYPE,
};
enum class InsertError {
CORRUPTED_JSON,
PARAM_IS_NULL,
INVALID_PARAM,
INSERT_FAILED,
};
enum class ReplaceError {
REPLACE_FAILED,
INVALID_PARAM,
SELF_IS_NULL,
};
enum class GetError {
SELF_IS_NULL,
};
struct CJSON;
using ParseResult = std::expected<std::reference_wrapper<CJSON>, ParseError>;
using AddResult = std::expected<std::reference_wrapper<CJSON>, AddError>;
using DetachResult = std::expected<std::reference_wrapper<CJSON>, DetachError>;
using AssignResult = std::expected<std::reference_wrapper<CJSON>, AssignError>;
using InsertResult = std::expected<std::reference_wrapper<CJSON>, InsertError>;
using ReplaceResult = std::expected<std::reference_wrapper<CJSON>, ReplaceError>;
using GetResult = std::expected<CJSON, GetError>;
// The CJSON structure
struct CJSONCore {
// next/prev allow you to walk array/object chains. Alternatively, use cjson::CJSON::get_array_size/cjson::CJSON::get_array_item/cjson::CJSON::get_object_item.
CJSONCore* next;
CJSONCore* prev;
// An array or object item will have a child pointer pointing to a chain of the items in the array/object.
CJSONCore* child;
// The type of the item, as above.
int type;
char* value_str; // The item's string, if type==cjson::STRING and type == cjson::RAW
int value_int; // writing to valueint is DEPRECATED, use cjson::set_number_value instead
double value_double; // The item's number, if type==cjson::NUMBER
// The item's name string, if this item is the child of, or is in the list of subitems of an object.
char* string;
};
struct NullJSONType {};
constexpr NullJSONType nulljson = NullJSONType{};
CJSON copy(CJSON const& other);
// The CJSON interface
class CJSON {
public:
CJSONCore* core;
struct ObjectConstruct {};
struct ArrayConstruct {};
struct MoveConstruct {};
struct RefConstruct {};
struct RawConstruct {};
struct ObjRefConstruct {};
struct ArrRefConstruct {};
// These calls create a CJSON item of the appropriate type.
CJSON_PUBLIC() CJSON();
CJSON_PUBLIC() CJSON(ObjectConstruct);
CJSON_PUBLIC() CJSON(ArrayConstruct);
CJSON_PUBLIC() CJSON(std::nullptr_t);
CJSON_PUBLIC() CJSON(NullJSONType);
CJSON_PUBLIC() CJSON(bool val);
CJSON_PUBLIC() CJSON(double num);
CJSON_PUBLIC() CJSON(char const* str);
CJSON_PUBLIC() CJSON(char const* str, RefConstruct);
CJSON_PUBLIC() CJSON(char const* str, RawConstruct);
CJSON_PUBLIC() CJSON(CJSONCore* value);
CJSON_PUBLIC() CJSON(CJSONCore* value, MoveConstruct);
CJSON_PUBLIC() CJSON(CJSON& other);
CJSON_PUBLIC() CJSON(CJSON const& other, ObjRefConstruct);
CJSON_PUBLIC() CJSON(CJSON const& other, ArrRefConstruct);
CJSON_PUBLIC() CJSON(CJSON&& other);
CJSON_PUBLIC() CJSON(int const* numbers, size_t count);
CJSON_PUBLIC() CJSON(float const* numbers, size_t count);
CJSON_PUBLIC() CJSON(double const* numbers, size_t count);
CJSON_PUBLIC() CJSON(char const* const* strings, size_t count);
CJSON_PUBLIC() ~CJSON();
// Memory Management: the caller is always responsible to free the results from all variants of cjson::CJSON::parse (with cjson::CJSON::del) and cjson::print (with stdlib free, cjson::CJSONHooks.free_fn, or cJSON_free as appropriate). The exception is cjson::print_preallocated, where the caller has full responsibility of the buffer.
// Supply a block of JSON, and this returns a CJSON object you can interrogate.
// ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed.
// If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr().
template <bool RequireNullTerminated = false>
CJSON_PUBLIC(ParseResult) parse(char const* value, char const** return_parse_end = nullptr, std::optional<std::reference_wrapper<Error>> error = std::nullopt);
template <bool RequireNullTerminated = false>
CJSON_PUBLIC(ParseResult) parse(char const* value, size_t buffer_length, char const** return_parse_end = nullptr, std::optional<std::reference_wrapper<Error>> error = std::nullopt);
// Delete a cJSON entity and all subentities.
CJSON_PUBLIC(void) del();
// Returns the number of items in an array (or object).
CJSON_PUBLIC(size_t) size() const;
// Retrieve item number "index" from array "array". Returns NONE if unsuccessful.
CJSON_PUBLIC(GetResult) operator[](size_t index) const;
// Get item "string" from object. Case insensitive.
CJSON_PUBLIC(GetResult) operator[](char const* const string) const;
CJSON_PUBLIC(GetResult) get_sensitive(char const* const string) const;
CJSON_PUBLIC(bool) contain(char const* string);
// Check item type and return its value
CJSON_PUBLIC(char*) get_string_value() const;
CJSON_PUBLIC(double) get_number_value() const;
// Remove/Detach items from Arrays/Objects.
CJSON_PUBLIC(DetachResult) detach(CJSON& const item);
CJSON_PUBLIC(DetachResult) detach(size_t which);
template <bool CaseSensitive = false>
CJSON_PUBLIC(DetachResult) detach(char const* string);
CJSON_PUBLIC(void) remove(size_t which);
template <bool CaseSensitive = false>
CJSON_PUBLIC(void) remove(char const* string);
// Update array items.
CJSON_PUBLIC(InsertResult) insert(size_t which, CJSON& new_item); // Shifts pre-existing items to the right.
CJSON_PUBLIC(ReplaceResult) replace(CJSON& item, CJSON& replacement);
CJSON_PUBLIC(ReplaceResult) replace(size_t which, CJSON& newitem);
template <bool CaseSensitive = false>
CJSON_PUBLIC(ReplaceResult) replace(char const* string, CJSON& newitem);
// These functions check the type of an item
CJSON_PUBLIC(bool) is_invalid() const;
CJSON_PUBLIC(bool) is_false() const;
CJSON_PUBLIC(bool) is_true() const;
CJSON_PUBLIC(bool) is_bool() const;
CJSON_PUBLIC(bool) is_null() const;
CJSON_PUBLIC(bool) is_number() const;
CJSON_PUBLIC(bool) is_string() const;
CJSON_PUBLIC(bool) is_array() const;
CJSON_PUBLIC(bool) is_object() const;
CJSON_PUBLIC(bool) is_raw() const;
/* Duplicate will create a new, identical CJSON item to the one you pass, in new memory that will
* need to be released. With recurse != 0, it will duplicate any children connected to the item.
* The item->next and ->prev pointers are always zero on return from Duplicate. */
CJSON_PUBLIC(bool) operator==(CJSON const& b) const;
template <bool CaseSensitive = false>
CJSON_PUBLIC(bool) compare(CJSON const& b) const;
// Append item to the specified array/object.
CJSON_PUBLIC(AddResult) operator+=(CJSON& item);
CJSON_PUBLIC(AddResult) add(CJSON& item);
CJSON_PUBLIC(AddResult) add(char const* string, CJSON& item);
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the CJSON object.
* WARNING: When this function was used, make sure to always check that (item->type & string_is_const) is zero before
* writing to `item->string` */
CJSON_PUBLIC(AddResult) add_cs(char const* string, CJSON& item);
// Helper functions for creating and adding items to an object at the same time.
CJSON_PUBLIC(AddResult) add_null(char const* name);
CJSON_PUBLIC(AddResult) add_bool(char const* name, bool boolean);
CJSON_PUBLIC(AddResult) add_number(char const* name, double number);
CJSON_PUBLIC(AddResult) add_string(char const* name, char const* string);
CJSON_PUBLIC(AddResult) add_raw(char const* name, char const* raw);
CJSON_PUBLIC(AddResult) add_object(char const* name);
CJSON_PUBLIC(AddResult) add_array(char const* name);
// When assigning an integer value, it needs to be propagated to value_double too.
constexpr CJSON& operator=(int number) { core->value_int = core->value_double = number; return *this; }
CJSON_PUBLIC(double) set_number_helper(double number);
constexpr CJSON& operator=(double number) { set_number_helper((double)number); return *this; }
// Change the value_str of a cjson::STRING object, only takes effect when type of object is cjson::STRING
CJSON_PUBLIC(AssignResult) operator=(char const* value_string);
CJSON_PUBLIC(AssignResult) operator=(CJSON& other);
CJSON_PUBLIC(AssignResult) operator=(CJSON&& other);
// If the object is not a boolean type this does nothing and returns cjson::INVALID else it returns the new type
constexpr CJSON& operator=(bool bool_value) { (core->type & (FALSE | TRUE)) ? (core->type = (core->type & ~(FALSE | TRUE)) | (bool_value ? TRUE : FALSE)) : INVALID; return *this; }
struct Iterator {
CJSONCore* current;
constexpr Iterator(CJSONCore* node) : current(node) {}
CJSON operator*() const { return CJSON(current); }
constexpr Iterator& operator++() { current = current->next; return *this; }
constexpr bool operator!=(const Iterator& other) const { return current != other.current; }
};
constexpr Iterator begin() const { return Iterator(core ? core->child : nullptr); }
constexpr Iterator end() const { return Iterator(nullptr); }
};
struct BaseAllocator {
[[nodiscard]] virtual BaseAllocator* clone() noexcept = 0;
[[nodiscard]] virtual void* allocate(size_t bytes) noexcept = 0;
[[nodiscard]] virtual void* reallocate(void* ptr, size_t bytes) noexcept = 0;
virtual void deallocate(void* ptr) noexcept = 0;
virtual ~BaseAllocator() = default;
};
template <class T>
struct IAllocator : BaseAllocator {
[[nodiscard]] virtual BaseAllocator* clone() noexcept override final {
return new T{};
}
};
template <typename T>
concept RAIICallable = requires(T t) { t(); };
template <typename F>
requires RAIICallable<F>
struct RAII {
F func;
constexpr RAII(F fn) noexcept : func(fn) {}
constexpr ~RAII() noexcept { func(); }
};
/* Limits how deeply nested arrays/objects can be before CJSON rejects to parse them.
* This is to prevent stack overflows. */
#ifndef CJSON_NESTING_LIMIT
constexpr auto NESTING_LIMIT = 1000;
#endif
/* Limits the length of circular references can be before CJSON rejects to parse them.
* This is to prevent stack overflows. */
#ifndef CJSON_CIRCULAR_LIMIT
constexpr auto CIRCULAR_LIMIT = 10000;
#endif
CJSON_PUBLIC(void) _internal_hooks(BaseAllocator* allocator);
// Supply malloc, realloc and free functions to CJSON
template <class T, typename... Args>
requires std::derived_from<T, BaseAllocator>
CJSON_PUBLIC(void) init_hooks(Args&&... args) { _internal_hooks(new T(std::forward<Args>(args)...)); }
CJSON_PUBLIC(void) reset_hooks() { _internal_hooks(nullptr); }
static constexpr size_t _internal_count_digits(int n) {
if (n == 0) return 1;
size_t count = 0;
while (n > 0) {
++count;
n /= 10;
}
return count;
}
template <size_t N>
struct InternalStaticString {
std::array<char, N> data{};
constexpr operator std::string_view() const { return std::string_view(data.data(), N - 1); }
};
template <int Major, int Minor, int Patch>
static constexpr auto _internal_make_version_string() {
constexpr size_t len1 = _internal_count_digits(Major);
constexpr size_t len2 = _internal_count_digits(Minor);
constexpr size_t len3 = _internal_count_digits(Patch);
constexpr size_t TotalSize = len1 + 1 + len2 + 1 + len3 + 1;
InternalStaticString<TotalSize> result{};
size_t pos = 0;
auto write_int = [&](int val, size_t len) {
size_t end_pos = pos + len;
for (size_t i : std::views::iota(end_pos, pos) | std::views::reverse) {
result.data[i - 1] = '0' + (val % 10);
val /= 10;
}
pos = end_pos;
};
write_int(Major, len1);
result.data[pos++] = '.';
write_int(Minor, len2);
result.data[pos++] = '.';
write_int(Patch, len3);
result.data[pos] = '\0';
return result;
}
constexpr CJSON_PUBLIC(std::string_view) version() {
return _internal_make_version_string<VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH>();
}
/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
* The input pointer json cannot point to a read-only address area, such as a string constant,
* but should point to a readable and writable address area. */
CJSON_PUBLIC(void) minify(char* json);
// Render a cJSON entity to text for transfer/storage.
CJSON_PUBLIC(char*) print(CJSON const& item);
// Render a cJSON entity to text for transfer/storage without any formatting.
CJSON_PUBLIC(char*) print_unformatted(CJSON const& item);
// Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted
CJSON_PUBLIC(char*) print_buffered(CJSON const& item, int prebuffer, bool fmt);
// Render a CJSON entity to text using a buffer already allocated in memory with given length. Returns true on success and false on failure.
// NOTE: CJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need
CJSON_PUBLIC(bool) print_preallocated(CJSON& item, char* buffer, int const length, bool const format);
}