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
data/ext/iodine/fiobj_hash.h
CHANGED
@@ -1,12 +1,26 @@
|
|
1
|
-
#ifndef H_FIOBJ_HASH_H
|
2
|
-
#define H_FIOBJ_HASH_H
|
3
1
|
/*
|
4
|
-
Copyright: Boaz Segev, 2017
|
2
|
+
Copyright: Boaz Segev, 2017-2018
|
5
3
|
License: MIT
|
6
4
|
*/
|
5
|
+
#ifndef H_FIOBJ_HASH_H
|
6
|
+
/**
|
7
|
+
* The facil.io Hash object is an ordered Hash Table implementation.
|
8
|
+
*
|
9
|
+
* By compromising some of the HashMap's collision resistance (comparing only
|
10
|
+
* the Hash values rather than comparing key data), memory comparison can be
|
11
|
+
* avoided and performance increased.
|
12
|
+
*
|
13
|
+
* By being ordered it's possible to iterate over key-value pairs in the order
|
14
|
+
* in which they were added to the Hash table, making it possible to output JSON
|
15
|
+
* in a controlled manner.
|
16
|
+
*/
|
17
|
+
#define H_FIOBJ_HASH_H
|
7
18
|
|
19
|
+
#include "fiobject.h"
|
20
|
+
|
21
|
+
#include "fio_siphash.h"
|
8
22
|
#include "fiobj_str.h"
|
9
|
-
#include
|
23
|
+
#include <errno.h>
|
10
24
|
|
11
25
|
#ifdef __cplusplus
|
12
26
|
extern "C" {
|
@@ -15,111 +29,144 @@ extern "C" {
|
|
15
29
|
/* MUST be a power of 2 */
|
16
30
|
#define HASH_INITIAL_CAPACITY 16
|
17
31
|
|
18
|
-
#include <errno.h>
|
19
|
-
|
20
32
|
/** attempts to rehash the hashmap. */
|
21
|
-
void fiobj_hash_rehash(
|
33
|
+
void fiobj_hash_rehash(FIOBJ h);
|
22
34
|
|
23
35
|
/* *****************************************************************************
|
24
|
-
|
36
|
+
Hash Creation
|
25
37
|
***************************************************************************** */
|
26
38
|
|
27
|
-
/** Couplet type identifier. */
|
28
|
-
extern const uintptr_t FIOBJ_T_COUPLET;
|
29
|
-
|
30
39
|
/**
|
31
|
-
*
|
32
|
-
* (Symbol) from the key-value pair.
|
40
|
+
* Creates a mutable empty Hash object. Use `fiobj_free` when done.
|
33
41
|
*
|
34
|
-
*
|
42
|
+
* Notice that these Hash objects are optimized for smaller collections and
|
43
|
+
* retain order of object insertion.
|
35
44
|
*/
|
36
|
-
|
45
|
+
FIOBJ fiobj_hash_new(void);
|
37
46
|
|
38
47
|
/**
|
39
|
-
*
|
40
|
-
*
|
48
|
+
* Creates a mutable empty Hash object with an initial capacity of `capa`. Use
|
49
|
+
* `fiobj_free` when done.
|
41
50
|
*
|
42
|
-
*
|
51
|
+
* This allows optimizations for larger (or smaller) collections.
|
43
52
|
*/
|
44
|
-
|
53
|
+
FIOBJ fiobj_hash_new2(size_t capa);
|
45
54
|
|
46
55
|
/* *****************************************************************************
|
47
|
-
Hash
|
56
|
+
Hash properties and state
|
48
57
|
***************************************************************************** */
|
49
58
|
|
50
|
-
/**
|
59
|
+
/**
|
60
|
+
* Returns a temporary theoretical Hash map capacity.
|
61
|
+
* This could be used for testig performance and memory consumption.
|
62
|
+
*/
|
63
|
+
size_t fiobj_hash_capa(const FIOBJ hash);
|
51
64
|
|
52
|
-
|
53
|
-
|
65
|
+
/** Returns the number of elements in the Hash. */
|
66
|
+
size_t fiobj_hash_count(const FIOBJ hash);
|
54
67
|
|
55
|
-
|
56
|
-
|
68
|
+
/** Returns the key for the object in the current `fiobj_each` loop (if any). */
|
69
|
+
FIOBJ fiobj_hash_key_in_loop(void);
|
57
70
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
*/
|
62
|
-
extern const uintptr_t FIOBJ_T_HASH;
|
71
|
+
/* *****************************************************************************
|
72
|
+
Populating the Hash
|
73
|
+
***************************************************************************** */
|
63
74
|
|
64
75
|
/**
|
65
|
-
*
|
76
|
+
* Sets a key-value pair in the Hash, duplicating the Symbol and **moving**
|
77
|
+
* the ownership of the object to the Hash.
|
66
78
|
*
|
67
|
-
*
|
68
|
-
* retain order of object insertion.
|
79
|
+
* Returns -1 on error.
|
69
80
|
*/
|
70
|
-
|
81
|
+
int fiobj_hash_set(FIOBJ hash, FIOBJ key, FIOBJ obj);
|
71
82
|
|
72
|
-
/**
|
73
|
-
|
83
|
+
/**
|
84
|
+
* Allows the Hash to be used as a stack.
|
85
|
+
*
|
86
|
+
* If a pointer `key` is provided, it will receive ownership of the key
|
87
|
+
* (remember to free).
|
88
|
+
*
|
89
|
+
* Returns FIOBJ_INVALID on error.
|
90
|
+
*
|
91
|
+
* Returns and object if successful (remember to free).
|
92
|
+
*/
|
93
|
+
FIOBJ fiobj_hash_pop(FIOBJ hash, FIOBJ *key);
|
74
94
|
|
75
95
|
/**
|
76
|
-
*
|
77
|
-
*
|
96
|
+
* Replaces the value in a key-value pair, returning the old value (and it's
|
97
|
+
* ownership) to the caller.
|
78
98
|
*
|
79
|
-
*
|
99
|
+
* A return value of FIOBJ_INVALID indicates that no previous object existed
|
100
|
+
* (but a new key-value pair was created.
|
101
|
+
*
|
102
|
+
* Errors are silently ignored.
|
103
|
+
*
|
104
|
+
* Remember to free the returned object.
|
80
105
|
*/
|
81
|
-
|
106
|
+
FIOBJ fiobj_hash_replace(FIOBJ hash, FIOBJ key, FIOBJ obj);
|
82
107
|
|
83
108
|
/**
|
84
109
|
* Removes a key-value pair from the Hash, if it exists, returning the old
|
85
110
|
* object (instead of freeing it).
|
86
111
|
*/
|
87
|
-
|
112
|
+
FIOBJ fiobj_hash_remove(FIOBJ hash, FIOBJ key);
|
113
|
+
|
114
|
+
/**
|
115
|
+
* Removes a key-value pair from the Hash, if it exists, returning the old
|
116
|
+
* object (instead of freeing it).
|
117
|
+
*/
|
118
|
+
FIOBJ fiobj_hash_remove2(FIOBJ hash, uint64_t key_hash);
|
119
|
+
|
120
|
+
/**
|
121
|
+
* Deletes a key-value pair from the Hash, if it exists, freeing the
|
122
|
+
* associated object.
|
123
|
+
*
|
124
|
+
* Returns -1 on type error or if the object never existed.
|
125
|
+
*/
|
126
|
+
int fiobj_hash_delete(FIOBJ hash, FIOBJ key);
|
88
127
|
|
89
128
|
/**
|
90
129
|
* Deletes a key-value pair from the Hash, if it exists, freeing the
|
91
130
|
* associated object.
|
92
131
|
*
|
132
|
+
* This function takes a `uint64_t` Hash value (see `fio_siphash`) to
|
133
|
+
* perform a lookup in the HashMap, which is slightly faster than the other
|
134
|
+
* variations.
|
135
|
+
*
|
93
136
|
* Returns -1 on type error or if the object never existed.
|
94
137
|
*/
|
95
|
-
int
|
138
|
+
int fiobj_hash_delete2(FIOBJ hash, uint64_t key_hash);
|
96
139
|
|
97
140
|
/**
|
98
|
-
* Returns a temporary handle to the object associated with the Symbol,
|
99
|
-
* if none.
|
141
|
+
* Returns a temporary handle to the object associated with the Symbol,
|
142
|
+
* FIOBJ_INVALID if none.
|
100
143
|
*/
|
101
|
-
|
144
|
+
FIOBJ fiobj_hash_get(const FIOBJ hash, FIOBJ key);
|
102
145
|
|
103
146
|
/**
|
104
|
-
* Returns a temporary handle to the object associated
|
147
|
+
* Returns a temporary handle to the object associated hashed key value.
|
105
148
|
*
|
106
|
-
* This function takes a
|
107
|
-
*
|
149
|
+
* This function takes a `uint64_t` Hash value (see `fio_siphash`) to
|
150
|
+
* perform a lookup in the HashMap, which is slightly faster than the other
|
151
|
+
* variations.
|
108
152
|
*
|
109
|
-
* Returns
|
153
|
+
* Returns FIOBJ_INVALID if no object is asociated with this hashed key value.
|
110
154
|
*/
|
111
|
-
|
155
|
+
FIOBJ fiobj_hash_get2(const FIOBJ hash, uint64_t key_hash);
|
112
156
|
|
113
157
|
/**
|
114
158
|
* Returns 1 if the key (Symbol) exists in the Hash, even if it's value is NULL.
|
115
159
|
*/
|
116
|
-
int fiobj_hash_haskey(const
|
160
|
+
int fiobj_hash_haskey(const FIOBJ hash, FIOBJ key);
|
117
161
|
|
118
162
|
/**
|
119
|
-
*
|
120
|
-
* This could be used for testig performance and memory consumption.
|
163
|
+
* Empties the Hash.
|
121
164
|
*/
|
122
|
-
|
165
|
+
void fiobj_hash_clear(const FIOBJ hash);
|
166
|
+
|
167
|
+
#if DEBUG
|
168
|
+
void fiobj_test_hash(void);
|
169
|
+
#endif
|
123
170
|
|
124
171
|
#ifdef __cplusplus
|
125
172
|
} /* extern "C" */
|
data/ext/iodine/fiobj_json.c
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
/*
|
2
|
-
Copyright: Boaz Segev, 2017
|
2
|
+
Copyright: Boaz Segev, 2017-2018
|
3
3
|
License: MIT
|
4
4
|
*/
|
5
5
|
#include "fiobj_json.h"
|
6
|
+
#include "fio_json_parser.h"
|
6
7
|
|
7
|
-
|
8
|
+
#include "fio_ary.h"
|
9
|
+
|
10
|
+
#include <assert.h>
|
8
11
|
#include <ctype.h>
|
9
12
|
#include <math.h>
|
10
13
|
#include <stdlib.h>
|
@@ -13,81 +16,158 @@ License: MIT
|
|
13
16
|
/* *****************************************************************************
|
14
17
|
JSON API
|
15
18
|
***************************************************************************** */
|
16
|
-
|
19
|
+
|
17
20
|
/**
|
18
21
|
* Parses JSON, setting `pobj` to point to the new Object.
|
19
22
|
*
|
20
23
|
* Returns the number of bytes consumed. On Error, 0 is returned and no data is
|
21
24
|
* consumed.
|
22
25
|
*/
|
23
|
-
size_t fiobj_json2obj(
|
26
|
+
size_t fiobj_json2obj(FIOBJ *pobj, const void *data, size_t len);
|
24
27
|
/* Formats an object into a JSON string. Remember to `fiobj_free`. */
|
25
|
-
|
28
|
+
FIOBJ fiobj_obj2json(FIOBJ, uint8_t);
|
29
|
+
|
30
|
+
/* *****************************************************************************
|
31
|
+
FIOBJ Parser
|
32
|
+
***************************************************************************** */
|
33
|
+
|
34
|
+
typedef struct {
|
35
|
+
json_parser_s p;
|
36
|
+
FIOBJ key;
|
37
|
+
FIOBJ top;
|
38
|
+
FIOBJ target;
|
39
|
+
fio_ary_s stack;
|
40
|
+
uint8_t is_hash;
|
41
|
+
} fiobj_json_parser_s;
|
26
42
|
|
27
43
|
/* *****************************************************************************
|
28
|
-
|
44
|
+
FIOBJ Callacks
|
29
45
|
***************************************************************************** */
|
30
46
|
|
31
|
-
static
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
static
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
47
|
+
static inline void fiobj_json_add2parser(fiobj_json_parser_s *p, FIOBJ o) {
|
48
|
+
if (p->top) {
|
49
|
+
if (p->is_hash) {
|
50
|
+
if (p->key) {
|
51
|
+
fiobj_hash_set(p->top, p->key, o);
|
52
|
+
fiobj_free(p->key);
|
53
|
+
p->key = FIOBJ_INVALID;
|
54
|
+
} else {
|
55
|
+
p->key = o;
|
56
|
+
}
|
57
|
+
} else {
|
58
|
+
fiobj_ary_push(p->top, o);
|
59
|
+
}
|
60
|
+
} else {
|
61
|
+
p->top = o;
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
/** a NULL object was detected */
|
66
|
+
static void fio_json_on_null(json_parser_s *p) {
|
67
|
+
fiobj_json_add2parser((fiobj_json_parser_s *)p, fiobj_null());
|
68
|
+
}
|
69
|
+
/** a TRUE object was detected */
|
70
|
+
static void fio_json_on_true(json_parser_s *p) {
|
71
|
+
fiobj_json_add2parser((fiobj_json_parser_s *)p, fiobj_true());
|
72
|
+
}
|
73
|
+
/** a FALSE object was detected */
|
74
|
+
static void fio_json_on_false(json_parser_s *p) {
|
75
|
+
fiobj_json_add2parser((fiobj_json_parser_s *)p, fiobj_false());
|
76
|
+
}
|
77
|
+
/** a Numberl was detected (long long). */
|
78
|
+
static void fio_json_on_number(json_parser_s *p, long long i) {
|
79
|
+
fiobj_json_add2parser((fiobj_json_parser_s *)p, fiobj_num_new(i));
|
80
|
+
}
|
81
|
+
/** a Float was detected (double). */
|
82
|
+
static void fio_json_on_float(json_parser_s *p, double f) {
|
83
|
+
fiobj_json_add2parser((fiobj_json_parser_s *)p, fiobj_float_new(f));
|
84
|
+
}
|
85
|
+
/** a String was detected (int / float). update `pos` to point at ending */
|
86
|
+
static void fio_json_on_string(json_parser_s *p, void *start, size_t length) {
|
87
|
+
FIOBJ str = fiobj_str_buf(length);
|
88
|
+
fiobj_str_resize(
|
89
|
+
str, fio_json_unescape_str(fiobj_obj2cstr(str).data, start, length));
|
90
|
+
fiobj_json_add2parser((fiobj_json_parser_s *)p, str);
|
91
|
+
}
|
92
|
+
/** a dictionary object was detected */
|
93
|
+
static int fio_json_on_start_object(json_parser_s *p) {
|
94
|
+
fiobj_json_parser_s *pr = (fiobj_json_parser_s *)p;
|
95
|
+
if (pr->target) {
|
96
|
+
/* push NULL, don't free the objects */
|
97
|
+
fio_ary_push(&pr->stack, (void *)pr->top);
|
98
|
+
pr->top = pr->target;
|
99
|
+
pr->target = FIOBJ_INVALID;
|
100
|
+
} else {
|
101
|
+
FIOBJ hash = fiobj_hash_new();
|
102
|
+
fiobj_json_add2parser(pr, hash);
|
103
|
+
fio_ary_push(&pr->stack, (void *)pr->top);
|
104
|
+
pr->top = hash;
|
105
|
+
}
|
106
|
+
pr->is_hash = 1;
|
107
|
+
return 0;
|
108
|
+
}
|
109
|
+
/** a dictionary object closure detected */
|
110
|
+
static void fio_json_on_end_object(json_parser_s *p) {
|
111
|
+
fiobj_json_parser_s *pr = (fiobj_json_parser_s *)p;
|
112
|
+
if (pr->key) {
|
113
|
+
fprintf(stderr, "WARNING: (JSON parsing) malformed JSON, "
|
114
|
+
"ignoring dangling Hash key.\n");
|
115
|
+
fiobj_free(pr->key);
|
116
|
+
pr->key = FIOBJ_INVALID;
|
79
117
|
}
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
118
|
+
pr->top = (FIOBJ)fio_ary_pop(&pr->stack);
|
119
|
+
pr->is_hash = FIOBJ_TYPE_IS(pr->top, FIOBJ_T_HASH);
|
120
|
+
}
|
121
|
+
/** an array object was detected */
|
122
|
+
static int fio_json_on_start_array(json_parser_s *p) {
|
123
|
+
fiobj_json_parser_s *pr = (fiobj_json_parser_s *)p;
|
124
|
+
if (pr->target)
|
125
|
+
return -1;
|
126
|
+
FIOBJ ary = fiobj_ary_new2(4);
|
127
|
+
fiobj_json_add2parser(pr, ary);
|
128
|
+
fio_ary_push(&pr->stack, (void *)pr->top);
|
129
|
+
pr->top = ary;
|
130
|
+
pr->is_hash = 0;
|
131
|
+
return 0;
|
132
|
+
}
|
133
|
+
/** an array closure was detected */
|
134
|
+
static void fio_json_on_end_array(json_parser_s *p) {
|
135
|
+
fiobj_json_parser_s *pr = (fiobj_json_parser_s *)p;
|
136
|
+
pr->top = (FIOBJ)fio_ary_pop(&pr->stack);
|
137
|
+
pr->is_hash = FIOBJ_TYPE_IS(pr->top, FIOBJ_T_HASH);
|
138
|
+
}
|
139
|
+
/** the JSON parsing is complete */
|
140
|
+
static void fio_json_on_json(json_parser_s *p) {
|
141
|
+
// fiobj_json_parser_s *pr = (fiobj_json_parser_s *)p;
|
142
|
+
// FIO_ARY_FOR(&pr->stack, pos) { fiobj_free((FIOBJ)pos.obj); }
|
143
|
+
// fio_ary_free(&pr->stack);
|
144
|
+
(void)p; /* nothing special... right? */
|
85
145
|
}
|
146
|
+
/** the JSON parsing is complete */
|
147
|
+
static void fio_json_on_error(json_parser_s *p) {
|
148
|
+
fiobj_json_parser_s *pr = (fiobj_json_parser_s *)p;
|
149
|
+
#if DEBUG
|
150
|
+
fprintf(stderr, "ERROR: JSON on error called.\n");
|
151
|
+
#endif
|
152
|
+
fiobj_free((FIOBJ)fio_ary_index(&pr->stack, 0));
|
153
|
+
fiobj_free(pr->key);
|
154
|
+
fio_ary_free(&pr->stack);
|
155
|
+
pr->stack = FIO_ARY_INIT;
|
156
|
+
*pr = (fiobj_json_parser_s){.top = FIOBJ_INVALID};
|
157
|
+
}
|
158
|
+
|
159
|
+
/* *****************************************************************************
|
160
|
+
JSON formatting
|
161
|
+
***************************************************************************** */
|
86
162
|
|
87
163
|
/** Writes a JSON friendly version of the src String */
|
88
|
-
static void write_safe_str(
|
164
|
+
static void write_safe_str(FIOBJ dest, const FIOBJ str) {
|
89
165
|
fio_cstr_s s = fiobj_obj2cstr(str);
|
90
166
|
fio_cstr_s t = fiobj_obj2cstr(dest);
|
167
|
+
t.data[t.len] = '"';
|
168
|
+
t.len++;
|
169
|
+
fiobj_str_resize(dest, t.len);
|
170
|
+
t = fiobj_obj2cstr(dest);
|
91
171
|
const uint8_t *restrict src = (const uint8_t *)s.data;
|
92
172
|
size_t len = s.len;
|
93
173
|
uint64_t end = t.len;
|
@@ -95,15 +175,18 @@ static void write_safe_str(fiobj_s *dest, const fiobj_s *str) {
|
|
95
175
|
size_t added = 0;
|
96
176
|
size_t capa = fiobj_str_capa(dest);
|
97
177
|
if (capa <= end + s.len + 64) {
|
98
|
-
|
99
|
-
|
178
|
+
if (0) {
|
179
|
+
capa = (((capa >> 12) + 1) << 12) - 1;
|
180
|
+
capa = fiobj_str_capa_assert(dest, capa);
|
181
|
+
} else {
|
182
|
+
capa = fiobj_str_capa_assert(dest, (end + s.len + 64));
|
183
|
+
}
|
100
184
|
fio_cstr_s tmp = fiobj_obj2cstr(dest);
|
101
185
|
t = tmp;
|
102
186
|
}
|
103
187
|
while (len) {
|
104
188
|
char *restrict writer = (char *)t.data;
|
105
|
-
while (len &&
|
106
|
-
(src[0] > 32 && src[0] != '"' && src[0] != '\\' && src[0] != '/')) {
|
189
|
+
while (len && (src[0] > 32 && src[0] != '"' && src[0] != '\\')) {
|
107
190
|
len--;
|
108
191
|
writer[end++] = *(src++);
|
109
192
|
}
|
@@ -159,575 +242,369 @@ static void write_safe_str(fiobj_s *dest, const fiobj_s *str) {
|
|
159
242
|
src++;
|
160
243
|
len--;
|
161
244
|
if (added >= 48 && capa <= end + len + 64) {
|
162
|
-
|
163
|
-
|
245
|
+
if (0) {
|
246
|
+
capa = (((capa >> 12) + 1) << 12) - 1;
|
247
|
+
capa = fiobj_str_capa_assert(dest, capa);
|
248
|
+
} else {
|
249
|
+
capa = fiobj_str_capa_assert(dest, (end + len + 64));
|
250
|
+
}
|
164
251
|
t = fiobj_obj2cstr(dest);
|
165
252
|
added = 0;
|
166
253
|
}
|
167
254
|
}
|
255
|
+
t.data[end++] = '"';
|
168
256
|
fiobj_str_resize(dest, end);
|
169
257
|
}
|
170
258
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
uint8_t
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
struct fiobj_str_new_json_data_s *data = d_;
|
186
|
-
if (data->count && fiobj_obj2num(data->count))
|
187
|
-
fiobj_num_set(data->count, fiobj_obj2num(data->count) - 1);
|
188
|
-
/* headroom */
|
189
|
-
fiobj_str_capa_assert(
|
190
|
-
data->buffer,
|
191
|
-
((((fiobj_obj2cstr(data->buffer).len + 63) >> 12) + 1) << 12) - 1);
|
192
|
-
pretty_re_rooted:
|
193
|
-
/* pretty? */
|
194
|
-
if (data->pretty) {
|
195
|
-
fiobj_str_write(data->buffer, "\n", 1);
|
196
|
-
for (size_t i = 0; i < fiobj_ary_count(data->parent); i++) {
|
197
|
-
fiobj_str_write(data->buffer, " ", 2);
|
198
|
-
}
|
259
|
+
typedef struct {
|
260
|
+
FIOBJ dest;
|
261
|
+
FIOBJ parent;
|
262
|
+
fio_ary_s *stack;
|
263
|
+
uintptr_t count;
|
264
|
+
uint8_t pretty;
|
265
|
+
} obj2json_data_s;
|
266
|
+
|
267
|
+
static int fiobj_fiobj_obj2json_task(FIOBJ o, void *data_) {
|
268
|
+
obj2json_data_s *data = data_;
|
269
|
+
uint8_t add_seperator = 1;
|
270
|
+
if (fiobj_hash_key_in_loop()) {
|
271
|
+
write_safe_str(data->dest, fiobj_hash_key_in_loop());
|
272
|
+
fiobj_str_write(data->dest, ":", 1);
|
199
273
|
}
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
274
|
+
switch (FIOBJ_TYPE(o)) {
|
275
|
+
case FIOBJ_T_NUMBER:
|
276
|
+
case FIOBJ_T_NULL:
|
277
|
+
case FIOBJ_T_TRUE:
|
278
|
+
case FIOBJ_T_FALSE:
|
279
|
+
case FIOBJ_T_FLOAT:
|
280
|
+
fiobj_str_join(data->dest, o);
|
281
|
+
--data->count;
|
282
|
+
break;
|
283
|
+
|
284
|
+
case FIOBJ_T_DATA:
|
285
|
+
case FIOBJ_T_UNKNOWN:
|
286
|
+
case FIOBJ_T_STRING:
|
287
|
+
write_safe_str(data->dest, o);
|
288
|
+
--data->count;
|
289
|
+
break;
|
290
|
+
|
291
|
+
case FIOBJ_T_ARRAY:
|
292
|
+
--data->count;
|
293
|
+
fio_ary_push(data->stack, (void *)data->parent);
|
294
|
+
fio_ary_push(data->stack, (void *)data->count);
|
295
|
+
data->parent = o;
|
296
|
+
data->count = fiobj_ary_count(o);
|
297
|
+
fiobj_str_write(data->dest, "[", 1);
|
298
|
+
add_seperator = 0;
|
299
|
+
break;
|
300
|
+
|
301
|
+
case FIOBJ_T_HASH:
|
302
|
+
--data->count;
|
303
|
+
fio_ary_push(data->stack, (void *)data->parent);
|
304
|
+
fio_ary_push(data->stack, (void *)data->count);
|
305
|
+
data->parent = o;
|
306
|
+
data->count = fiobj_hash_count(o);
|
307
|
+
fiobj_str_write(data->dest, "{", 1);
|
308
|
+
add_seperator = 0;
|
309
|
+
break;
|
204
310
|
}
|
205
|
-
if (
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
fiobj_ary_push(data->parent, obj);
|
214
|
-
fiobj_ary_push(data->waiting, data->count);
|
215
|
-
data->count = fiobj_num_new(fiobj_ary_count(obj));
|
216
|
-
} else if (obj->type == FIOBJ_T_SYMBOL || FIOBJ_IS_STRING(obj)) {
|
217
|
-
fiobj_str_capa_assert(
|
218
|
-
data->buffer,
|
219
|
-
((((fiobj_obj2cstr(data->buffer).len + 63 + fiobj_obj2cstr(obj).len) >>
|
220
|
-
12) +
|
221
|
-
1)
|
222
|
-
<< 12) -
|
223
|
-
1);
|
224
|
-
fiobj_str_write(data->buffer, "\"", 1);
|
225
|
-
write_safe_str(data->buffer, obj);
|
226
|
-
fiobj_str_write(data->buffer, "\"", 1);
|
227
|
-
} else if (obj->type == FIOBJ_T_COUPLET) {
|
228
|
-
fiobj_str_capa_assert(data->buffer,
|
229
|
-
((((fiobj_obj2cstr(data->buffer).len + 31 +
|
230
|
-
fiobj_obj2cstr(fiobj_couplet2key(obj)).len) >>
|
231
|
-
12) +
|
232
|
-
1)
|
233
|
-
<< 12) -
|
234
|
-
1);
|
235
|
-
fiobj_str_write(data->buffer, "\"", 1);
|
236
|
-
write_safe_str(data->buffer, fiobj_couplet2key(obj));
|
237
|
-
fiobj_str_write(data->buffer, "\":", 2);
|
238
|
-
obj = fiobj_couplet2obj(obj);
|
239
|
-
if (data->pretty &&
|
240
|
-
(obj->type == FIOBJ_T_ARRAY || obj->type == FIOBJ_T_HASH))
|
241
|
-
goto pretty_re_rooted;
|
242
|
-
goto re_rooted;
|
243
|
-
} else if (obj->type == FIOBJ_T_NUMBER || obj->type == FIOBJ_T_FLOAT) {
|
244
|
-
fio_cstr_s i2s = fiobj_obj2cstr(obj);
|
245
|
-
fiobj_str_write(data->buffer, i2s.data, i2s.len);
|
246
|
-
} else if (obj->type == FIOBJ_T_NULL) {
|
247
|
-
fiobj_str_write(data->buffer, "null", 4);
|
248
|
-
} else if (fiobj_is_true(obj)) {
|
249
|
-
fiobj_str_write(data->buffer, "true", 4);
|
250
|
-
} else
|
251
|
-
fiobj_str_write(data->buffer, "false", 5);
|
252
|
-
|
253
|
-
review_nesting:
|
254
|
-
/* print clousure to String */
|
255
|
-
while (!fiobj_obj2num(data->count)) {
|
256
|
-
fiobj_s *tmp = fiobj_ary_pop(data->parent);
|
257
|
-
if (!tmp)
|
258
|
-
break;
|
259
|
-
fiobj_free(data->count);
|
260
|
-
data->count = fiobj_ary_pop(data->waiting);
|
261
|
-
if (data->pretty) {
|
262
|
-
fiobj_str_write(data->buffer, "\n", 1);
|
263
|
-
for (size_t i = 0; i < fiobj_ary_count(data->parent); i++) {
|
264
|
-
fiobj_str_write(data->buffer, " ", 2);
|
311
|
+
if (data->pretty) {
|
312
|
+
fiobj_str_capa_assert(data->dest, fiobj_obj2cstr(data->dest).len +
|
313
|
+
(fio_ary_count(data->stack) * 5));
|
314
|
+
while (!data->count && data->parent) {
|
315
|
+
if (FIOBJ_TYPE_IS(data->parent, FIOBJ_T_HASH)) {
|
316
|
+
fiobj_str_write(data->dest, "}", 1);
|
317
|
+
} else {
|
318
|
+
fiobj_str_write(data->dest, "]", 1);
|
265
319
|
}
|
320
|
+
add_seperator = 1;
|
321
|
+
data->count = (uintptr_t)fio_ary_pop(data->stack);
|
322
|
+
data->parent = (FIOBJ)fio_ary_pop(data->stack);
|
266
323
|
}
|
267
|
-
if (tmp->type == FIOBJ_T_ARRAY)
|
268
|
-
fiobj_str_write(data->buffer, "]", 1);
|
269
|
-
else
|
270
|
-
fiobj_str_write(data->buffer, "}", 1);
|
271
|
-
}
|
272
|
-
/* print object divisions to String */
|
273
|
-
if (data->count && fiobj_obj2num(data->count) &&
|
274
|
-
(!obj || (obj->type != FIOBJ_T_ARRAY && obj->type != FIOBJ_T_HASH)))
|
275
|
-
fiobj_str_write(data->buffer, ",", 1);
|
276
|
-
return 0;
|
277
|
-
}
|
278
|
-
|
279
|
-
/* Formats an object into a JSON string. Remember to `fiobj_free`. */
|
280
|
-
fiobj_s *fiobj_obj2json(fiobj_s *obj, uint8_t pretty) {
|
281
|
-
/* Using a whole page size could optimize future allocations (no copy) */
|
282
|
-
struct fiobj_str_new_json_data_s data = {
|
283
|
-
.parent = fiobj_ary_new(),
|
284
|
-
.waiting = fiobj_ary_new(),
|
285
|
-
.buffer = fiobj_str_buf(0),
|
286
|
-
.count = NULL,
|
287
|
-
.pretty = pretty,
|
288
|
-
};
|
289
|
-
fiobj_each2(obj, fiobj_str_new_json_task, &data);
|
290
|
-
|
291
|
-
while (fiobj_ary_pop(data.parent))
|
292
|
-
; /* we didn't duplicate the objects, so we must remove them from array */
|
293
|
-
fiobj_free(data.parent);
|
294
|
-
fiobj_free(data.waiting);
|
295
|
-
fiobj_str_minimize(data.buffer);
|
296
|
-
return data.buffer;
|
297
|
-
}
|
298
|
-
|
299
|
-
/* *****************************************************************************
|
300
|
-
JSON parsing
|
301
|
-
*****************************************************************************
|
302
|
-
*/
|
303
|
-
|
304
|
-
/*
|
305
|
-
Marks as object seperators any of the following:
|
306
|
-
|
307
|
-
* White Space: [0x09, 0x0A, 0x0D, 0x20]
|
308
|
-
* Comma ("," / 0x2C)
|
309
|
-
* Colon (":" / 0x3A)
|
310
|
-
The rest belong to objects,
|
311
|
-
*/
|
312
|
-
static const uint8_t JSON_SEPERATOR[] = {
|
313
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
314
|
-
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
|
315
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
316
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
317
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
318
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
319
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
320
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
321
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
322
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
323
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
324
|
-
};
|
325
|
-
|
326
|
-
inline static void move_to_end(const uint8_t **pos, const uint8_t *limit) {
|
327
|
-
while (*pos < limit && JSON_SEPERATOR[**pos] == 0)
|
328
|
-
(*pos)++;
|
329
|
-
}
|
330
|
-
inline static void move_to_start(const uint8_t **pos, const uint8_t *limit) {
|
331
|
-
while (*pos < limit && JSON_SEPERATOR[**pos])
|
332
|
-
(*pos)++;
|
333
|
-
}
|
334
|
-
inline static uint8_t move_to_eol(const uint8_t **pos, const uint8_t *limit) {
|
335
|
-
/* single char lookup using library is best when target is far... */
|
336
|
-
if (*pos >= limit)
|
337
|
-
return 0;
|
338
|
-
if (**pos == '\n')
|
339
|
-
return 1;
|
340
|
-
void *tmp = memchr(*pos, '\n', limit - (*pos));
|
341
|
-
if (tmp) {
|
342
|
-
*pos = tmp;
|
343
|
-
return 1;
|
344
|
-
}
|
345
|
-
*pos = limit;
|
346
|
-
return 0;
|
347
|
-
}
|
348
|
-
|
349
|
-
inline static int move_to_quote(const uint8_t **pos, const uint8_t *limit) {
|
350
|
-
if (**pos == '\\')
|
351
|
-
return 1;
|
352
|
-
if (**pos == '\"')
|
353
|
-
return 0;
|
354
|
-
uint64_t wanted1 = 0x0101010101010101ULL * '"';
|
355
|
-
uint64_t wanted2 = 0x0101010101010101ULL * '\\';
|
356
|
-
uint64_t *lpos = (uint64_t *)*pos;
|
357
|
-
uint64_t *llimit = ((uint64_t *)limit) - 1;
|
358
|
-
|
359
|
-
for (; lpos < llimit; ++lpos) {
|
360
|
-
const uint64_t eq1 = ~((*lpos) ^ wanted1);
|
361
|
-
const uint64_t t0 = (eq1 & 0x7f7f7f7f7f7f7f7fllu) + 0x0101010101010101llu;
|
362
|
-
const uint64_t t1 = (eq1 & 0x8080808080808080llu);
|
363
|
-
const uint64_t eq2 = ~((*lpos) ^ wanted2);
|
364
|
-
const uint64_t t2 = (eq2 & 0x7f7f7f7f7f7f7f7fllu) + 0x0101010101010101llu;
|
365
|
-
const uint64_t t3 = (eq2 & 0x8080808080808080llu);
|
366
|
-
if ((t0 & t1) || (t2 & t3))
|
367
|
-
break;
|
368
|
-
}
|
369
|
-
*pos = (uint8_t *)lpos;
|
370
324
|
|
371
|
-
|
372
|
-
|
373
|
-
|
325
|
+
if (add_seperator && data->parent) {
|
326
|
+
fiobj_str_write(data->dest, ",\n", 2);
|
327
|
+
uintptr_t indent = fio_ary_count(data->stack) - 1;
|
328
|
+
fiobj_str_capa_assert(data->dest,
|
329
|
+
fiobj_obj2cstr(data->dest).len + (indent * 2));
|
330
|
+
fio_cstr_s buf = fiobj_obj2cstr(data->dest);
|
331
|
+
while (indent--) {
|
332
|
+
buf.bytes[buf.len++] = ' ';
|
333
|
+
buf.bytes[buf.len++] = ' ';
|
334
|
+
}
|
335
|
+
fiobj_str_resize(data->dest, buf.len);
|
336
|
+
}
|
337
|
+
} else {
|
338
|
+
fiobj_str_capa_assert(data->dest, fiobj_obj2cstr(data->dest).len +
|
339
|
+
(fio_ary_count(data->stack) << 1));
|
340
|
+
while (!data->count && data->parent) {
|
341
|
+
if (FIOBJ_TYPE_IS(data->parent, FIOBJ_T_HASH)) {
|
342
|
+
fiobj_str_write(data->dest, "}", 1);
|
343
|
+
} else {
|
344
|
+
fiobj_str_write(data->dest, "]", 1);
|
345
|
+
}
|
346
|
+
add_seperator = 1;
|
347
|
+
data->count = (uintptr_t)fio_ary_pop(data->stack);
|
348
|
+
data->parent = (FIOBJ)fio_ary_pop(data->stack);
|
374
349
|
}
|
375
|
-
|
376
|
-
|
350
|
+
|
351
|
+
if (add_seperator && data->parent) {
|
352
|
+
fiobj_str_write(data->dest, ",", 1);
|
377
353
|
}
|
378
|
-
(*pos)++;
|
379
354
|
}
|
355
|
+
|
380
356
|
return 0;
|
381
357
|
}
|
382
358
|
|
383
359
|
/* *****************************************************************************
|
384
|
-
|
385
|
-
*****************************************************************************
|
386
|
-
*/
|
360
|
+
FIOBJ API
|
361
|
+
***************************************************************************** */
|
387
362
|
|
388
|
-
|
363
|
+
/**
|
364
|
+
* Parses JSON, setting `pobj` to point to the new Object.
|
389
365
|
*
|
390
|
-
*
|
366
|
+
* Returns the number of bytes consumed. On Error, 0 is returned and no data is
|
367
|
+
* consumed.
|
391
368
|
*/
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
369
|
+
size_t fiobj_json2obj(FIOBJ *pobj, const void *data, size_t len) {
|
370
|
+
fiobj_json_parser_s p = {.top = FIOBJ_INVALID};
|
371
|
+
size_t consumed = fio_json_parse(&p.p, data, len);
|
372
|
+
if (!consumed || p.p.depth) {
|
373
|
+
fiobj_free((FIOBJ)fio_ary_index(&p.stack, 0));
|
374
|
+
p.top = FIOBJ_INVALID;
|
397
375
|
}
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
uint8_t *writer = (uint8_t *)s.bytes;
|
403
|
-
while (reader < end) {
|
404
|
-
while (reader < end && reader[0] != '\\') {
|
405
|
-
*(writer++) = *(reader++);
|
406
|
-
}
|
407
|
-
if (reader[0] != '\\')
|
408
|
-
break;
|
409
|
-
|
410
|
-
// had_changed = 1;
|
411
|
-
switch (reader[1]) {
|
412
|
-
case 'b':
|
413
|
-
*(writer++) = '\b';
|
414
|
-
reader += 2;
|
415
|
-
break; /* from switch */
|
416
|
-
case 'f':
|
417
|
-
*(writer++) = '\f';
|
418
|
-
reader += 2;
|
419
|
-
break; /* from switch */
|
420
|
-
case 'n':
|
421
|
-
*(writer++) = '\n';
|
422
|
-
reader += 2;
|
423
|
-
break; /* from switch */
|
424
|
-
case 'r':
|
425
|
-
*(writer++) = '\r';
|
426
|
-
reader += 2;
|
427
|
-
break; /* from switch */
|
428
|
-
case 't':
|
429
|
-
*(writer++) = '\t';
|
430
|
-
reader += 2;
|
431
|
-
break; /* from switch */
|
432
|
-
case 'u': { /* test for octal notation */
|
433
|
-
if (is_hex[reader[2]] && is_hex[reader[3]] && is_hex[reader[4]] &&
|
434
|
-
is_hex[reader[5]]) {
|
435
|
-
uint32_t t =
|
436
|
-
((((is_hex[reader[2]] - 1) << 4) | (is_hex[reader[3]] - 1)) << 8) |
|
437
|
-
(((is_hex[reader[4]] - 1) << 4) | (is_hex[reader[5]] - 1));
|
438
|
-
if (reader[6] == '\\' && reader[7] == 'u' && is_hex[reader[8]] &&
|
439
|
-
is_hex[reader[9]] && is_hex[reader[10]] && is_hex[reader[11]]) {
|
440
|
-
/* Serrogate Pair */
|
441
|
-
t = (t & 0x03FF) << 10;
|
442
|
-
t |= ((((((is_hex[reader[8]] - 1) << 4) | (is_hex[reader[9]] - 1))
|
443
|
-
<< 8) |
|
444
|
-
(((is_hex[reader[10]] - 1) << 4) | (is_hex[reader[11]] - 1))) &
|
445
|
-
0x03FF);
|
446
|
-
t += 0x10000;
|
447
|
-
/* Wikipedia way: */
|
448
|
-
// t = 0x10000 + ((t - 0xD800) * 0x400) +
|
449
|
-
// ((((((is_hex[reader[8]] - 1) << 4) | (is_hex[reader[9]] - 1))
|
450
|
-
// << 8) |
|
451
|
-
// (((is_hex[reader[10]] - 1) << 4) | (is_hex[reader[11]] -
|
452
|
-
// 1)))
|
453
|
-
// -
|
454
|
-
// 0xDC00);
|
455
|
-
reader += 6;
|
456
|
-
}
|
457
|
-
writer += utf8_from_u32(writer, t);
|
458
|
-
reader += 6;
|
459
|
-
break; /* from switch */
|
460
|
-
} else
|
461
|
-
goto invalid_escape;
|
462
|
-
}
|
463
|
-
case 'x': { /* test for hex notation */
|
464
|
-
if (is_hex[reader[2]] && is_hex[reader[3]]) {
|
465
|
-
*(writer++) = ((is_hex[reader[2]] - 1) << 4) | (is_hex[reader[3]] - 1);
|
466
|
-
reader += 4;
|
467
|
-
break; /* from switch */
|
468
|
-
} else
|
469
|
-
goto invalid_escape;
|
470
|
-
}
|
471
|
-
case '0':
|
472
|
-
case '1':
|
473
|
-
case '2':
|
474
|
-
case '3':
|
475
|
-
case '4':
|
476
|
-
case '5':
|
477
|
-
case '6':
|
478
|
-
case '7': { /* test for octal notation */
|
479
|
-
if (reader[1] >= '0' && reader[1] <= '7' && reader[2] >= '0' &&
|
480
|
-
reader[2] <= '7') {
|
481
|
-
*(writer++) = ((reader[1] - '0') << 3) | (reader[2] - '0');
|
482
|
-
reader += 3;
|
483
|
-
break; /* from switch */
|
484
|
-
} else
|
485
|
-
goto invalid_escape;
|
486
|
-
}
|
487
|
-
case '"':
|
488
|
-
case '\\':
|
489
|
-
case '/':
|
490
|
-
/* fallthrough */
|
491
|
-
default:
|
492
|
-
invalid_escape:
|
493
|
-
*(writer++) = reader[1];
|
494
|
-
reader += 2;
|
495
|
-
}
|
496
|
-
}
|
497
|
-
// if(had_changed)
|
498
|
-
return ((uintptr_t)writer - (uintptr_t)s.bytes);
|
376
|
+
fio_ary_free(&p.stack);
|
377
|
+
fiobj_free(p.key);
|
378
|
+
*pobj = p.top;
|
379
|
+
return consumed;
|
499
380
|
}
|
500
381
|
|
501
|
-
/* *****************************************************************************
|
502
|
-
JSON => Obj
|
503
|
-
*****************************************************************************
|
504
|
-
*/
|
505
|
-
|
506
382
|
/**
|
507
|
-
*
|
383
|
+
* Updates a Hash using JSON data.
|
384
|
+
*
|
385
|
+
* Parsing errors and non-dictionar object JSON data are silently ignored,
|
386
|
+
* attempting to update the Hash as much as possible before any errors
|
387
|
+
* encountered.
|
508
388
|
*
|
509
|
-
*
|
510
|
-
*
|
389
|
+
* Conflicting Hash data is overwritten (prefering the new over the old).
|
390
|
+
*
|
391
|
+
* Returns the number of bytes consumed. On Error, 0 is returned and no data is
|
392
|
+
* consumed.
|
511
393
|
*/
|
512
|
-
size_t
|
513
|
-
if (!
|
514
|
-
*pobj = NULL;
|
394
|
+
size_t fiobj_hash_update_json(FIOBJ hash, const void *data, size_t len) {
|
395
|
+
if (!hash)
|
515
396
|
return 0;
|
516
|
-
}
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
while (1) {
|
525
|
-
/* skip any white space / seperators */
|
526
|
-
move_to_start(&end, stop);
|
527
|
-
/* set objcet data end point to the starting endpoint */
|
528
|
-
obj = NULL;
|
529
|
-
start = end;
|
530
|
-
if (end >= stop) {
|
531
|
-
goto finish;
|
532
|
-
}
|
533
|
-
|
534
|
-
/* test object type. tests are ordered by precedence, if one fails, the
|
535
|
-
* other is performed. */
|
536
|
-
if (start[0] == '"') {
|
537
|
-
/* object is a string (require qoutes) */
|
538
|
-
start++;
|
539
|
-
end++;
|
540
|
-
uint8_t dirty = 0;
|
541
|
-
while (move_to_quote(&end, stop)) {
|
542
|
-
end += 2;
|
543
|
-
dirty = 1;
|
544
|
-
}
|
545
|
-
if (end >= stop) {
|
546
|
-
goto error;
|
547
|
-
}
|
548
|
-
|
549
|
-
if (fiobj_ary_index(nesting, -1) &&
|
550
|
-
fiobj_ary_index(nesting, -1)->type == FIOBJ_T_HASH) {
|
551
|
-
obj = fiobj_sym_new((char *)start, end - start);
|
552
|
-
if (dirty)
|
553
|
-
fiobj_sym_reinitialize(obj, (size_t)safestr2local(obj));
|
554
|
-
} else {
|
555
|
-
obj = fiobj_str_new((char *)start, end - start);
|
556
|
-
if (dirty)
|
557
|
-
fiobj_str_resize(obj, (size_t)safestr2local(obj));
|
558
|
-
}
|
559
|
-
end++;
|
560
|
-
|
561
|
-
goto has_obj;
|
562
|
-
}
|
563
|
-
if (end[0] >= 'a' && end[0] <= 'z') {
|
564
|
-
if (end + 3 < stop && end[0] == 't' && end[1] == 'r' && end[2] == 'u' &&
|
565
|
-
end[3] == 'e') {
|
566
|
-
/* true */
|
567
|
-
end += 4;
|
568
|
-
obj = fiobj_true();
|
569
|
-
goto has_obj;
|
570
|
-
}
|
571
|
-
if (end + 4 < stop && end[0] == 'f' && end[1] == 'a' && end[2] == 'l' &&
|
572
|
-
end[3] == 's' && end[4] == 'e') {
|
573
|
-
/* false */
|
574
|
-
end += 5;
|
575
|
-
obj = fiobj_false();
|
576
|
-
goto has_obj;
|
577
|
-
}
|
578
|
-
if (end + 3 < stop && end[0] == 'n' && end[1] == 'u' && end[2] == 'l' &&
|
579
|
-
end[3] == 'l') {
|
580
|
-
/* null */
|
581
|
-
end += 4;
|
582
|
-
obj = fiobj_null();
|
583
|
-
goto has_obj;
|
584
|
-
}
|
585
|
-
goto error;
|
586
|
-
}
|
587
|
-
if (end[0] == '{') {
|
588
|
-
/* start an object (hash) */
|
589
|
-
fiobj_ary_push(nesting, fiobj_hash_new());
|
590
|
-
end++;
|
591
|
-
depth++;
|
592
|
-
if (depth >= JSON_MAX_DEPTH) {
|
593
|
-
goto error;
|
594
|
-
}
|
595
|
-
continue;
|
596
|
-
}
|
597
|
-
if (end[0] == '[') {
|
598
|
-
/* start an array */
|
599
|
-
fiobj_ary_push(nesting, fiobj_ary_new2(4));
|
600
|
-
end++;
|
601
|
-
depth++;
|
602
|
-
if (depth >= JSON_MAX_DEPTH) {
|
603
|
-
goto error;
|
604
|
-
}
|
605
|
-
continue;
|
606
|
-
}
|
607
|
-
if (end[0] == '}') {
|
608
|
-
/* end an object (hash) */
|
609
|
-
end++;
|
610
|
-
depth--;
|
611
|
-
obj = fiobj_ary_pop(nesting);
|
612
|
-
if (obj->type != FIOBJ_T_HASH) {
|
613
|
-
goto error;
|
614
|
-
}
|
615
|
-
goto has_obj;
|
616
|
-
}
|
617
|
-
if (end[0] == ']') {
|
618
|
-
/* end an array */
|
619
|
-
end++;
|
620
|
-
depth--;
|
621
|
-
obj = fiobj_ary_pop(nesting);
|
622
|
-
if (obj->type != FIOBJ_T_ARRAY) {
|
623
|
-
goto error;
|
624
|
-
}
|
625
|
-
goto has_obj;
|
626
|
-
}
|
627
|
-
|
628
|
-
if (end[0] == '-') {
|
629
|
-
if (end[0] == '-' && end[1] == 'N' && end[2] == 'a' && end[3] == 'N') {
|
630
|
-
move_to_end(&end, stop);
|
631
|
-
double fl = nan("");
|
632
|
-
fl = copysign(fl, (double)-1);
|
633
|
-
obj = fiobj_float_new(fl);
|
634
|
-
goto has_obj;
|
635
|
-
}
|
636
|
-
if (end[0] == '-' && end[1] == 'I' && end[2] == 'n' && end[3] == 'f') {
|
637
|
-
move_to_end(&end, stop);
|
638
|
-
double fl = INFINITY;
|
639
|
-
fl = copysign(fl, (double)-1);
|
640
|
-
obj = fiobj_float_new(fl);
|
641
|
-
goto has_obj;
|
642
|
-
}
|
643
|
-
goto test_for_number;
|
644
|
-
}
|
645
|
-
if (end[0] >= '0' && end[0] <= '9') {
|
646
|
-
test_for_number: /* test for a number OR float */
|
647
|
-
num = fio_atol((char **)&end);
|
648
|
-
if (end == start || *end == '.' || *end == 'e' || *end == 'E') {
|
649
|
-
end = start;
|
650
|
-
double fnum = fio_atof((char **)&end);
|
651
|
-
if (end == start)
|
652
|
-
goto error;
|
653
|
-
obj = fiobj_float_new(fnum);
|
654
|
-
goto has_obj;
|
655
|
-
}
|
656
|
-
obj = fiobj_num_new(num);
|
657
|
-
goto has_obj;
|
658
|
-
}
|
397
|
+
fiobj_json_parser_s p = {.top = FIOBJ_INVALID, .target = hash};
|
398
|
+
size_t consumed = fio_json_parse(&p.p, data, len);
|
399
|
+
fio_ary_free(&p.stack);
|
400
|
+
fiobj_free(p.key);
|
401
|
+
if (p.top != hash)
|
402
|
+
fiobj_free(p.top);
|
403
|
+
return consumed;
|
404
|
+
}
|
659
405
|
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
continue;
|
684
|
-
}
|
685
|
-
}
|
686
|
-
if (end[0] == '#') {
|
687
|
-
/* could be a Ruby style comment */
|
688
|
-
move_to_eol(&end, stop);
|
689
|
-
continue;
|
690
|
-
}
|
691
|
-
goto error;
|
406
|
+
/**
|
407
|
+
* Formats an object into a JSON string, appending the JSON string to an
|
408
|
+
* existing String. Remember to `fiobj_free`.
|
409
|
+
*/
|
410
|
+
FIOBJ fiobj_obj2json2(FIOBJ dest, FIOBJ o, uint8_t pretty) {
|
411
|
+
assert(dest && FIOBJ_TYPE_IS(dest, FIOBJ_T_STRING));
|
412
|
+
if (!o) {
|
413
|
+
fiobj_str_write(dest, "null", 4);
|
414
|
+
return 0;
|
415
|
+
}
|
416
|
+
fio_ary_s stack;
|
417
|
+
obj2json_data_s data = {
|
418
|
+
.dest = dest, .stack = &stack, .pretty = pretty, .count = 1,
|
419
|
+
};
|
420
|
+
if (!o || !FIOBJ_IS_ALLOCATED(o) || !FIOBJECT2VTBL(o)->each) {
|
421
|
+
fiobj_fiobj_obj2json_task(o, &data);
|
422
|
+
return dest;
|
423
|
+
}
|
424
|
+
fio_ary_new(&stack, 0);
|
425
|
+
fiobj_each2(o, fiobj_fiobj_obj2json_task, &data);
|
426
|
+
fio_ary_free(&stack);
|
427
|
+
return dest;
|
428
|
+
}
|
692
429
|
|
693
|
-
|
430
|
+
/* Formats an object into a JSON string. Remember to `fiobj_free`. */
|
431
|
+
FIOBJ fiobj_obj2json(FIOBJ obj, uint8_t pretty) {
|
432
|
+
return fiobj_obj2json2(fiobj_str_buf(0), obj, pretty);
|
433
|
+
}
|
694
434
|
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
fiobj_ary_push(fiobj_ary_index(nesting, -1), obj);
|
699
|
-
continue;
|
700
|
-
}
|
701
|
-
if (fiobj_ary_index(nesting, -1)->type == FIOBJ_T_HASH) {
|
702
|
-
fiobj_ary_push(nesting, obj);
|
703
|
-
continue;
|
704
|
-
}
|
705
|
-
fiobj_s *sym = fiobj_ary_pop(nesting);
|
706
|
-
if (fiobj_ary_index(nesting, -1)->type != FIOBJ_T_HASH)
|
707
|
-
goto error;
|
708
|
-
fiobj_hash_set(fiobj_ary_index(nesting, -1), sym, obj);
|
709
|
-
fiobj_free(sym);
|
710
|
-
continue;
|
711
|
-
}
|
435
|
+
/* *****************************************************************************
|
436
|
+
Test
|
437
|
+
***************************************************************************** */
|
712
438
|
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
if (
|
718
|
-
*
|
719
|
-
|
720
|
-
|
439
|
+
#if DEBUG
|
440
|
+
void fiobj_test_json(void) {
|
441
|
+
fprintf(stderr, "=== Testing JSON parser (simple test)\n");
|
442
|
+
#define TEST_ASSERT(cond, ...) \
|
443
|
+
if (!(cond)) { \
|
444
|
+
fprintf(stderr, "* " __VA_ARGS__); \
|
445
|
+
fprintf(stderr, "Testing failed.\n"); \
|
446
|
+
exit(-1); \
|
721
447
|
}
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
448
|
+
char json_str[] = "{\"array\":[1,2,3,\"boom\"],\"my\":{\"secret\":42},"
|
449
|
+
"\"true\":true,\"false\":false,\"null\":null,\"float\":-2."
|
450
|
+
"2,\"string\":\"I \\\"wrote\\\" this.\"}";
|
451
|
+
char json_str_update[] = "{\"array\":[1,2,3]}";
|
452
|
+
char json_str2[] =
|
453
|
+
"[\n \"JSON Test Pattern pass1\",\n {\"object with 1 "
|
454
|
+
"member\":[\"array with 1 element\"]},\n {},\n [],\n -42,\n "
|
455
|
+
"true,\n false,\n null,\n {\n \"integer\": 1234567890,\n "
|
456
|
+
" \"real\": -9876.543210,\n \"e\": 0.123456789e-12,\n "
|
457
|
+
" \"E\": 1.234567890E+34,\n \"\": 23456789012E66,\n "
|
458
|
+
"\"zero\": 0,\n \"one\": 1,\n \"space\": \" \",\n "
|
459
|
+
"\"quote\": \"\\\"\",\n \"backslash\": \"\\\\\",\n "
|
460
|
+
"\"controls\": \"\\b\\f\\n\\r\\t\",\n \"slash\": \"/ & \\/\",\n "
|
461
|
+
" \"alpha\": \"abcdefghijklmnopqrstuvwyz\",\n \"ALPHA\": "
|
462
|
+
"\"ABCDEFGHIJKLMNOPQRSTUVWYZ\",\n \"digit\": \"0123456789\",\n "
|
463
|
+
" \"0123456789\": \"digit\",\n \"special\": "
|
464
|
+
"\"`1~!@#$%^&*()_+-={':[,]}|;.</>?\",\n \"hex\": "
|
465
|
+
"\"\\u0123\\u4567\\u89AB\\uCDEF\\uabcd\\uef4A\",\n \"true\": "
|
466
|
+
"true,\n \"false\": false,\n \"null\": null,\n "
|
467
|
+
"\"array\":[ ],\n \"object\":{ },\n \"address\": \"50 "
|
468
|
+
"St. James Street\",\n \"url\": \"http://www.JSON.org/\",\n "
|
469
|
+
" \"comment\": \"// /* <!-- --\",\n \"# -- --> */\": \" \",\n "
|
470
|
+
" \" s p a c e d \" :[1,2 , 3\n\n,\n\n4 , 5 , 6 "
|
471
|
+
" ,7 ],\"compact\":[1,2,3,4,5,6,7],\n \"jsontext\": "
|
472
|
+
"\"{\\\"object with 1 member\\\":[\\\"array with 1 element\\\"]}\",\n "
|
473
|
+
" \"quotes\": \"" \\u0022 %22 0x22 034 "\",\n "
|
474
|
+
"\"\\/"
|
475
|
+
"\\\\\\\"\\uCAFE\\uBABE\\uAB98\\uFCDE\\ubcda\\uef4A\\b\\f\\n\\r\\t`1~!@#$"
|
476
|
+
"%^&*()_+-=[]{}|;:',./<>?\"\n: \"A key can be any string\"\n },\n "
|
477
|
+
"0.5 "
|
478
|
+
",98.6\n,\n99.44\n,\n\n1066,\n1e1,\n0.1e1,\n1e-1,\n1e00,2e+00,2e-00\n,"
|
479
|
+
"\"rosebud\"]";
|
480
|
+
|
481
|
+
FIOBJ o = 0;
|
482
|
+
TEST_ASSERT(fiobj_json2obj(&o, "1", 2) == 1,
|
483
|
+
"JSON number parsing failed to run!\n");
|
484
|
+
TEST_ASSERT(o, "JSON (single) object missing!\n");
|
485
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_NUMBER),
|
486
|
+
"JSON (single) not a number!\n");
|
487
|
+
TEST_ASSERT(fiobj_obj2num(o) == 1, "JSON (single) not == 1!\n");
|
488
|
+
fiobj_free(o);
|
489
|
+
|
490
|
+
TEST_ASSERT(fiobj_json2obj(&o, "2.0", 5) == 3,
|
491
|
+
"JSON float parsing failed to run!\n");
|
492
|
+
TEST_ASSERT(o, "JSON (float) object missing!\n");
|
493
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_FLOAT), "JSON (float) not a float!\n");
|
494
|
+
TEST_ASSERT(fiobj_obj2float(o) == 2, "JSON (float) not == 2!\n");
|
495
|
+
fiobj_free(o);
|
496
|
+
|
497
|
+
TEST_ASSERT(fiobj_json2obj(&o, json_str, sizeof(json_str)) ==
|
498
|
+
(sizeof(json_str) - 1),
|
499
|
+
"JSON parsing failed to run!\n");
|
500
|
+
TEST_ASSERT(o, "JSON object missing!\n");
|
501
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_HASH),
|
502
|
+
"JSON root not a dictionary (not a hash)!\n");
|
503
|
+
FIOBJ tmp = fiobj_hash_get2(o, fio_siphash("array", 5));
|
504
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(tmp, FIOBJ_T_ARRAY),
|
505
|
+
"JSON 'array' not an Array!\n");
|
506
|
+
TEST_ASSERT(fiobj_obj2num(fiobj_ary_index(tmp, 0)) == 1,
|
507
|
+
"JSON 'array' index 0 error!\n");
|
508
|
+
TEST_ASSERT(fiobj_obj2num(fiobj_ary_index(tmp, 1)) == 2,
|
509
|
+
"JSON 'array' index 1 error!\n");
|
510
|
+
TEST_ASSERT(fiobj_obj2num(fiobj_ary_index(tmp, 2)) == 3,
|
511
|
+
"JSON 'array' index 2 error!\n");
|
512
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(fiobj_ary_index(tmp, 3), FIOBJ_T_STRING),
|
513
|
+
"JSON 'array' index 3 type error!\n");
|
514
|
+
TEST_ASSERT(!memcmp("boom", fiobj_obj2cstr(fiobj_ary_index(tmp, 3)).data, 4),
|
515
|
+
"JSON 'array' index 3 error!\n");
|
516
|
+
tmp = fiobj_hash_get2(o, fio_siphash("my", 2));
|
517
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(tmp, FIOBJ_T_HASH),
|
518
|
+
"JSON 'my:secret' not a Hash!\n");
|
519
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(fiobj_hash_get2(tmp, fio_siphash("secret", 6)),
|
520
|
+
FIOBJ_T_NUMBER),
|
521
|
+
"JSON 'my:secret' doesn't hold a number!\n");
|
522
|
+
TEST_ASSERT(fiobj_obj2num(fiobj_hash_get2(tmp, fio_siphash("secret", 6))) ==
|
523
|
+
42,
|
524
|
+
"JSON 'my:secret' not 42!\n");
|
525
|
+
TEST_ASSERT(fiobj_hash_get2(o, fio_siphash("true", 4)) == fiobj_true(),
|
526
|
+
"JSON 'true' not true!\n");
|
527
|
+
TEST_ASSERT(fiobj_hash_get2(o, fio_siphash("false", 5)) == fiobj_false(),
|
528
|
+
"JSON 'false' not false!\n");
|
529
|
+
TEST_ASSERT(fiobj_hash_get2(o, fio_siphash("null", 4)) == fiobj_null(),
|
530
|
+
"JSON 'null' not null!\n");
|
531
|
+
tmp = fiobj_hash_get2(o, fio_siphash("float", 5));
|
532
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(tmp, FIOBJ_T_FLOAT), "JSON 'float' not a float!\n");
|
533
|
+
tmp = fiobj_hash_get2(o, fio_siphash("string", 6));
|
534
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(tmp, FIOBJ_T_STRING),
|
535
|
+
"JSON 'string' not a string!\n");
|
536
|
+
TEST_ASSERT(!strcmp(fiobj_obj2cstr(tmp).data, "I \"wrote\" this."),
|
537
|
+
"JSON 'string' incorrect!\n");
|
538
|
+
fprintf(stderr, "* passed.\n");
|
539
|
+
fprintf(stderr, "=== Testing JSON formatting (simple test)\n");
|
540
|
+
tmp = fiobj_obj2json(o, 0);
|
541
|
+
fprintf(stderr, "* data (%p):\n%.*s\n", fiobj_obj2cstr(tmp).buffer,
|
542
|
+
(int)fiobj_obj2cstr(tmp).len, fiobj_obj2cstr(tmp).data);
|
543
|
+
if (!strcmp(fiobj_obj2cstr(tmp).data, json_str))
|
544
|
+
fprintf(stderr, "* Stringify == Original.\n");
|
545
|
+
TEST_ASSERT(
|
546
|
+
fiobj_hash_update_json(o, json_str_update, strlen(json_str_update)),
|
547
|
+
"JSON update failed to parse data.");
|
548
|
+
fiobj_free(tmp);
|
549
|
+
|
550
|
+
tmp = fiobj_hash_get2(o, fio_siphash("array", 5));
|
551
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(tmp, FIOBJ_T_ARRAY),
|
552
|
+
"JSON updated 'array' not an Array!\n");
|
553
|
+
TEST_ASSERT(fiobj_ary_count(tmp) == 3, "JSON updated 'array' not updated?");
|
554
|
+
tmp = fiobj_hash_get2(o, fio_siphash("float", 5));
|
555
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(tmp, FIOBJ_T_FLOAT),
|
556
|
+
"JSON updated (old) 'float' missing!\n");
|
557
|
+
fiobj_free(o);
|
558
|
+
fprintf(stderr, "* passed.\n");
|
559
|
+
|
560
|
+
fprintf(stderr, "=== Testing JSON parsing (UTF-8 and special cases)\n");
|
561
|
+
fiobj_json2obj(&o, "[\"\\uD834\\uDD1E\"]", 16);
|
562
|
+
TEST_ASSERT(o, "JSON G clef String failed to parse!\n");
|
563
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_ARRAY),
|
564
|
+
"JSON G clef container has an incorrect type! (%s)\n",
|
565
|
+
fiobj_type_name(o));
|
566
|
+
tmp = o;
|
567
|
+
o = fiobj_ary_pop(o);
|
568
|
+
fiobj_free(tmp);
|
569
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_STRING),
|
570
|
+
"JSON G clef String incorrect type! %p => %s\n", (void *)o,
|
571
|
+
fiobj_type_name(o));
|
572
|
+
TEST_ASSERT((!strcmp(fiobj_obj2cstr(o).data, "\xF0\x9D\x84\x9E")),
|
573
|
+
"JSON G clef String incorrect %s !\n", fiobj_obj2cstr(o).data);
|
574
|
+
fiobj_free(o);
|
575
|
+
|
576
|
+
fiobj_json2obj(&o, "\"\\uD834\\uDD1E\"", 14);
|
577
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_STRING),
|
578
|
+
"JSON direct G clef String incorrect type! %p => %s\n", (void *)o,
|
579
|
+
fiobj_type_name(o));
|
580
|
+
TEST_ASSERT((!strcmp(fiobj_obj2cstr(o).data, "\xF0\x9D\x84\x9E")),
|
581
|
+
"JSON direct G clef String incorrect %s !\n",
|
582
|
+
fiobj_obj2cstr(o).data);
|
583
|
+
fiobj_free(o);
|
584
|
+
fiobj_json2obj(&o, "\"Hello\\u0000World\"", 19);
|
585
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_STRING),
|
586
|
+
"JSON NUL containing String incorrect type! %p => %s\n",
|
587
|
+
(void *)o, fiobj_type_name(o));
|
588
|
+
TEST_ASSERT(
|
589
|
+
(!memcmp(fiobj_obj2cstr(o).data, "Hello\0World", fiobj_obj2cstr(o).len)),
|
590
|
+
"JSON NUL containing String incorrect! (%u): %s . %s\n",
|
591
|
+
(int)fiobj_obj2cstr(o).len, fiobj_obj2cstr(o).data,
|
592
|
+
fiobj_obj2cstr(o).data + 3);
|
593
|
+
fiobj_free(o);
|
594
|
+
size_t consumed = fiobj_json2obj(&o, json_str2, sizeof(json_str2));
|
595
|
+
TEST_ASSERT(
|
596
|
+
consumed == (sizeof(json_str2) - 1),
|
597
|
+
"JSON messy string failed to parse (consumed %lu instead of %lu\n",
|
598
|
+
(unsigned long)consumed, (unsigned long)(sizeof(json_str2) - 1));
|
599
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_ARRAY),
|
600
|
+
"JSON messy string object error\n");
|
601
|
+
tmp = fiobj_obj2json(o, 1);
|
602
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(tmp, FIOBJ_T_STRING),
|
603
|
+
"JSON messy string isn't a string\n");
|
604
|
+
fprintf(stderr, "Messy JSON:\n%s\n", fiobj_obj2cstr(tmp).data);
|
605
|
+
fiobj_free(o);
|
606
|
+
fiobj_free(tmp);
|
607
|
+
fprintf(stderr, "* passed.\n");
|
733
608
|
}
|
609
|
+
|
610
|
+
#endif
|