iodine 0.4.19 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

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