isomorfeus-iodine 0.7.49 → 0.7.50

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +1 -1
  3. data/CHANGELOG.md +17 -3
  4. data/Rakefile +1 -9
  5. data/examples/etag.ru +16 -0
  6. data/ext/{iodine → iodine_ext}/extconf.rb +1 -1
  7. data/ext/{iodine → iodine_ext}/fio.c +0 -0
  8. data/ext/{iodine → iodine_ext}/fio.h +0 -0
  9. data/ext/{iodine → iodine_ext}/fio_cli.c +0 -0
  10. data/ext/{iodine → iodine_ext}/fio_cli.h +189 -189
  11. data/ext/{iodine → iodine_ext}/fio_json_parser.h +687 -687
  12. data/ext/{iodine → iodine_ext}/fio_siphash.c +157 -157
  13. data/ext/{iodine → iodine_ext}/fio_siphash.h +37 -37
  14. data/ext/{iodine → iodine_ext}/fio_tls.h +129 -129
  15. data/ext/{iodine → iodine_ext}/fio_tls_missing.c +0 -0
  16. data/ext/{iodine → iodine_ext}/fio_tls_openssl.c +0 -0
  17. data/ext/{iodine → iodine_ext}/fio_tmpfile.h +0 -0
  18. data/ext/{iodine → iodine_ext}/fiobj.h +44 -44
  19. data/ext/{iodine → iodine_ext}/fiobj4fio.h +21 -21
  20. data/ext/{iodine → iodine_ext}/fiobj_ary.c +333 -333
  21. data/ext/{iodine → iodine_ext}/fiobj_ary.h +139 -139
  22. data/ext/{iodine → iodine_ext}/fiobj_data.c +0 -0
  23. data/ext/{iodine → iodine_ext}/fiobj_data.h +0 -0
  24. data/ext/{iodine → iodine_ext}/fiobj_hash.c +0 -0
  25. data/ext/{iodine → iodine_ext}/fiobj_hash.h +176 -176
  26. data/ext/{iodine → iodine_ext}/fiobj_json.c +622 -622
  27. data/ext/{iodine → iodine_ext}/fiobj_json.h +68 -68
  28. data/ext/{iodine → iodine_ext}/fiobj_mem.h +71 -71
  29. data/ext/{iodine → iodine_ext}/fiobj_mustache.c +0 -0
  30. data/ext/{iodine → iodine_ext}/fiobj_mustache.h +62 -62
  31. data/ext/{iodine → iodine_ext}/fiobj_numbers.c +0 -0
  32. data/ext/{iodine → iodine_ext}/fiobj_numbers.h +127 -127
  33. data/ext/{iodine → iodine_ext}/fiobj_str.c +0 -0
  34. data/ext/{iodine → iodine_ext}/fiobj_str.h +172 -172
  35. data/ext/{iodine → iodine_ext}/fiobject.c +0 -0
  36. data/ext/{iodine → iodine_ext}/fiobject.h +0 -0
  37. data/ext/{iodine → iodine_ext}/hpack.h +1923 -1923
  38. data/ext/{iodine → iodine_ext}/http.c +14 -27
  39. data/ext/{iodine → iodine_ext}/http.h +1002 -1002
  40. data/ext/{iodine → iodine_ext}/http1.c +0 -0
  41. data/ext/{iodine → iodine_ext}/http1.h +29 -29
  42. data/ext/{iodine → iodine_ext}/http1_parser.h +0 -0
  43. data/ext/{iodine → iodine_ext}/http_internal.c +0 -0
  44. data/ext/{iodine → iodine_ext}/http_internal.h +0 -0
  45. data/ext/{iodine → iodine_ext}/http_mime_parser.h +350 -350
  46. data/ext/{iodine → iodine_ext}/iodine.c +1 -1
  47. data/ext/{iodine → iodine_ext}/iodine.h +0 -0
  48. data/ext/{iodine → iodine_ext}/iodine_caller.c +0 -0
  49. data/ext/{iodine → iodine_ext}/iodine_caller.h +0 -0
  50. data/ext/{iodine → iodine_ext}/iodine_connection.c +0 -0
  51. data/ext/{iodine → iodine_ext}/iodine_connection.h +55 -55
  52. data/ext/{iodine → iodine_ext}/iodine_defer.c +0 -0
  53. data/ext/{iodine → iodine_ext}/iodine_defer.h +6 -6
  54. data/ext/{iodine → iodine_ext}/iodine_fiobj2rb.h +120 -120
  55. data/ext/{iodine → iodine_ext}/iodine_helpers.c +0 -0
  56. data/ext/{iodine → iodine_ext}/iodine_helpers.h +12 -12
  57. data/ext/{iodine → iodine_ext}/iodine_http.c +0 -2
  58. data/ext/{iodine → iodine_ext}/iodine_http.h +23 -23
  59. data/ext/{iodine → iodine_ext}/iodine_json.c +302 -302
  60. data/ext/{iodine → iodine_ext}/iodine_json.h +6 -6
  61. data/ext/{iodine → iodine_ext}/iodine_mustache.c +0 -0
  62. data/ext/{iodine → iodine_ext}/iodine_mustache.h +6 -6
  63. data/ext/{iodine → iodine_ext}/iodine_pubsub.c +0 -0
  64. data/ext/{iodine → iodine_ext}/iodine_pubsub.h +26 -26
  65. data/ext/{iodine → iodine_ext}/iodine_rack_io.c +0 -0
  66. data/ext/{iodine → iodine_ext}/iodine_rack_io.h +20 -20
  67. data/ext/{iodine → iodine_ext}/iodine_store.c +0 -0
  68. data/ext/{iodine → iodine_ext}/iodine_store.h +20 -20
  69. data/ext/{iodine → iodine_ext}/iodine_tcp.c +0 -0
  70. data/ext/{iodine → iodine_ext}/iodine_tcp.h +0 -0
  71. data/ext/{iodine → iodine_ext}/iodine_tls.c +0 -0
  72. data/ext/{iodine → iodine_ext}/iodine_tls.h +13 -13
  73. data/ext/{iodine → iodine_ext}/mustache_parser.h +0 -0
  74. data/ext/{iodine → iodine_ext}/redis_engine.c +0 -0
  75. data/ext/{iodine → iodine_ext}/redis_engine.h +0 -0
  76. data/ext/{iodine → iodine_ext}/resp_parser.h +0 -0
  77. data/ext/{iodine → iodine_ext}/websocket_parser.h +505 -505
  78. data/ext/{iodine → iodine_ext}/websockets.c +0 -0
  79. data/ext/{iodine → iodine_ext}/websockets.h +185 -185
  80. data/isomorfeus-iodine.gemspec +1 -2
  81. data/lib/iodine/version.rb +1 -1
  82. data/lib/iodine.rb +1 -1
  83. metadata +79 -78
@@ -1,687 +1,687 @@
1
- #ifndef H_FIO_JSON_H
2
- /* *****************************************************************************
3
- * Copyright: Boaz Segev, 2017-2019
4
- * License: MIT
5
- *
6
- * This header file is a single-file JSON naive parse.
7
- *
8
- * The code was extracted form the FIOBJ implementation in order to allow the
9
- * parser to be used independantly from the rest of the facil.io library.
10
- *
11
- * The parser ignores missing commas and other formatting errors when possible.
12
- *
13
- * The parser also extends the JSON format to allow for C and Bash style
14
- * comments as well as hex numerical formats.
15
- *****************************************************************************
16
- */
17
- #define H_FIO_JSON_H
18
-
19
- #include <ctype.h>
20
- #include <math.h>
21
- #include <stdint.h>
22
- #include <stdlib.h>
23
- #include <string.h>
24
-
25
- #if DEBUG
26
- #include <stdio.h>
27
- #endif
28
-
29
- #if !defined(__GNUC__) && !defined(__clang__) && !defined(FIO_GNUC_BYPASS)
30
- #define __attribute__(...)
31
- #define __has_include(...) 0
32
- #define __has_builtin(...) 0
33
- #define FIO_GNUC_BYPASS 1
34
- #elif !defined(__clang__) && !defined(__has_builtin)
35
- #define __has_builtin(...) 0
36
- #define FIO_GNUC_BYPASS 1
37
- #endif
38
-
39
- /* *****************************************************************************
40
- JSON API
41
- ***************************************************************************** */
42
-
43
- /* maximum allowed depth values max out at 32, since a bitmap is used */
44
- #if !defined(JSON_MAX_DEPTH) || JSON_MAX_DEPTH > 32
45
- #undef JSON_MAX_DEPTH
46
- #define JSON_MAX_DEPTH 32
47
- #endif
48
-
49
- /** The JSON parser type. Memory must be initialized to 0 before first uses. */
50
- typedef struct {
51
- /** in dictionary flag. */
52
- uint32_t dict;
53
- /** level of nesting. */
54
- uint8_t depth;
55
- /** in dictionary waiting for key. */
56
- uint8_t key;
57
- } json_parser_s;
58
-
59
- /**
60
- * Stream parsing of JSON data using a persistent parser.
61
- *
62
- * Returns the number of bytes consumed (0 being a valid value).
63
- *
64
- * Unconsumed data should be resent to the parser once more data is available.
65
- *
66
- * For security (due to numeral parsing concerns), a NUL byte should be placed
67
- * at `buffer[length]`.
68
- */
69
- static size_t __attribute__((unused))
70
- fio_json_parse(json_parser_s *parser, const char *buffer, size_t length);
71
-
72
- /**
73
- * This function allows JSON formatted strings to be converted to native
74
- * strings.
75
- */
76
- static size_t __attribute__((unused))
77
- fio_json_unescape_str(void *dest, const char *source, size_t length);
78
-
79
- /* *****************************************************************************
80
- JSON Callacks - these must be implemented in the C file that uses the parser
81
- ***************************************************************************** */
82
-
83
- /** a NULL object was detected */
84
- static void fio_json_on_null(json_parser_s *p);
85
- /** a TRUE object was detected */
86
- static void fio_json_on_true(json_parser_s *p);
87
- /** a FALSE object was detected */
88
- static void fio_json_on_false(json_parser_s *p);
89
- /** a Numberl was detected (long long). */
90
- static void fio_json_on_number(json_parser_s *p, long long i);
91
- /** a Float was detected (double). */
92
- static void fio_json_on_float(json_parser_s *p, double f);
93
- /** a String was detected (int / float). update `pos` to point at ending */
94
- static void fio_json_on_string(json_parser_s *p, void *start, size_t length);
95
- /** a dictionary object was detected, should return 0 unless error occurred. */
96
- static int fio_json_on_start_object(json_parser_s *p);
97
- /** a dictionary object closure detected */
98
- static void fio_json_on_end_object(json_parser_s *p);
99
- /** an array object was detected, should return 0 unless error occurred. */
100
- static int fio_json_on_start_array(json_parser_s *p);
101
- /** an array closure was detected */
102
- static void fio_json_on_end_array(json_parser_s *p);
103
- /** the JSON parsing is complete */
104
- static void fio_json_on_json(json_parser_s *p);
105
- /** the JSON parsing is complete */
106
- static void fio_json_on_error(json_parser_s *p);
107
-
108
- /* *****************************************************************************
109
- JSON maps (arrays used to map data to simplify `if` statements)
110
- ***************************************************************************** */
111
-
112
- /*
113
- Marks as object seperators any of the following:
114
-
115
- * White Space: [0x09, 0x0A, 0x0D, 0x20]
116
- * Comma ("," / 0x2C)
117
- * NOT Colon (":" / 0x3A)
118
- * == [0x09, 0x0A, 0x0D, 0x20, 0x2C]
119
- The rest belong to objects,
120
- */
121
- static const uint8_t JSON_SEPERATOR[] = {
122
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123
- 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
124
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
125
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
126
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
128
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
129
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
130
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
131
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
132
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
133
- };
134
-
135
- /*
136
- Marks a numeral valid char (it's a permisive list):
137
- ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'e', 'E', '+', '-', 'x', 'b',
138
- '.']
139
- */
140
- static const uint8_t JSON_NUMERAL[] = {
141
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
142
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0,
143
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
144
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
145
- 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
146
- 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
147
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
148
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
149
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
150
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
151
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
152
- };
153
-
154
- static const char hex_chars[] = {'0', '1', '2', '3', '4', '5', '6', '7',
155
- '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
156
-
157
- static const uint8_t is_hex[] = {
158
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
159
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
160
- 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0,
161
- 0, 0, 0, 0, 0, 11, 12, 13, 14, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
162
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 13,
163
- 14, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
164
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
165
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
166
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
167
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
168
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
169
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
170
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
171
-
172
- /*
173
- Stops seeking a String:
174
- ['\\', '"']
175
- */
176
- static const uint8_t string_seek_stop[] = {
177
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
178
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
179
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
180
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
181
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
182
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
183
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
184
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
185
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
186
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
187
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
188
-
189
- /* *****************************************************************************
190
- JSON String Helper - Seeking to the end of a string
191
- ***************************************************************************** */
192
-
193
- /**
194
- * finds the first occurance of either '"' or '\\'.
195
- */
196
- static inline int seek2marker(uint8_t **buffer,
197
- register const uint8_t *const limit) {
198
- if (string_seek_stop[**buffer])
199
- return 1;
200
-
201
- #if !ALLOW_UNALIGNED_MEMORY_ACCESS || (!__x86_64__ && !__aarch64__)
202
- /* too short for this mess */
203
- if ((uintptr_t)limit <= 8 + ((uintptr_t)*buffer & (~(uintptr_t)7)))
204
- goto finish;
205
- /* align memory */
206
- if (1) {
207
- {
208
- const uint8_t *alignment =
209
- (uint8_t *)(((uintptr_t)(*buffer) & (~(uintptr_t)7)) + 8);
210
- if (limit >= alignment) {
211
- while (*buffer < alignment) {
212
- if (string_seek_stop[**buffer])
213
- return 1;
214
- *buffer += 1;
215
- }
216
- }
217
- }
218
- const uint8_t *limit64 = (uint8_t *)((uintptr_t)limit & (~(uintptr_t)7));
219
- #else
220
- const uint8_t *limit64 = (uint8_t *)limit - 7;
221
- #endif
222
- uint64_t wanted1 = 0x0101010101010101ULL * '"';
223
- uint64_t wanted2 = 0x0101010101010101ULL * '\\';
224
- for (; *buffer < limit64; *buffer += 8) {
225
- const uint64_t eq1 = ~((*((uint64_t *)*buffer)) ^ wanted1);
226
- const uint64_t t1 =
227
- ((eq1 & 0x7f7f7f7f7f7f7f7fULL) + 0x0101010101010101ULL) &
228
- (eq1 & 0x8080808080808080ULL);
229
- const uint64_t eq2 = ~((*((uint64_t *)*buffer)) ^ wanted2);
230
- const uint64_t t2 =
231
- ((eq2 & 0x7f7f7f7f7f7f7f7fULL) + 0x0101010101010101ULL) &
232
- (eq2 & 0x8080808080808080ULL);
233
- if ((t1 | t2)) {
234
- break;
235
- }
236
- }
237
- }
238
- #if !ALLOW_UNALIGNED_MEMORY_ACCESS || (!__x86_64__ && !__aarch64__)
239
- finish:
240
- #endif
241
- if (*buffer + 4 <= limit) {
242
- if (string_seek_stop[(*buffer)[0]]) {
243
- // *buffer += 0;
244
- return 1;
245
- }
246
- if (string_seek_stop[(*buffer)[1]]) {
247
- *buffer += 1;
248
- return 1;
249
- }
250
- if (string_seek_stop[(*buffer)[2]]) {
251
- *buffer += 2;
252
- return 1;
253
- }
254
- if (string_seek_stop[(*buffer)[3]]) {
255
- *buffer += 3;
256
- return 1;
257
- }
258
- *buffer += 4;
259
- }
260
- while (*buffer < limit) {
261
- if (string_seek_stop[**buffer])
262
- return 1;
263
- (*buffer)++;
264
- }
265
- return 0;
266
- }
267
-
268
- static inline int seek2eos(uint8_t **buffer,
269
- register const uint8_t *const limit) {
270
- while (*buffer < limit) {
271
- if (seek2marker(buffer, limit) && **buffer == '"')
272
- return 1;
273
- (*buffer) += 2; /* consume both the escape '\\' and the escape code. */
274
- }
275
- return 0;
276
- }
277
-
278
- /* *****************************************************************************
279
- JSON String to Numeral Helpers - allowing for stand-alone mode
280
- ***************************************************************************** */
281
-
282
- #ifndef H_FACIL_IO_H /* defined in fio.h */
283
-
284
- /**
285
- * We include this in case the parser is used outside of facil.io.
286
- */
287
- int64_t __attribute__((weak)) fio_atol(char **pstr) {
288
- return strtoll((char *)*pstr, (char **)pstr, 0);
289
- }
290
- #pragma weak fio_atol
291
-
292
- /**
293
- * We include this in case the parser is used outside of facil.io.
294
- */
295
- double __attribute__((weak)) fio_atof(char **pstr) {
296
- return strtod((char *)*pstr, (char **)pstr);
297
- }
298
- #pragma weak fio_atof
299
-
300
- #endif
301
-
302
- /* *****************************************************************************
303
- JSON Consumption (astract parsing)
304
- ***************************************************************************** */
305
-
306
- /**
307
- * Returns the number of bytes consumed. Stops as close as possible to the end
308
- * of the buffer or once an object parsing was completed.
309
- */
310
- static size_t __attribute__((unused))
311
- fio_json_parse(json_parser_s *parser, const char *buffer, size_t length) {
312
- if (!length || !buffer)
313
- return 0;
314
- uint8_t *pos = (uint8_t *)buffer;
315
- const uint8_t *limit = pos + length;
316
- do {
317
- while (pos < limit && JSON_SEPERATOR[*pos])
318
- ++pos;
319
- if (pos == limit)
320
- goto stop;
321
- switch (*pos) {
322
- case '"': {
323
- uint8_t *tmp = pos + 1;
324
- if (seek2eos(&tmp, limit) == 0)
325
- goto stop;
326
- if (parser->key) {
327
- uint8_t *key = tmp + 1;
328
- while (key < limit && JSON_SEPERATOR[*key])
329
- ++key;
330
- if (key >= limit)
331
- goto stop;
332
- if (*key != ':')
333
- goto error;
334
- ++pos;
335
- fio_json_on_string(parser, pos, (uintptr_t)(tmp - pos));
336
- pos = key + 1;
337
- parser->key = 0;
338
- continue; /* skip tests */
339
- } else {
340
- ++pos;
341
- fio_json_on_string(parser, pos, (uintptr_t)(tmp - pos));
342
- pos = tmp + 1;
343
- }
344
- break;
345
- }
346
- case '{':
347
- if (parser->key) {
348
- #if DEBUG
349
- fprintf(stderr, "ERROR: JSON key can't be a Hash.\n");
350
- #endif
351
- goto error;
352
- }
353
- ++parser->depth;
354
- if (parser->depth >= JSON_MAX_DEPTH)
355
- goto error;
356
- parser->dict = (parser->dict << 1) | 1;
357
- ++pos;
358
- if (fio_json_on_start_object(parser))
359
- goto error;
360
- break;
361
- case '}':
362
- if ((parser->dict & 1) == 0) {
363
- #if DEBUG
364
- fprintf(stderr, "ERROR: JSON dictionary closure error.\n");
365
- #endif
366
- goto error;
367
- }
368
- if (!parser->key) {
369
- #if DEBUG
370
- fprintf(stderr, "ERROR: JSON dictionary closure missing key value.\n");
371
- goto error;
372
- #endif
373
- fio_json_on_null(parser); /* append NULL and recuperate from error. */
374
- }
375
- --parser->depth;
376
- ++pos;
377
- parser->dict = (parser->dict >> 1);
378
- fio_json_on_end_object(parser);
379
- break;
380
- case '[':
381
- if (parser->key) {
382
- #if DEBUG
383
- fprintf(stderr, "ERROR: JSON key can't be an array.\n");
384
- #endif
385
- goto error;
386
- }
387
- ++parser->depth;
388
- if (parser->depth >= JSON_MAX_DEPTH)
389
- goto error;
390
- ++pos;
391
- parser->dict = (parser->dict << 1);
392
- if (fio_json_on_start_array(parser))
393
- goto error;
394
- break;
395
- case ']':
396
- if ((parser->dict & 1))
397
- goto error;
398
- --parser->depth;
399
- ++pos;
400
- parser->dict = (parser->dict >> 1);
401
- fio_json_on_end_array(parser);
402
- break;
403
- case 't':
404
- if (pos + 3 >= limit)
405
- goto stop;
406
- if (pos[1] == 'r' && pos[2] == 'u' && pos[3] == 'e')
407
- fio_json_on_true(parser);
408
- else
409
- goto error;
410
- pos += 4;
411
- break;
412
- case 'N': /* overflow */
413
- case 'n':
414
- if (pos + 2 <= limit && (pos[1] | 32) == 'a' && (pos[2] | 32) == 'n')
415
- goto numeral;
416
- if (pos + 3 >= limit)
417
- goto stop;
418
- if (pos[1] == 'u' && pos[2] == 'l' && pos[3] == 'l')
419
- fio_json_on_null(parser);
420
- else
421
- goto error;
422
- pos += 4;
423
- break;
424
- case 'f':
425
- if (pos + 4 >= limit)
426
- goto stop;
427
- if (pos + 4 < limit && pos[1] == 'a' && pos[2] == 'l' && pos[3] == 's' &&
428
- pos[4] == 'e')
429
- fio_json_on_false(parser);
430
- else
431
- goto error;
432
- pos += 5;
433
- break;
434
- case '-': /* overflow */
435
- case '0': /* overflow */
436
- case '1': /* overflow */
437
- case '2': /* overflow */
438
- case '3': /* overflow */
439
- case '4': /* overflow */
440
- case '5': /* overflow */
441
- case '6': /* overflow */
442
- case '7': /* overflow */
443
- case '8': /* overflow */
444
- case '9': /* overflow */
445
- case '.': /* overflow */
446
- case 'e': /* overflow */
447
- case 'E': /* overflow */
448
- case 'x': /* overflow */
449
- case 'i': /* overflow */
450
- case 'I': /* overflow */
451
- numeral : {
452
- uint8_t *tmp = pos;
453
- long long i = fio_atol((char **)&tmp);
454
- if (tmp > limit)
455
- goto stop;
456
- if (!tmp || JSON_NUMERAL[*tmp]) {
457
- tmp = pos;
458
- double f = fio_atof((char **)&tmp);
459
- if (tmp > limit)
460
- goto stop;
461
- if (!tmp || JSON_NUMERAL[*tmp])
462
- goto error;
463
- fio_json_on_float(parser, f);
464
- pos = tmp;
465
- } else {
466
- fio_json_on_number(parser, i);
467
- pos = tmp;
468
- }
469
- break;
470
- }
471
- case '#': /* Ruby style comment */
472
- {
473
- uint8_t *tmp = memchr(pos, '\n', (uintptr_t)(limit - pos));
474
- if (!tmp)
475
- goto stop;
476
- pos = tmp + 1;
477
- continue; /* skip tests */
478
- ;
479
- }
480
- case '/': /* C style / Javascript style comment */
481
- if (pos[1] == '*') {
482
- if (pos + 4 > limit)
483
- goto stop;
484
- uint8_t *tmp = pos + 3; /* avoid this: /*/
485
- do {
486
- tmp = memchr(tmp, '/', (uintptr_t)(limit - tmp));
487
- } while (tmp && tmp[-1] != '*');
488
- if (!tmp)
489
- goto stop;
490
- pos = tmp + 1;
491
- } else if (pos[1] == '/') {
492
- uint8_t *tmp = memchr(pos, '\n', (uintptr_t)(limit - pos));
493
- if (!tmp)
494
- goto stop;
495
- pos = tmp + 1;
496
- } else
497
- goto error;
498
- continue; /* skip tests */
499
- ;
500
- default:
501
- goto error;
502
- }
503
- if (parser->depth == 0) {
504
- fio_json_on_json(parser);
505
- goto stop;
506
- }
507
- parser->key = (parser->dict & 1);
508
- } while (pos < limit);
509
- stop:
510
- return (size_t)((uintptr_t)pos - (uintptr_t)buffer);
511
- error:
512
- fio_json_on_error(parser);
513
- return 0;
514
- }
515
-
516
- /* *****************************************************************************
517
- JSON Unescape String
518
- ***************************************************************************** */
519
-
520
- #ifdef __cplusplus
521
- #define REGISTER
522
- #else
523
- #define REGISTER register
524
- #endif
525
-
526
- /* converts a uint32_t to UTF-8 and returns the number of bytes written */
527
- static inline int utf8_from_u32(uint8_t *dest, uint32_t u) {
528
- if (u <= 127) {
529
- *dest = u;
530
- return 1;
531
- } else if (u <= 2047) {
532
- *(dest++) = 192 | (u >> 6);
533
- *(dest++) = 128 | (u & 63);
534
- return 2;
535
- } else if (u <= 65535) {
536
- *(dest++) = 224 | (u >> 12);
537
- *(dest++) = 128 | ((u >> 6) & 63);
538
- *(dest++) = 128 | (u & 63);
539
- return 3;
540
- }
541
- *(dest++) = 240 | ((u >> 18) & 7);
542
- *(dest++) = 128 | ((u >> 12) & 63);
543
- *(dest++) = 128 | ((u >> 6) & 63);
544
- *(dest++) = 128 | (u & 63);
545
- return 4;
546
- }
547
-
548
- static void __attribute__((unused))
549
- fio_json_unescape_str_internal(uint8_t **dest, const uint8_t **src) {
550
- ++(*src);
551
- switch (**src) {
552
- case 'b':
553
- **dest = '\b';
554
- ++(*src);
555
- ++(*dest);
556
- return; /* from switch */
557
- case 'f':
558
- **dest = '\f';
559
- ++(*src);
560
- ++(*dest);
561
- return; /* from switch */
562
- case 'n':
563
- **dest = '\n';
564
- ++(*src);
565
- ++(*dest);
566
- return; /* from switch */
567
- case 'r':
568
- **dest = '\r';
569
- ++(*src);
570
- ++(*dest);
571
- return; /* from switch */
572
- case 't':
573
- **dest = '\t';
574
- ++(*src);
575
- ++(*dest);
576
- return; /* from switch */
577
- case 'u': { /* test for octal notation */
578
- if (is_hex[(*src)[1]] && is_hex[(*src)[2]] && is_hex[(*src)[3]] &&
579
- is_hex[(*src)[4]]) {
580
- uint32_t t =
581
- ((((is_hex[(*src)[1]] - 1) << 4) | (is_hex[(*src)[2]] - 1)) << 8) |
582
- (((is_hex[(*src)[3]] - 1) << 4) | (is_hex[(*src)[4]] - 1));
583
- if ((*src)[5] == '\\' && (*src)[6] == 'u' && is_hex[(*src)[7]] &&
584
- is_hex[(*src)[8]] && is_hex[(*src)[9]] && is_hex[(*src)[10]]) {
585
- /* Serrogate Pair */
586
- t = (t & 0x03FF) << 10;
587
- t |= ((((((is_hex[(*src)[7]] - 1) << 4) | (is_hex[(*src)[8]] - 1))
588
- << 8) |
589
- (((is_hex[(*src)[9]] - 1) << 4) | (is_hex[(*src)[10]] - 1))) &
590
- 0x03FF);
591
- t += 0x10000;
592
- (*src) += 6;
593
- }
594
- *dest += utf8_from_u32(*dest, t);
595
- *src += 5;
596
- return;
597
- } else
598
- goto invalid_escape;
599
- }
600
- case 'x': { /* test for hex notation */
601
- if (is_hex[(*src)[1]] && is_hex[(*src)[2]]) {
602
- **dest = ((is_hex[(*src)[1]] - 1) << 4) | (is_hex[(*src)[2]] - 1);
603
- ++(*dest);
604
- (*src) += 3;
605
- return;
606
- } else
607
- goto invalid_escape;
608
- }
609
- case '0':
610
- case '1':
611
- case '2':
612
- case '3':
613
- case '4':
614
- case '5':
615
- case '6':
616
- case '7': { /* test for octal notation */
617
- if ((*src)[1] >= '0' && (*src)[1] <= '7') {
618
- **dest = (((*src)[0] - '0') << 3) | ((*src)[1] - '0');
619
- ++(*dest);
620
- (*src) += 2;
621
- break; /* from switch */
622
- } else
623
- goto invalid_escape;
624
- }
625
- case '"':
626
- case '\\':
627
- case '/':
628
- /* fallthrough */
629
- default:
630
- invalid_escape:
631
- **dest = **src;
632
- ++(*src);
633
- ++(*dest);
634
- }
635
- }
636
-
637
- static size_t __attribute__((unused))
638
- fio_json_unescape_str(void *dest, const char *source, size_t length) {
639
- const uint8_t *reader = (uint8_t *)source;
640
- const uint8_t *stop = reader + length;
641
- uint8_t *writer = (uint8_t *)dest;
642
- /* copy in chuncks unless we hit an escape marker */
643
- while (reader < stop) {
644
- #if !__x86_64__ && !__aarch64__
645
- /* we can't leverage unaligned memory access, so we read the buffer twice */
646
- uint8_t *tmp = memchr(reader, '\\', (size_t)(stop - reader));
647
- if (!tmp) {
648
- memmove(writer, reader, (size_t)(stop - reader));
649
- writer += (size_t)(stop - reader);
650
- goto finish;
651
- }
652
- memmove(writer, reader, (size_t)(tmp - reader));
653
- writer += (size_t)(tmp - reader);
654
- reader = tmp;
655
- #else
656
- const uint8_t *limit64 = (uint8_t *)stop - 7;
657
- uint64_t wanted1 = 0x0101010101010101ULL * '\\';
658
- while (reader < limit64) {
659
- const uint64_t eq1 = ~((*((uint64_t *)reader)) ^ wanted1);
660
- const uint64_t t0 = (eq1 & 0x7f7f7f7f7f7f7f7fllu) + 0x0101010101010101llu;
661
- const uint64_t t1 = (eq1 & 0x8080808080808080llu);
662
- if ((t0 & t1)) {
663
- break;
664
- }
665
- *((uint64_t *)writer) = *((uint64_t *)reader);
666
- reader += 8;
667
- writer += 8;
668
- }
669
- while (reader < stop) {
670
- if (*reader == '\\')
671
- break;
672
- *writer = *reader;
673
- ++reader;
674
- ++writer;
675
- }
676
- if (reader >= stop)
677
- goto finish;
678
- #endif
679
- fio_json_unescape_str_internal(&writer, &reader);
680
- }
681
- finish:
682
- return (size_t)((uintptr_t)writer - (uintptr_t)dest);
683
- }
684
-
685
- #undef REGISTER
686
-
687
- #endif
1
+ #ifndef H_FIO_JSON_H
2
+ /* *****************************************************************************
3
+ * Copyright: Boaz Segev, 2017-2019
4
+ * License: MIT
5
+ *
6
+ * This header file is a single-file JSON naive parse.
7
+ *
8
+ * The code was extracted form the FIOBJ implementation in order to allow the
9
+ * parser to be used independantly from the rest of the facil.io library.
10
+ *
11
+ * The parser ignores missing commas and other formatting errors when possible.
12
+ *
13
+ * The parser also extends the JSON format to allow for C and Bash style
14
+ * comments as well as hex numerical formats.
15
+ *****************************************************************************
16
+ */
17
+ #define H_FIO_JSON_H
18
+
19
+ #include <ctype.h>
20
+ #include <math.h>
21
+ #include <stdint.h>
22
+ #include <stdlib.h>
23
+ #include <string.h>
24
+
25
+ #if DEBUG
26
+ #include <stdio.h>
27
+ #endif
28
+
29
+ #if !defined(__GNUC__) && !defined(__clang__) && !defined(FIO_GNUC_BYPASS)
30
+ #define __attribute__(...)
31
+ #define __has_include(...) 0
32
+ #define __has_builtin(...) 0
33
+ #define FIO_GNUC_BYPASS 1
34
+ #elif !defined(__clang__) && !defined(__has_builtin)
35
+ #define __has_builtin(...) 0
36
+ #define FIO_GNUC_BYPASS 1
37
+ #endif
38
+
39
+ /* *****************************************************************************
40
+ JSON API
41
+ ***************************************************************************** */
42
+
43
+ /* maximum allowed depth values max out at 32, since a bitmap is used */
44
+ #if !defined(JSON_MAX_DEPTH) || JSON_MAX_DEPTH > 32
45
+ #undef JSON_MAX_DEPTH
46
+ #define JSON_MAX_DEPTH 32
47
+ #endif
48
+
49
+ /** The JSON parser type. Memory must be initialized to 0 before first uses. */
50
+ typedef struct {
51
+ /** in dictionary flag. */
52
+ uint32_t dict;
53
+ /** level of nesting. */
54
+ uint8_t depth;
55
+ /** in dictionary waiting for key. */
56
+ uint8_t key;
57
+ } json_parser_s;
58
+
59
+ /**
60
+ * Stream parsing of JSON data using a persistent parser.
61
+ *
62
+ * Returns the number of bytes consumed (0 being a valid value).
63
+ *
64
+ * Unconsumed data should be resent to the parser once more data is available.
65
+ *
66
+ * For security (due to numeral parsing concerns), a NUL byte should be placed
67
+ * at `buffer[length]`.
68
+ */
69
+ static size_t __attribute__((unused))
70
+ fio_json_parse(json_parser_s *parser, const char *buffer, size_t length);
71
+
72
+ /**
73
+ * This function allows JSON formatted strings to be converted to native
74
+ * strings.
75
+ */
76
+ static size_t __attribute__((unused))
77
+ fio_json_unescape_str(void *dest, const char *source, size_t length);
78
+
79
+ /* *****************************************************************************
80
+ JSON Callacks - these must be implemented in the C file that uses the parser
81
+ ***************************************************************************** */
82
+
83
+ /** a NULL object was detected */
84
+ static void fio_json_on_null(json_parser_s *p);
85
+ /** a TRUE object was detected */
86
+ static void fio_json_on_true(json_parser_s *p);
87
+ /** a FALSE object was detected */
88
+ static void fio_json_on_false(json_parser_s *p);
89
+ /** a Numberl was detected (long long). */
90
+ static void fio_json_on_number(json_parser_s *p, long long i);
91
+ /** a Float was detected (double). */
92
+ static void fio_json_on_float(json_parser_s *p, double f);
93
+ /** a String was detected (int / float). update `pos` to point at ending */
94
+ static void fio_json_on_string(json_parser_s *p, void *start, size_t length);
95
+ /** a dictionary object was detected, should return 0 unless error occurred. */
96
+ static int fio_json_on_start_object(json_parser_s *p);
97
+ /** a dictionary object closure detected */
98
+ static void fio_json_on_end_object(json_parser_s *p);
99
+ /** an array object was detected, should return 0 unless error occurred. */
100
+ static int fio_json_on_start_array(json_parser_s *p);
101
+ /** an array closure was detected */
102
+ static void fio_json_on_end_array(json_parser_s *p);
103
+ /** the JSON parsing is complete */
104
+ static void fio_json_on_json(json_parser_s *p);
105
+ /** the JSON parsing is complete */
106
+ static void fio_json_on_error(json_parser_s *p);
107
+
108
+ /* *****************************************************************************
109
+ JSON maps (arrays used to map data to simplify `if` statements)
110
+ ***************************************************************************** */
111
+
112
+ /*
113
+ Marks as object seperators any of the following:
114
+
115
+ * White Space: [0x09, 0x0A, 0x0D, 0x20]
116
+ * Comma ("," / 0x2C)
117
+ * NOT Colon (":" / 0x3A)
118
+ * == [0x09, 0x0A, 0x0D, 0x20, 0x2C]
119
+ The rest belong to objects,
120
+ */
121
+ static const uint8_t JSON_SEPERATOR[] = {
122
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
124
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
125
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
126
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
128
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
129
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
130
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
131
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
132
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
133
+ };
134
+
135
+ /*
136
+ Marks a numeral valid char (it's a permisive list):
137
+ ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'e', 'E', '+', '-', 'x', 'b',
138
+ '.']
139
+ */
140
+ static const uint8_t JSON_NUMERAL[] = {
141
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
142
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0,
143
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
144
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
145
+ 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
146
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
147
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
148
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
149
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
150
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
151
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
152
+ };
153
+
154
+ static const char hex_chars[] = {'0', '1', '2', '3', '4', '5', '6', '7',
155
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
156
+
157
+ static const uint8_t is_hex[] = {
158
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
159
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
160
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0,
161
+ 0, 0, 0, 0, 0, 11, 12, 13, 14, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
162
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 13,
163
+ 14, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
164
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
165
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
166
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
167
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
168
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
169
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
170
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
171
+
172
+ /*
173
+ Stops seeking a String:
174
+ ['\\', '"']
175
+ */
176
+ static const uint8_t string_seek_stop[] = {
177
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
178
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
179
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
180
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
181
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
182
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
183
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
184
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
185
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
186
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
187
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
188
+
189
+ /* *****************************************************************************
190
+ JSON String Helper - Seeking to the end of a string
191
+ ***************************************************************************** */
192
+
193
+ /**
194
+ * finds the first occurance of either '"' or '\\'.
195
+ */
196
+ static inline int seek2marker(uint8_t **buffer,
197
+ register const uint8_t *const limit) {
198
+ if (string_seek_stop[**buffer])
199
+ return 1;
200
+
201
+ #if !ALLOW_UNALIGNED_MEMORY_ACCESS || (!__x86_64__ && !__aarch64__)
202
+ /* too short for this mess */
203
+ if ((uintptr_t)limit <= 8 + ((uintptr_t)*buffer & (~(uintptr_t)7)))
204
+ goto finish;
205
+ /* align memory */
206
+ if (1) {
207
+ {
208
+ const uint8_t *alignment =
209
+ (uint8_t *)(((uintptr_t)(*buffer) & (~(uintptr_t)7)) + 8);
210
+ if (limit >= alignment) {
211
+ while (*buffer < alignment) {
212
+ if (string_seek_stop[**buffer])
213
+ return 1;
214
+ *buffer += 1;
215
+ }
216
+ }
217
+ }
218
+ const uint8_t *limit64 = (uint8_t *)((uintptr_t)limit & (~(uintptr_t)7));
219
+ #else
220
+ const uint8_t *limit64 = (uint8_t *)limit - 7;
221
+ #endif
222
+ uint64_t wanted1 = 0x0101010101010101ULL * '"';
223
+ uint64_t wanted2 = 0x0101010101010101ULL * '\\';
224
+ for (; *buffer < limit64; *buffer += 8) {
225
+ const uint64_t eq1 = ~((*((uint64_t *)*buffer)) ^ wanted1);
226
+ const uint64_t t1 =
227
+ ((eq1 & 0x7f7f7f7f7f7f7f7fULL) + 0x0101010101010101ULL) &
228
+ (eq1 & 0x8080808080808080ULL);
229
+ const uint64_t eq2 = ~((*((uint64_t *)*buffer)) ^ wanted2);
230
+ const uint64_t t2 =
231
+ ((eq2 & 0x7f7f7f7f7f7f7f7fULL) + 0x0101010101010101ULL) &
232
+ (eq2 & 0x8080808080808080ULL);
233
+ if ((t1 | t2)) {
234
+ break;
235
+ }
236
+ }
237
+ }
238
+ #if !ALLOW_UNALIGNED_MEMORY_ACCESS || (!__x86_64__ && !__aarch64__)
239
+ finish:
240
+ #endif
241
+ if (*buffer + 4 <= limit) {
242
+ if (string_seek_stop[(*buffer)[0]]) {
243
+ // *buffer += 0;
244
+ return 1;
245
+ }
246
+ if (string_seek_stop[(*buffer)[1]]) {
247
+ *buffer += 1;
248
+ return 1;
249
+ }
250
+ if (string_seek_stop[(*buffer)[2]]) {
251
+ *buffer += 2;
252
+ return 1;
253
+ }
254
+ if (string_seek_stop[(*buffer)[3]]) {
255
+ *buffer += 3;
256
+ return 1;
257
+ }
258
+ *buffer += 4;
259
+ }
260
+ while (*buffer < limit) {
261
+ if (string_seek_stop[**buffer])
262
+ return 1;
263
+ (*buffer)++;
264
+ }
265
+ return 0;
266
+ }
267
+
268
+ static inline int seek2eos(uint8_t **buffer,
269
+ register const uint8_t *const limit) {
270
+ while (*buffer < limit) {
271
+ if (seek2marker(buffer, limit) && **buffer == '"')
272
+ return 1;
273
+ (*buffer) += 2; /* consume both the escape '\\' and the escape code. */
274
+ }
275
+ return 0;
276
+ }
277
+
278
+ /* *****************************************************************************
279
+ JSON String to Numeral Helpers - allowing for stand-alone mode
280
+ ***************************************************************************** */
281
+
282
+ #ifndef H_FACIL_IO_H /* defined in fio.h */
283
+
284
+ /**
285
+ * We include this in case the parser is used outside of facil.io.
286
+ */
287
+ int64_t __attribute__((weak)) fio_atol(char **pstr) {
288
+ return strtoll((char *)*pstr, (char **)pstr, 0);
289
+ }
290
+ #pragma weak fio_atol
291
+
292
+ /**
293
+ * We include this in case the parser is used outside of facil.io.
294
+ */
295
+ double __attribute__((weak)) fio_atof(char **pstr) {
296
+ return strtod((char *)*pstr, (char **)pstr);
297
+ }
298
+ #pragma weak fio_atof
299
+
300
+ #endif
301
+
302
+ /* *****************************************************************************
303
+ JSON Consumption (astract parsing)
304
+ ***************************************************************************** */
305
+
306
+ /**
307
+ * Returns the number of bytes consumed. Stops as close as possible to the end
308
+ * of the buffer or once an object parsing was completed.
309
+ */
310
+ static size_t __attribute__((unused))
311
+ fio_json_parse(json_parser_s *parser, const char *buffer, size_t length) {
312
+ if (!length || !buffer)
313
+ return 0;
314
+ uint8_t *pos = (uint8_t *)buffer;
315
+ const uint8_t *limit = pos + length;
316
+ do {
317
+ while (pos < limit && JSON_SEPERATOR[*pos])
318
+ ++pos;
319
+ if (pos == limit)
320
+ goto stop;
321
+ switch (*pos) {
322
+ case '"': {
323
+ uint8_t *tmp = pos + 1;
324
+ if (seek2eos(&tmp, limit) == 0)
325
+ goto stop;
326
+ if (parser->key) {
327
+ uint8_t *key = tmp + 1;
328
+ while (key < limit && JSON_SEPERATOR[*key])
329
+ ++key;
330
+ if (key >= limit)
331
+ goto stop;
332
+ if (*key != ':')
333
+ goto error;
334
+ ++pos;
335
+ fio_json_on_string(parser, pos, (uintptr_t)(tmp - pos));
336
+ pos = key + 1;
337
+ parser->key = 0;
338
+ continue; /* skip tests */
339
+ } else {
340
+ ++pos;
341
+ fio_json_on_string(parser, pos, (uintptr_t)(tmp - pos));
342
+ pos = tmp + 1;
343
+ }
344
+ break;
345
+ }
346
+ case '{':
347
+ if (parser->key) {
348
+ #if DEBUG
349
+ fprintf(stderr, "ERROR: JSON key can't be a Hash.\n");
350
+ #endif
351
+ goto error;
352
+ }
353
+ ++parser->depth;
354
+ if (parser->depth >= JSON_MAX_DEPTH)
355
+ goto error;
356
+ parser->dict = (parser->dict << 1) | 1;
357
+ ++pos;
358
+ if (fio_json_on_start_object(parser))
359
+ goto error;
360
+ break;
361
+ case '}':
362
+ if ((parser->dict & 1) == 0) {
363
+ #if DEBUG
364
+ fprintf(stderr, "ERROR: JSON dictionary closure error.\n");
365
+ #endif
366
+ goto error;
367
+ }
368
+ if (!parser->key) {
369
+ #if DEBUG
370
+ fprintf(stderr, "ERROR: JSON dictionary closure missing key value.\n");
371
+ goto error;
372
+ #endif
373
+ fio_json_on_null(parser); /* append NULL and recuperate from error. */
374
+ }
375
+ --parser->depth;
376
+ ++pos;
377
+ parser->dict = (parser->dict >> 1);
378
+ fio_json_on_end_object(parser);
379
+ break;
380
+ case '[':
381
+ if (parser->key) {
382
+ #if DEBUG
383
+ fprintf(stderr, "ERROR: JSON key can't be an array.\n");
384
+ #endif
385
+ goto error;
386
+ }
387
+ ++parser->depth;
388
+ if (parser->depth >= JSON_MAX_DEPTH)
389
+ goto error;
390
+ ++pos;
391
+ parser->dict = (parser->dict << 1);
392
+ if (fio_json_on_start_array(parser))
393
+ goto error;
394
+ break;
395
+ case ']':
396
+ if ((parser->dict & 1))
397
+ goto error;
398
+ --parser->depth;
399
+ ++pos;
400
+ parser->dict = (parser->dict >> 1);
401
+ fio_json_on_end_array(parser);
402
+ break;
403
+ case 't':
404
+ if (pos + 3 >= limit)
405
+ goto stop;
406
+ if (pos[1] == 'r' && pos[2] == 'u' && pos[3] == 'e')
407
+ fio_json_on_true(parser);
408
+ else
409
+ goto error;
410
+ pos += 4;
411
+ break;
412
+ case 'N': /* overflow */
413
+ case 'n':
414
+ if (pos + 2 <= limit && (pos[1] | 32) == 'a' && (pos[2] | 32) == 'n')
415
+ goto numeral;
416
+ if (pos + 3 >= limit)
417
+ goto stop;
418
+ if (pos[1] == 'u' && pos[2] == 'l' && pos[3] == 'l')
419
+ fio_json_on_null(parser);
420
+ else
421
+ goto error;
422
+ pos += 4;
423
+ break;
424
+ case 'f':
425
+ if (pos + 4 >= limit)
426
+ goto stop;
427
+ if (pos + 4 < limit && pos[1] == 'a' && pos[2] == 'l' && pos[3] == 's' &&
428
+ pos[4] == 'e')
429
+ fio_json_on_false(parser);
430
+ else
431
+ goto error;
432
+ pos += 5;
433
+ break;
434
+ case '-': /* overflow */
435
+ case '0': /* overflow */
436
+ case '1': /* overflow */
437
+ case '2': /* overflow */
438
+ case '3': /* overflow */
439
+ case '4': /* overflow */
440
+ case '5': /* overflow */
441
+ case '6': /* overflow */
442
+ case '7': /* overflow */
443
+ case '8': /* overflow */
444
+ case '9': /* overflow */
445
+ case '.': /* overflow */
446
+ case 'e': /* overflow */
447
+ case 'E': /* overflow */
448
+ case 'x': /* overflow */
449
+ case 'i': /* overflow */
450
+ case 'I': /* overflow */
451
+ numeral : {
452
+ uint8_t *tmp = pos;
453
+ long long i = fio_atol((char **)&tmp);
454
+ if (tmp > limit)
455
+ goto stop;
456
+ if (!tmp || JSON_NUMERAL[*tmp]) {
457
+ tmp = pos;
458
+ double f = fio_atof((char **)&tmp);
459
+ if (tmp > limit)
460
+ goto stop;
461
+ if (!tmp || JSON_NUMERAL[*tmp])
462
+ goto error;
463
+ fio_json_on_float(parser, f);
464
+ pos = tmp;
465
+ } else {
466
+ fio_json_on_number(parser, i);
467
+ pos = tmp;
468
+ }
469
+ break;
470
+ }
471
+ case '#': /* Ruby style comment */
472
+ {
473
+ uint8_t *tmp = memchr(pos, '\n', (uintptr_t)(limit - pos));
474
+ if (!tmp)
475
+ goto stop;
476
+ pos = tmp + 1;
477
+ continue; /* skip tests */
478
+ ;
479
+ }
480
+ case '/': /* C style / Javascript style comment */
481
+ if (pos[1] == '*') {
482
+ if (pos + 4 > limit)
483
+ goto stop;
484
+ uint8_t *tmp = pos + 3; /* avoid this: /*/
485
+ do {
486
+ tmp = memchr(tmp, '/', (uintptr_t)(limit - tmp));
487
+ } while (tmp && tmp[-1] != '*');
488
+ if (!tmp)
489
+ goto stop;
490
+ pos = tmp + 1;
491
+ } else if (pos[1] == '/') {
492
+ uint8_t *tmp = memchr(pos, '\n', (uintptr_t)(limit - pos));
493
+ if (!tmp)
494
+ goto stop;
495
+ pos = tmp + 1;
496
+ } else
497
+ goto error;
498
+ continue; /* skip tests */
499
+ ;
500
+ default:
501
+ goto error;
502
+ }
503
+ if (parser->depth == 0) {
504
+ fio_json_on_json(parser);
505
+ goto stop;
506
+ }
507
+ parser->key = (parser->dict & 1);
508
+ } while (pos < limit);
509
+ stop:
510
+ return (size_t)((uintptr_t)pos - (uintptr_t)buffer);
511
+ error:
512
+ fio_json_on_error(parser);
513
+ return 0;
514
+ }
515
+
516
+ /* *****************************************************************************
517
+ JSON Unescape String
518
+ ***************************************************************************** */
519
+
520
+ #ifdef __cplusplus
521
+ #define REGISTER
522
+ #else
523
+ #define REGISTER register
524
+ #endif
525
+
526
+ /* converts a uint32_t to UTF-8 and returns the number of bytes written */
527
+ static inline int utf8_from_u32(uint8_t *dest, uint32_t u) {
528
+ if (u <= 127) {
529
+ *dest = u;
530
+ return 1;
531
+ } else if (u <= 2047) {
532
+ *(dest++) = 192 | (u >> 6);
533
+ *(dest++) = 128 | (u & 63);
534
+ return 2;
535
+ } else if (u <= 65535) {
536
+ *(dest++) = 224 | (u >> 12);
537
+ *(dest++) = 128 | ((u >> 6) & 63);
538
+ *(dest++) = 128 | (u & 63);
539
+ return 3;
540
+ }
541
+ *(dest++) = 240 | ((u >> 18) & 7);
542
+ *(dest++) = 128 | ((u >> 12) & 63);
543
+ *(dest++) = 128 | ((u >> 6) & 63);
544
+ *(dest++) = 128 | (u & 63);
545
+ return 4;
546
+ }
547
+
548
+ static void __attribute__((unused))
549
+ fio_json_unescape_str_internal(uint8_t **dest, const uint8_t **src) {
550
+ ++(*src);
551
+ switch (**src) {
552
+ case 'b':
553
+ **dest = '\b';
554
+ ++(*src);
555
+ ++(*dest);
556
+ return; /* from switch */
557
+ case 'f':
558
+ **dest = '\f';
559
+ ++(*src);
560
+ ++(*dest);
561
+ return; /* from switch */
562
+ case 'n':
563
+ **dest = '\n';
564
+ ++(*src);
565
+ ++(*dest);
566
+ return; /* from switch */
567
+ case 'r':
568
+ **dest = '\r';
569
+ ++(*src);
570
+ ++(*dest);
571
+ return; /* from switch */
572
+ case 't':
573
+ **dest = '\t';
574
+ ++(*src);
575
+ ++(*dest);
576
+ return; /* from switch */
577
+ case 'u': { /* test for octal notation */
578
+ if (is_hex[(*src)[1]] && is_hex[(*src)[2]] && is_hex[(*src)[3]] &&
579
+ is_hex[(*src)[4]]) {
580
+ uint32_t t =
581
+ ((((is_hex[(*src)[1]] - 1) << 4) | (is_hex[(*src)[2]] - 1)) << 8) |
582
+ (((is_hex[(*src)[3]] - 1) << 4) | (is_hex[(*src)[4]] - 1));
583
+ if ((*src)[5] == '\\' && (*src)[6] == 'u' && is_hex[(*src)[7]] &&
584
+ is_hex[(*src)[8]] && is_hex[(*src)[9]] && is_hex[(*src)[10]]) {
585
+ /* Serrogate Pair */
586
+ t = (t & 0x03FF) << 10;
587
+ t |= ((((((is_hex[(*src)[7]] - 1) << 4) | (is_hex[(*src)[8]] - 1))
588
+ << 8) |
589
+ (((is_hex[(*src)[9]] - 1) << 4) | (is_hex[(*src)[10]] - 1))) &
590
+ 0x03FF);
591
+ t += 0x10000;
592
+ (*src) += 6;
593
+ }
594
+ *dest += utf8_from_u32(*dest, t);
595
+ *src += 5;
596
+ return;
597
+ } else
598
+ goto invalid_escape;
599
+ }
600
+ case 'x': { /* test for hex notation */
601
+ if (is_hex[(*src)[1]] && is_hex[(*src)[2]]) {
602
+ **dest = ((is_hex[(*src)[1]] - 1) << 4) | (is_hex[(*src)[2]] - 1);
603
+ ++(*dest);
604
+ (*src) += 3;
605
+ return;
606
+ } else
607
+ goto invalid_escape;
608
+ }
609
+ case '0':
610
+ case '1':
611
+ case '2':
612
+ case '3':
613
+ case '4':
614
+ case '5':
615
+ case '6':
616
+ case '7': { /* test for octal notation */
617
+ if ((*src)[1] >= '0' && (*src)[1] <= '7') {
618
+ **dest = (((*src)[0] - '0') << 3) | ((*src)[1] - '0');
619
+ ++(*dest);
620
+ (*src) += 2;
621
+ break; /* from switch */
622
+ } else
623
+ goto invalid_escape;
624
+ }
625
+ case '"':
626
+ case '\\':
627
+ case '/':
628
+ /* fallthrough */
629
+ default:
630
+ invalid_escape:
631
+ **dest = **src;
632
+ ++(*src);
633
+ ++(*dest);
634
+ }
635
+ }
636
+
637
+ static size_t __attribute__((unused))
638
+ fio_json_unescape_str(void *dest, const char *source, size_t length) {
639
+ const uint8_t *reader = (uint8_t *)source;
640
+ const uint8_t *stop = reader + length;
641
+ uint8_t *writer = (uint8_t *)dest;
642
+ /* copy in chuncks unless we hit an escape marker */
643
+ while (reader < stop) {
644
+ #if !__x86_64__ && !__aarch64__
645
+ /* we can't leverage unaligned memory access, so we read the buffer twice */
646
+ uint8_t *tmp = memchr(reader, '\\', (size_t)(stop - reader));
647
+ if (!tmp) {
648
+ memmove(writer, reader, (size_t)(stop - reader));
649
+ writer += (size_t)(stop - reader);
650
+ goto finish;
651
+ }
652
+ memmove(writer, reader, (size_t)(tmp - reader));
653
+ writer += (size_t)(tmp - reader);
654
+ reader = tmp;
655
+ #else
656
+ const uint8_t *limit64 = (uint8_t *)stop - 7;
657
+ uint64_t wanted1 = 0x0101010101010101ULL * '\\';
658
+ while (reader < limit64) {
659
+ const uint64_t eq1 = ~((*((uint64_t *)reader)) ^ wanted1);
660
+ const uint64_t t0 = (eq1 & 0x7f7f7f7f7f7f7f7fllu) + 0x0101010101010101llu;
661
+ const uint64_t t1 = (eq1 & 0x8080808080808080llu);
662
+ if ((t0 & t1)) {
663
+ break;
664
+ }
665
+ *((uint64_t *)writer) = *((uint64_t *)reader);
666
+ reader += 8;
667
+ writer += 8;
668
+ }
669
+ while (reader < stop) {
670
+ if (*reader == '\\')
671
+ break;
672
+ *writer = *reader;
673
+ ++reader;
674
+ ++writer;
675
+ }
676
+ if (reader >= stop)
677
+ goto finish;
678
+ #endif
679
+ fio_json_unescape_str_internal(&writer, &reader);
680
+ }
681
+ finish:
682
+ return (size_t)((uintptr_t)writer - (uintptr_t)dest);
683
+ }
684
+
685
+ #undef REGISTER
686
+
687
+ #endif