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
@@ -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 "fiobj_sym.h"
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(fiobj_s *h);
33
+ void fiobj_hash_rehash(FIOBJ h);
22
34
 
23
35
  /* *****************************************************************************
24
- Couplets API - the Key-Value pair, created by the Hash object
36
+ Hash Creation
25
37
  ***************************************************************************** */
26
38
 
27
- /** Couplet type identifier. */
28
- extern const uintptr_t FIOBJ_T_COUPLET;
29
-
30
39
  /**
31
- * If object is a Hash couplet (occurs in `fiobj_each2`), returns the key
32
- * (Symbol) from the key-value pair.
40
+ * Creates a mutable empty Hash object. Use `fiobj_free` when done.
33
41
  *
34
- * Otherwise returns NULL.
42
+ * Notice that these Hash objects are optimized for smaller collections and
43
+ * retain order of object insertion.
35
44
  */
36
- fiobj_s *fiobj_couplet2key(const fiobj_s *obj);
45
+ FIOBJ fiobj_hash_new(void);
37
46
 
38
47
  /**
39
- * If object is a Hash couplet (occurs in `fiobj_each2`), returns the object
40
- * (the value) from the key-value pair.
48
+ * Creates a mutable empty Hash object with an initial capacity of `capa`. Use
49
+ * `fiobj_free` when done.
41
50
  *
42
- * Otherwise returns NULL.
51
+ * This allows optimizations for larger (or smaller) collections.
43
52
  */
44
- fiobj_s *fiobj_couplet2obj(const fiobj_s *obj);
53
+ FIOBJ fiobj_hash_new2(size_t capa);
45
54
 
46
55
  /* *****************************************************************************
47
- Hash API
56
+ Hash properties and state
48
57
  ***************************************************************************** */
49
58
 
50
- /** Hash type identifier.
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
- The facil.io Hash object is, by default, an insecure (non-collision resistant)
53
- ordered Hash Table implementation.
65
+ /** Returns the number of elements in the Hash. */
66
+ size_t fiobj_hash_count(const FIOBJ hash);
54
67
 
55
- By being non-collision resistant (comparing only the Hash data), memory
56
- comparison can be avoided and performance increased.
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
- By being ordered it's possible to iterate over key-value pairs in the order in
59
- which they were added to the Hash table, making it possible to output JSON in a
60
- controlled manner.
61
- */
62
- extern const uintptr_t FIOBJ_T_HASH;
71
+ /* *****************************************************************************
72
+ Populating the Hash
73
+ ***************************************************************************** */
63
74
 
64
75
  /**
65
- * Creates a mutable empty Hash object. Use `fiobj_free` when done.
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
- * Notice that these Hash objects are optimized for smaller collections and
68
- * retain order of object insertion.
79
+ * Returns -1 on error.
69
80
  */
70
- fiobj_s *fiobj_hash_new(void);
81
+ int fiobj_hash_set(FIOBJ hash, FIOBJ key, FIOBJ obj);
71
82
 
72
- /** Returns the number of elements in the Hash. */
73
- size_t fiobj_hash_count(const fiobj_s *hash);
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
- * Sets a key-value pair in the Hash, duplicating the Symbol and **moving**
77
- * the ownership of the object to the Hash.
96
+ * Replaces the value in a key-value pair, returning the old value (and it's
97
+ * ownership) to the caller.
78
98
  *
79
- * Returns -1 on error.
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
- int fiobj_hash_set(fiobj_s *hash, fiobj_s *sym, fiobj_s *obj);
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
- fiobj_s *fiobj_hash_remove(fiobj_s *hash, fiobj_s *sym);
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 fiobj_hash_delete(fiobj_s *hash, fiobj_s *sym);
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, NULL
99
- * if none.
141
+ * Returns a temporary handle to the object associated with the Symbol,
142
+ * FIOBJ_INVALID if none.
100
143
  */
101
- fiobj_s *fiobj_hash_get(const fiobj_s *hash, fiobj_s *sym);
144
+ FIOBJ fiobj_hash_get(const FIOBJ hash, FIOBJ key);
102
145
 
103
146
  /**
104
- * Returns a temporary handle to the object associated with the Symbol C string.
147
+ * Returns a temporary handle to the object associated hashed key value.
105
148
  *
106
- * This function takes a C string instead of a Symbol, which is slower if a
107
- * Symbol can be cached but faster if a Symbol must be created.
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 NULL if no object is asociated with this String data.
153
+ * Returns FIOBJ_INVALID if no object is asociated with this hashed key value.
110
154
  */
111
- fiobj_s *fiobj_hash_get2(const fiobj_s *hash, const char *str, size_t len);
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 fiobj_s *hash, fiobj_s *sym);
160
+ int fiobj_hash_haskey(const FIOBJ hash, FIOBJ key);
117
161
 
118
162
  /**
119
- * Returns a temporary theoretical Hash map capacity.
120
- * This could be used for testig performance and memory consumption.
163
+ * Empties the Hash.
121
164
  */
122
- size_t fiobj_hash_capa(const fiobj_s *hash);
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" */
@@ -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
- // #include "fio2resp.h"
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
- #define JSON_MAX_DEPTH 24
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(fiobj_s **pobj, const void *data, size_t len);
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
- fiobj_s *fiobj_obj2json(fiobj_s *, uint8_t);
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
- JSON UTF-8 safe string formatting
44
+ FIOBJ Callacks
29
45
  ***************************************************************************** */
30
46
 
31
- static const char hex_chars[] = {'0', '1', '2', '3', '4', '5', '6', '7',
32
- '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
33
-
34
- static const uint8_t is_hex[] = {
35
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
36
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
37
- 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0,
38
- 0, 0, 0, 0, 0, 11, 12, 13, 14, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
39
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 13,
40
- 14, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
41
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
42
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
43
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
44
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
45
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
46
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
47
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
48
-
49
- // /* invalid byte length is ignored */
50
- // static inline int utf8_clen(const uint8_t *str) {
51
- // if (str[0] <= 127)
52
- // return 1;
53
- // if ((str[0] & 224) == 192 && (str[1] & 192) == 128)
54
- // return 2;
55
- // if ((str[0] & 240) == 224 && (str[1] & 192) == 128 && (str[2] & 192) ==
56
- // 128)
57
- // return 3;
58
- // if ((str[0] & 248) == 240 && (str[1] & 192) == 128 && (str[2] & 192) == 128
59
- // &&
60
- // (str[3] & 192) == 128)
61
- // return 4;
62
- // return 0; /* invalid UTF-8 */
63
- // }
64
-
65
- /* converts a uint16_t to UTF-8 and returns the number of bytes written */
66
- static inline int utf8_from_u32(uint8_t *dest, uint32_t u) {
67
- if (u <= 127) {
68
- *dest = u;
69
- return 1;
70
- } else if (u <= 2047) {
71
- *(dest++) = 192 | (u >> 6);
72
- *(dest++) = 128 | (u & 63);
73
- return 2;
74
- } else if (u <= 65535) {
75
- *(dest++) = 224 | (u >> 12);
76
- *(dest++) = 128 | ((u >> 6) & 63);
77
- *(dest++) = 128 | (u & 63);
78
- return 3;
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
- *(dest++) = 240 | ((u >> 18) & 7);
81
- *(dest++) = 128 | ((u >> 12) & 63);
82
- *(dest++) = 128 | ((u >> 6) & 63);
83
- *(dest++) = 128 | (u & 63);
84
- return 4;
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(fiobj_s *dest, const fiobj_s *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
- capa = (((capa >> 12) + 1) << 12) - 1;
99
- fiobj_str_capa_assert(dest, capa);
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
- capa = (((capa >> 12) + 1) << 12) - 1;
163
- fiobj_str_capa_assert(dest, capa);
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
- JSON formatting
173
- ***************************************************************************** */
174
-
175
- /* this is used to persist data in `fiobj_each2` */
176
- struct fiobj_str_new_json_data_s {
177
- fiobj_s *parent; /* stores item types */
178
- fiobj_s *waiting; /* stores item counts and types */
179
- fiobj_s *buffer; /* we'll write the JSON here */
180
- fiobj_s *count; /* used to persist item counts for arrays / hashes */
181
- uint8_t pretty; /* make it beautiful */
182
- };
183
-
184
- static int fiobj_str_new_json_task(fiobj_s *obj, void *d_) {
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
- re_rooted:
201
- if (!obj) {
202
- fiobj_str_write(data->buffer, "null", 4);
203
- goto review_nesting;
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 (obj->type == FIOBJ_T_HASH) {
206
- fiobj_str_write(data->buffer, "{", 1);
207
- fiobj_ary_push(data->parent, obj);
208
- fiobj_ary_push(data->waiting, data->count);
209
- data->count = fiobj_num_new(fiobj_hash_count(obj));
210
- } else if (obj->type == FIOBJ_T_ARRAY) {
211
- fiobj_str_write(data->buffer, "[", 1);
212
- /* push current state to stacks and update state */
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
- while (*pos < limit) {
372
- if (**pos == '\"') {
373
- return 0;
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
- if (**pos == '\\') {
376
- return 1;
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
- JSON UTF-8 safe string deconstruction
385
- *****************************************************************************
386
- */
360
+ FIOBJ API
361
+ ***************************************************************************** */
387
362
 
388
- /* Converts a JSON friendly String to a binary String.
363
+ /**
364
+ * Parses JSON, setting `pobj` to point to the new Object.
389
365
  *
390
- * Also deals with the non-standard oct ("\77") and hex ("\xFF") notations
366
+ * Returns the number of bytes consumed. On Error, 0 is returned and no data is
367
+ * consumed.
391
368
  */
392
- static uintptr_t safestr2local(fiobj_s *str) {
393
- if (str->type != FIOBJ_T_STRING && str->type != FIOBJ_T_SYMBOL) {
394
- fprintf(stderr,
395
- "CRITICAL ERROR: unexpected function call `safestr2local`\n");
396
- exit(-1);
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
- fio_cstr_s s = fiobj_obj2cstr(str);
399
- // uint8_t had_changed = 0;
400
- uint8_t *end = (uint8_t *)s.bytes + s.len;
401
- uint8_t *reader = (uint8_t *)s.bytes;
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
- * Parses JSON, setting `pobj` to point to the new Object.
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
- * Returns the number of bytes consumed. On Error, 0 is returned and no data
510
- * is consumed.
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 fiobj_json2obj(fiobj_s **pobj, const void *data, size_t len) {
513
- if (!data) {
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
- fiobj_s *nesting = fiobj_ary_new2(JSON_MAX_DEPTH + 2);
518
- int64_t num;
519
- const uint8_t *start;
520
- fiobj_s *obj;
521
- const uint8_t *end = (uint8_t *)data;
522
- const uint8_t *stop = end + len;
523
- uint8_t depth = 0;
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
- if (end[0] == 'N' && end[1] == 'a' && end[2] == 'N') {
661
- move_to_end(&end, stop);
662
- obj = fiobj_float_new(nan(""));
663
- goto has_obj;
664
- }
665
- if (end[0] == 'I' && end[1] == 'n' && end[2] == 'f') {
666
- move_to_end(&end, stop);
667
- obj = fiobj_float_new(INFINITY);
668
- goto has_obj;
669
- }
670
- if (end[0] == '/') {
671
- /* could be a Javascript comment */
672
- if (end[1] == '/') {
673
- end += 2;
674
- move_to_eol(&end, stop);
675
- continue;
676
- }
677
- if (end[1] == '*') {
678
- end += 2;
679
- while (end < stop && !(end[0] == '*' && end[1] == '/'))
680
- end++;
681
- if (end < stop && end[0] == '*')
682
- end += 2;
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
- has_obj:
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
- if (fiobj_ary_count(nesting) == 0)
696
- goto finish_with_obj;
697
- if (fiobj_ary_index(nesting, -1)->type == FIOBJ_T_ARRAY) {
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
- finish:
714
- if (!obj)
715
- obj = fiobj_ary_pop(nesting);
716
- finish_with_obj:
717
- if (obj && fiobj_ary_count(nesting) == 0) {
718
- *pobj = obj;
719
- fiobj_free(nesting);
720
- return end - (uint8_t *)data;
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
- error:
723
- if (obj)
724
- fiobj_free(obj);
725
- while ((obj = fiobj_ary_pop(nesting)))
726
- fiobj_free(obj);
727
- fiobj_free(nesting);
728
- *pobj = NULL;
729
- // fprintf(stderr, "ERROR starting at %.*s, ending at %.*s, with %s\n", 3,
730
- // start,
731
- // 3, end, start);
732
- return 0;
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\": \"&#34; \\u0022 %22 0x22 034 &#x22;\",\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