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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -2
- data/CHANGELOG.md +22 -0
- data/LIMITS.md +19 -9
- data/README.md +92 -77
- data/SPEC-PubSub-Draft.md +113 -0
- data/SPEC-Websocket-Draft.md +127 -143
- data/bin/http-hello +0 -1
- data/bin/raw-rbhttp +1 -1
- data/bin/raw_broadcast +8 -10
- data/bin/updated api +2 -2
- data/bin/ws-broadcast +2 -4
- data/bin/ws-echo +2 -2
- data/examples/config.ru +13 -13
- data/examples/echo.ru +5 -6
- data/examples/hello.ru +2 -3
- data/examples/info.md +316 -0
- data/examples/pubsub_engine.ru +81 -0
- data/examples/redis.ru +9 -9
- data/examples/shootout.ru +45 -11
- data/ext/iodine/defer.c +194 -297
- data/ext/iodine/defer.h +61 -53
- data/ext/iodine/evio.c +0 -260
- data/ext/iodine/evio.h +50 -22
- data/ext/iodine/evio_callbacks.c +26 -0
- data/ext/iodine/evio_epoll.c +251 -0
- data/ext/iodine/evio_kqueue.c +193 -0
- data/ext/iodine/extconf.rb +1 -1
- data/ext/iodine/facil.c +1420 -542
- data/ext/iodine/facil.h +151 -64
- data/ext/iodine/fio_ary.h +418 -0
- data/ext/iodine/{base64.c → fio_base64.c} +33 -24
- data/ext/iodine/{base64.h → fio_base64.h} +6 -7
- data/ext/iodine/{fio_cli_helper.c → fio_cli.c} +77 -58
- data/ext/iodine/{fio_cli_helper.h → fio_cli.h} +9 -4
- data/ext/iodine/fio_hashmap.h +759 -0
- data/ext/iodine/fio_json_parser.h +651 -0
- data/ext/iodine/fio_llist.h +257 -0
- data/ext/iodine/fio_mem.c +672 -0
- data/ext/iodine/fio_mem.h +140 -0
- data/ext/iodine/fio_random.c +248 -0
- data/ext/iodine/{random.h → fio_random.h} +11 -14
- data/ext/iodine/{sha1.c → fio_sha1.c} +28 -24
- data/ext/iodine/{sha1.h → fio_sha1.h} +38 -16
- data/ext/iodine/{sha2.c → fio_sha2.c} +66 -49
- data/ext/iodine/{sha2.h → fio_sha2.h} +57 -26
- data/ext/iodine/{fiobj_internal.c → fio_siphash.c} +9 -90
- data/ext/iodine/fio_siphash.h +18 -0
- data/ext/iodine/fio_tmpfile.h +38 -0
- data/ext/iodine/fiobj.h +24 -7
- data/ext/iodine/fiobj4sock.h +23 -0
- data/ext/iodine/fiobj_ary.c +143 -226
- data/ext/iodine/fiobj_ary.h +17 -16
- data/ext/iodine/fiobj_data.c +1160 -0
- data/ext/iodine/fiobj_data.h +164 -0
- data/ext/iodine/fiobj_hash.c +298 -406
- data/ext/iodine/fiobj_hash.h +101 -54
- data/ext/iodine/fiobj_json.c +478 -601
- data/ext/iodine/fiobj_json.h +34 -9
- data/ext/iodine/fiobj_numbers.c +383 -51
- data/ext/iodine/fiobj_numbers.h +87 -11
- data/ext/iodine/fiobj_str.c +423 -184
- data/ext/iodine/fiobj_str.h +81 -32
- data/ext/iodine/fiobject.c +273 -522
- data/ext/iodine/fiobject.h +477 -112
- data/ext/iodine/http.c +2243 -83
- data/ext/iodine/http.h +842 -121
- data/ext/iodine/http1.c +810 -385
- data/ext/iodine/http1.h +16 -39
- data/ext/iodine/http1_parser.c +146 -74
- data/ext/iodine/http1_parser.h +15 -4
- data/ext/iodine/http_internal.c +1258 -0
- data/ext/iodine/http_internal.h +226 -0
- data/ext/iodine/http_mime_parser.h +341 -0
- data/ext/iodine/iodine.c +86 -68
- data/ext/iodine/iodine.h +26 -11
- data/ext/iodine/iodine_helpers.c +8 -7
- data/ext/iodine/iodine_http.c +487 -324
- data/ext/iodine/iodine_json.c +304 -0
- data/ext/iodine/iodine_json.h +6 -0
- data/ext/iodine/iodine_protocol.c +107 -45
- data/ext/iodine/iodine_pubsub.c +526 -225
- data/ext/iodine/iodine_pubsub.h +10 -0
- data/ext/iodine/iodine_websockets.c +268 -510
- data/ext/iodine/iodine_websockets.h +2 -4
- data/ext/iodine/pubsub.c +726 -432
- data/ext/iodine/pubsub.h +85 -103
- data/ext/iodine/rb-call.c +4 -4
- data/ext/iodine/rb-defer.c +46 -22
- data/ext/iodine/rb-fiobj2rb.h +117 -0
- data/ext/iodine/rb-rack-io.c +73 -238
- data/ext/iodine/rb-rack-io.h +2 -2
- data/ext/iodine/rb-registry.c +35 -93
- data/ext/iodine/rb-registry.h +1 -0
- data/ext/iodine/redis_engine.c +742 -304
- data/ext/iodine/redis_engine.h +42 -39
- data/ext/iodine/resp_parser.h +311 -0
- data/ext/iodine/sock.c +627 -490
- data/ext/iodine/sock.h +345 -297
- data/ext/iodine/spnlock.inc +15 -4
- data/ext/iodine/websocket_parser.h +16 -20
- data/ext/iodine/websockets.c +188 -257
- data/ext/iodine/websockets.h +24 -133
- data/lib/iodine.rb +52 -7
- data/lib/iodine/cli.rb +6 -24
- data/lib/iodine/json.rb +40 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/iodine/websocket.rb +5 -3
- data/lib/rack/handler/iodine.rb +58 -13
- metadata +38 -48
- data/bin/ws-shootout +0 -107
- data/examples/broadcast.ru +0 -56
- data/ext/iodine/bscrypt-common.h +0 -116
- data/ext/iodine/bscrypt.h +0 -49
- data/ext/iodine/fio2resp.c +0 -60
- data/ext/iodine/fio2resp.h +0 -51
- data/ext/iodine/fio_dict.c +0 -446
- data/ext/iodine/fio_dict.h +0 -99
- data/ext/iodine/fio_hash_table.h +0 -370
- data/ext/iodine/fio_list.h +0 -111
- data/ext/iodine/fiobj_internal.h +0 -280
- data/ext/iodine/fiobj_primitives.c +0 -131
- data/ext/iodine/fiobj_primitives.h +0 -55
- data/ext/iodine/fiobj_sym.c +0 -135
- data/ext/iodine/fiobj_sym.h +0 -60
- data/ext/iodine/hex.c +0 -124
- data/ext/iodine/hex.h +0 -70
- data/ext/iodine/http1_request.c +0 -81
- data/ext/iodine/http1_request.h +0 -58
- data/ext/iodine/http1_response.c +0 -417
- data/ext/iodine/http1_response.h +0 -95
- data/ext/iodine/http_request.c +0 -111
- data/ext/iodine/http_request.h +0 -102
- data/ext/iodine/http_response.c +0 -1703
- data/ext/iodine/http_response.h +0 -250
- data/ext/iodine/misc.c +0 -182
- data/ext/iodine/misc.h +0 -74
- data/ext/iodine/random.c +0 -208
- data/ext/iodine/redis_connection.c +0 -278
- data/ext/iodine/redis_connection.h +0 -86
- data/ext/iodine/resp.c +0 -842
- data/ext/iodine/resp.h +0 -261
- data/ext/iodine/siphash.c +0 -154
- data/ext/iodine/siphash.h +0 -22
- data/ext/iodine/xor-crypt.c +0 -193
- 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
|