rage-iodine 1.7.58

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  3. data/.github/workflows/ruby.yml +42 -0
  4. data/.gitignore +20 -0
  5. data/.rspec +2 -0
  6. data/.yardopts +8 -0
  7. data/CHANGELOG.md +1098 -0
  8. data/Gemfile +11 -0
  9. data/LICENSE.txt +21 -0
  10. data/LIMITS.md +41 -0
  11. data/README.md +782 -0
  12. data/Rakefile +23 -0
  13. data/SPEC-PubSub-Draft.md +159 -0
  14. data/SPEC-WebSocket-Draft.md +239 -0
  15. data/bin/console +22 -0
  16. data/bin/info.md +353 -0
  17. data/bin/mustache_bench.rb +100 -0
  18. data/bin/poc/Gemfile.lock +23 -0
  19. data/bin/poc/README.md +37 -0
  20. data/bin/poc/config.ru +66 -0
  21. data/bin/poc/gemfile +1 -0
  22. data/bin/poc/www/index.html +57 -0
  23. data/examples/async_task.ru +92 -0
  24. data/examples/bates/README.md +3 -0
  25. data/examples/bates/config.ru +342 -0
  26. data/examples/bates/david+bold.pdf +0 -0
  27. data/examples/bates/public/drop-pdf.png +0 -0
  28. data/examples/bates/public/index.html +600 -0
  29. data/examples/config.ru +59 -0
  30. data/examples/echo.ru +59 -0
  31. data/examples/etag.ru +16 -0
  32. data/examples/hello.ru +29 -0
  33. data/examples/pubsub_engine.ru +81 -0
  34. data/examples/rack3.ru +12 -0
  35. data/examples/redis.ru +70 -0
  36. data/examples/shootout.ru +73 -0
  37. data/examples/sub-protocols.ru +90 -0
  38. data/examples/tcp_client.rb +66 -0
  39. data/examples/x-sendfile.ru +14 -0
  40. data/exe/iodine +280 -0
  41. data/ext/iodine/extconf.rb +110 -0
  42. data/ext/iodine/fio.c +12096 -0
  43. data/ext/iodine/fio.h +6390 -0
  44. data/ext/iodine/fio_cli.c +431 -0
  45. data/ext/iodine/fio_cli.h +189 -0
  46. data/ext/iodine/fio_json_parser.h +687 -0
  47. data/ext/iodine/fio_siphash.c +157 -0
  48. data/ext/iodine/fio_siphash.h +37 -0
  49. data/ext/iodine/fio_tls.h +129 -0
  50. data/ext/iodine/fio_tls_missing.c +649 -0
  51. data/ext/iodine/fio_tls_openssl.c +1056 -0
  52. data/ext/iodine/fio_tmpfile.h +50 -0
  53. data/ext/iodine/fiobj.h +44 -0
  54. data/ext/iodine/fiobj4fio.h +21 -0
  55. data/ext/iodine/fiobj_ary.c +333 -0
  56. data/ext/iodine/fiobj_ary.h +139 -0
  57. data/ext/iodine/fiobj_data.c +1185 -0
  58. data/ext/iodine/fiobj_data.h +167 -0
  59. data/ext/iodine/fiobj_hash.c +409 -0
  60. data/ext/iodine/fiobj_hash.h +176 -0
  61. data/ext/iodine/fiobj_json.c +622 -0
  62. data/ext/iodine/fiobj_json.h +68 -0
  63. data/ext/iodine/fiobj_mem.h +71 -0
  64. data/ext/iodine/fiobj_mustache.c +317 -0
  65. data/ext/iodine/fiobj_mustache.h +62 -0
  66. data/ext/iodine/fiobj_numbers.c +344 -0
  67. data/ext/iodine/fiobj_numbers.h +127 -0
  68. data/ext/iodine/fiobj_str.c +433 -0
  69. data/ext/iodine/fiobj_str.h +172 -0
  70. data/ext/iodine/fiobject.c +620 -0
  71. data/ext/iodine/fiobject.h +654 -0
  72. data/ext/iodine/hpack.h +1923 -0
  73. data/ext/iodine/http.c +2736 -0
  74. data/ext/iodine/http.h +1019 -0
  75. data/ext/iodine/http1.c +825 -0
  76. data/ext/iodine/http1.h +29 -0
  77. data/ext/iodine/http1_parser.h +1835 -0
  78. data/ext/iodine/http_internal.c +1279 -0
  79. data/ext/iodine/http_internal.h +248 -0
  80. data/ext/iodine/http_mime_parser.h +350 -0
  81. data/ext/iodine/iodine.c +1433 -0
  82. data/ext/iodine/iodine.h +64 -0
  83. data/ext/iodine/iodine_caller.c +218 -0
  84. data/ext/iodine/iodine_caller.h +27 -0
  85. data/ext/iodine/iodine_connection.c +941 -0
  86. data/ext/iodine/iodine_connection.h +55 -0
  87. data/ext/iodine/iodine_defer.c +420 -0
  88. data/ext/iodine/iodine_defer.h +6 -0
  89. data/ext/iodine/iodine_fiobj2rb.h +120 -0
  90. data/ext/iodine/iodine_helpers.c +282 -0
  91. data/ext/iodine/iodine_helpers.h +12 -0
  92. data/ext/iodine/iodine_http.c +1280 -0
  93. data/ext/iodine/iodine_http.h +23 -0
  94. data/ext/iodine/iodine_json.c +302 -0
  95. data/ext/iodine/iodine_json.h +6 -0
  96. data/ext/iodine/iodine_mustache.c +567 -0
  97. data/ext/iodine/iodine_mustache.h +6 -0
  98. data/ext/iodine/iodine_pubsub.c +580 -0
  99. data/ext/iodine/iodine_pubsub.h +26 -0
  100. data/ext/iodine/iodine_rack_io.c +273 -0
  101. data/ext/iodine/iodine_rack_io.h +20 -0
  102. data/ext/iodine/iodine_store.c +142 -0
  103. data/ext/iodine/iodine_store.h +20 -0
  104. data/ext/iodine/iodine_tcp.c +346 -0
  105. data/ext/iodine/iodine_tcp.h +13 -0
  106. data/ext/iodine/iodine_tls.c +261 -0
  107. data/ext/iodine/iodine_tls.h +13 -0
  108. data/ext/iodine/mustache_parser.h +1546 -0
  109. data/ext/iodine/redis_engine.c +957 -0
  110. data/ext/iodine/redis_engine.h +79 -0
  111. data/ext/iodine/resp_parser.h +317 -0
  112. data/ext/iodine/scheduler.c +173 -0
  113. data/ext/iodine/scheduler.h +6 -0
  114. data/ext/iodine/websocket_parser.h +506 -0
  115. data/ext/iodine/websockets.c +752 -0
  116. data/ext/iodine/websockets.h +185 -0
  117. data/iodine.gemspec +50 -0
  118. data/lib/iodine/connection.rb +61 -0
  119. data/lib/iodine/json.rb +42 -0
  120. data/lib/iodine/mustache.rb +113 -0
  121. data/lib/iodine/pubsub.rb +55 -0
  122. data/lib/iodine/rack_utils.rb +43 -0
  123. data/lib/iodine/tls.rb +16 -0
  124. data/lib/iodine/version.rb +3 -0
  125. data/lib/iodine.rb +274 -0
  126. data/lib/rack/handler/iodine.rb +33 -0
  127. data/logo.png +0 -0
  128. metadata +284 -0
@@ -0,0 +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