]> mj.ucw.cz Git - libucw.git/blob - ucw-json/json.h
JSON: Improved memory management with pushing/popping of states
[libucw.git] / ucw-json / json.h
1 /*
2  *      UCW JSON Library
3  *
4  *      (c) 2015 Martin Mares <mj@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #ifndef _UCW_JSON_JSON_H
11 #define _UCW_JSON_JSON_H
12
13 #include <ucw/clists.h>
14 #include <ucw/slists.h>
15 #include <ucw/mempool.h>
16 #include <ucw/fastbuf.h>
17
18 #ifdef CONFIG_UCW_CLEAN_ABI
19 #define json_array_append ucw_json_array_append
20 #define json_delete ucw_json_delete
21 #define json_new ucw_json_new
22 #define json_new_array ucw_json_new_array
23 #define json_new_node ucw_json_new_node
24 #define json_new_object ucw_json_new_object
25 #define json_next_token ucw_json_next_token
26 #define json_next_value ucw_json_next_value
27 #define json_object_get ucw_json_object_get
28 #define json_object_set ucw_json_object_set
29 #define json_parse ucw_json_parse
30 #define json_peek_token ucw_json_peek_token
31 #define json_pop ucw_json_pop
32 #define json_push ucw_json_push
33 #define json_reset ucw_json_reset
34 #define json_set_input ucw_json_set_input
35 #define json_set_output ucw_json_set_output
36 #define json_write ucw_json_write
37 #define json_write_value ucw_json_write_value
38 #endif
39
40 /***
41  * === JSON library context
42  *
43  * The context structure remembers the whole state of the JSON
44  * library. All JSON values are allocated from a memory pool associated
45  * with the context. By default, their lifetime is the same as that
46  * of the context.
47  *
48  * Alternatively, you can mark the current state of the context
49  * with json_push() and return to the marked state later using
50  * json_pop(). All JSON values created between these two operations
51  * are released afterwards. See json_push() for details.
52  ***/
53
54 /**
55  * The context is represented a pointer to this structure.
56  * The fields marked with [*] are publicly accessible, the rest is private.
57  **/
58 struct json_context {
59   // Memory management
60   struct mempool *pool;
61   struct mempool_state init_state;
62
63   // Parser context
64   struct fastbuf *in_fb;
65   uint in_line;                         // [*] Current line number
66   uint in_column;                       // [*] Current column number
67   bool in_eof;                          //     End of file was encountered
68   struct json_node *next_token;
69   struct json_node *trivial_token;
70   int next_char;
71
72   // Formatter context
73   struct fastbuf *out_fb;
74   uint out_indent;
75   uint format_options;                  // [*] Formatting options (a combination of JSON_FORMAT_xxx)
76 };
77
78 /** Creates a new JSON context. **/
79 struct json_context *json_new(void);
80
81 /** Deletes a JSON context, deallocating all memory associated with it. **/
82 void json_delete(struct json_context *js);
83
84 /**
85  * Recycles a JSON context. All state is reset, allocated objects are freed.
86  * This is equivalent to mp_delete() followed by mp_new(), but it is faster
87  * and the address of the context is preserved.
88  **/
89 void json_reset(struct json_context *js);
90
91 /**
92  * Push the current state of the context onto state stack.
93  *
94  * Between json_push() and the associated json_pop(), only newly
95  * created JSON values can be modified. Older values can be only
96  * inspected, never modified. In particular, new values cannot be
97  * inserted to old arrays nor objects.
98  *
99  * If you are using json_peek_token(), the saved tokens cannot
100  * be carried over push/pop boundary.
101  **/
102 void json_push(struct json_context *js);
103
104 /**
105  * Pop state of the context off state stack. All JSON values created
106  * since the state was saved by json_push() are released.
107  **/
108 void json_pop(struct json_context *js);
109
110 /***
111  * === JSON values
112  *
113  * Each JSON value is represented by <<struct json_node,struct json_node>>,
114  * which is either an elementary value (null, boolean, number, string),
115  * or a container (array, object) pointing to other values.
116  *
117  * A value can belong to multiple containers simultaneously, so in general,
118  * the relationships between values need not form a tree, but a directed
119  * acyclic graph.
120  *
121  * You are allowed to read contents of nodes directly, but construction
122  * and modification of nodes must be always performed using the appropriate
123  * library functions.
124  ***/
125
126 /** Node types **/
127 enum json_node_type {
128   JSON_INVALID,
129   JSON_NULL,
130   JSON_BOOLEAN,
131   JSON_NUMBER,
132   JSON_STRING,
133   JSON_ARRAY,
134   JSON_OBJECT,
135   // These are not real nodes, but raw tokens.
136   // They are not present in the tree of values, but you may see them
137   // if you call json_next_token() and friends.
138   JSON_BEGIN_ARRAY,
139   JSON_END_ARRAY,
140   JSON_BEGIN_OBJECT,
141   JSON_END_OBJECT,
142   JSON_NAME_SEP,
143   JSON_VALUE_SEP,
144   JSON_EOF,
145 };
146
147 /** Each value is represented by a single node. **/
148 struct json_node {
149   enum json_node_type type;
150   union {                               // Data specific to individual value types
151     bool boolean;
152     double number;
153     const char *string;
154     struct json_node **elements;        // Arrays: Growing array of values
155     struct json_pair *pairs;            // Objects: Growing array of pairs
156   };
157 };
158
159 /** Attributes of objects are stored as (key, value) pairs of this format. **/
160 struct json_pair {
161   const char *key;
162   struct json_node *value;
163   // FIXME: Hash table
164 };
165
166 // Used internally
167 struct json_node *json_new_node(struct json_context *js, enum json_node_type type);
168
169 /** Creates a new null value. **/
170 static inline struct json_node *json_new_null(struct json_context *js)
171 {
172   return json_new_node(js, JSON_NULL);
173 }
174
175 /** Creates a new boolean value. **/
176 static inline struct json_node *json_new_bool(struct json_context *js, bool value)
177 {
178   struct json_node *n = json_new_node(js, JSON_BOOLEAN);
179   n->boolean = value;
180   return n;
181 }
182
183 /** Creates a new numeric value. **/
184 static inline struct json_node *json_new_number(struct json_context *js, double value)
185 {
186   struct json_node *n = json_new_node(js, JSON_NUMBER);
187   n->number = value;
188   return n;
189 }
190
191 /** Creates a new string value. The @value is kept only as a reference. **/
192 static inline struct json_node *json_new_string_ref(struct json_context *js, const char *value)
193 {
194   struct json_node *n = json_new_node(js, JSON_STRING);
195   n->string = value;
196   return n;
197 }
198
199 /** Creates a new string value, making a private copy of @value. **/
200 static inline struct json_node *json_new_string(struct json_context *js, const char *value)
201 {
202   return json_new_string_ref(js, mp_strdup(js->pool, value));
203 }
204
205 /** Creates a new array value with no elements. **/
206 struct json_node *json_new_array(struct json_context *js);
207
208 /** Appends a new element to the given array. **/
209 void json_array_append(struct json_node *array, struct json_node *elt);
210
211 /** Creates a new object value with no attributes. **/
212 struct json_node *json_new_object(struct json_context *js);
213
214 /**
215  * Adds a new (@key, @value) pair to the given object. If @key is already
216  * present, the pair is replaced. If @value is NULL, no new pair is created
217  * and a pre-existing pair is deleted.
218  *
219  * The @key is referenced by the object, you must not free it during
220  * the lifetime of the JSON context.
221  *
222  * FIXME: Add json_copy_key().
223  **/
224 void json_object_set(struct json_node *n, const char *key, struct json_node *value);
225
226 /** Returns the value associated with @key, or NULL if no such value exists. **/
227 struct json_node *json_object_get(struct json_node *n, const char *key);
228
229 /***
230  * === Parser
231  *
232  * The simplest way to parse a complete JSON file is to call json_parse(),
233  * which returns a value tree representing the contents of the file.
234  *
235  * Alternatively, you can read the input token by token: call json_set_input()
236  * and then repeat json_next_token(). If you are parsing huge JSON files,
237  * you probably want to do json_push() first, then scan and process some
238  * tokens, and then json_pop().
239  ***/
240
241 /** Parses a JSON file from the given fastbuf stream. **/
242 struct json_node *json_parse(struct json_context *js, struct fastbuf *fb);
243
244 /** Selects the given fastbuf stream as parser input. **/
245 void json_set_input(struct json_context *js, struct fastbuf *in);
246
247 /** Reads the next token from the input. **/
248 struct json_node *json_next_token(struct json_context *js);
249
250 /** Reads the next token, but keeps it in the input. **/
251 struct json_node *json_peek_token(struct json_context *js);
252
253 /** Reads the next JSON value, including nested values. **/
254 struct json_node *json_next_value(struct json_context *js);
255
256 /***
257  * === Writer
258  *
259  * JSON files can be produced by simply calling json_write().
260  *
261  * If you want to generate the output on the fly (for example if it is huge),
262  * call json_set_output() and then iterate json_write_value().
263  *
264  * By default, we produce a single-line compact representation,
265  * but you can choose differently by setting the appropriate
266  * `format_options` in the `json_context`.
267  ***/
268
269 /** Writes a JSON file to the given fastbuf stream, containing the JSON value @n. **/
270 void json_write(struct json_context *js, struct fastbuf *fb, struct json_node *n);
271
272 /** Selects the given fastbuf stream as output. **/
273 void json_set_output(struct json_context *js, struct fastbuf *fb);
274
275 /** Writes a single JSON value to the output stream. **/
276 void json_write_value(struct json_context *js, struct json_node *n);
277
278 /** Formatting options. The `format_options` field in the context is a bitwise OR of these flags. **/
279 enum json_format_option {
280   JSON_FORMAT_ESCAPE_NONASCII = 1,      // Produce pure ASCII output by escaping all Unicode characters in strings
281   JSON_FORMAT_INDENT = 2,               // Produce pretty indented output
282 };
283
284 #endif