Skip to content
Newer
Older
100644 363 lines (320 sloc) 10.7 KB
d553293 move all files to trunk.
lloydh authored
1 /*
517abfa @lloyd update copyright and email
authored
2 * Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
de81b1f @lloyd yajl 2 will be relicensed under the ISC license. same idea, fewer bytes.
authored
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
d553293 move all files to trunk.
lloydh authored
16
17 #include "api/yajl_gen.h"
18 #include "yajl_buf.h"
19 #include "yajl_encode.h"
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdio.h>
3d77db1 @lloyd return an error from yajl_gen_double when client passes inf or nan. C…
authored
24 #include <math.h>
0858bb3 @lloyd rework programmatic configuration of yajl. both generator and parser …
authored
25 #include <stdarg.h>
d553293 move all files to trunk.
lloydh authored
26
27 typedef enum {
28 yajl_gen_start,
29 yajl_gen_map_start,
30 yajl_gen_map_key,
31 yajl_gen_map_val,
32 yajl_gen_array_start,
33 yajl_gen_in_array,
34 yajl_gen_complete,
35 yajl_gen_error
36 } yajl_gen_state;
37
266dd3e @lloyd add option for escaping the '/' (solidus) character. closes #28
authored
38 struct yajl_gen_t
d553293 move all files to trunk.
lloydh authored
39 {
0858bb3 @lloyd rework programmatic configuration of yajl. both generator and parser …
authored
40 unsigned int flags;
d553293 move all files to trunk.
lloydh authored
41 unsigned int depth;
42 const char * indentString;
43 yajl_gen_state state[YAJL_MAX_DEPTH];
32098df @brimworks Add API so that we can provide our own printer function instead of
brimworks authored
44 yajl_print_t print;
45 void * ctx; /* yajl_buf */
b78dd79 @lloyd * BREAKING API CHANGE: allow client to specify memory allocation rou…
authored
46 /* memory allocation routines */
47 yajl_alloc_funcs alloc;
d553293 move all files to trunk.
lloydh authored
48 };
49
0858bb3 @lloyd rework programmatic configuration of yajl. both generator and parser …
authored
50 int
51 yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...)
d553293 move all files to trunk.
lloydh authored
52 {
0858bb3 @lloyd rework programmatic configuration of yajl. both generator and parser …
authored
53 int rv = 1;
54 va_list ap;
55 va_start(ap, opt);
56
57 switch(opt) {
58 case yajl_gen_beautify:
9538bda @lloyd Add a generator feature to validate UTF8 strings as they are written …
authored
59 case yajl_gen_validate_utf8:
0d480b2 @ConradIrwin Allow the yajl_gen_escape_solidus option to be set.
ConradIrwin authored
60 case yajl_gen_escape_solidus:
0858bb3 @lloyd rework programmatic configuration of yajl. both generator and parser …
authored
61 if (va_arg(ap, int)) g->flags |= opt;
62 else g->flags &= ~opt;
63 break;
64 case yajl_gen_indent_string: {
65 const char *indent = va_arg(ap, const char *);
66 g->indentString = indent;
67 for (; *indent; indent++) {
68 if (*indent != '\n'
69 && *indent != '\v'
70 && *indent != '\f'
71 && *indent != '\t'
72 && *indent != '\r'
73 && *indent != ' ')
74 {
75 g->indentString = NULL;
76 rv = 0;
77 }
78 }
79 break;
80 }
81 case yajl_gen_print_callback:
82 yajl_buf_free(g->ctx);
83 g->print = va_arg(ap, const yajl_print_t);
84 g->ctx = va_arg(ap, void *);
85 break;
86 default:
87 rv = 0;
88 }
89
90 va_end(ap);
91
92 return rv;
32098df @brimworks Add API so that we can provide our own printer function instead of
brimworks authored
93 }
94
0858bb3 @lloyd rework programmatic configuration of yajl. both generator and parser …
authored
95
96
32098df @brimworks Add API so that we can provide our own printer function instead of
brimworks authored
97 yajl_gen
0858bb3 @lloyd rework programmatic configuration of yajl. both generator and parser …
authored
98 yajl_gen_alloc(const yajl_alloc_funcs * afs)
32098df @brimworks Add API so that we can provide our own printer function instead of
brimworks authored
99 {
b78dd79 @lloyd * BREAKING API CHANGE: allow client to specify memory allocation rou…
authored
100 yajl_gen g = NULL;
101 yajl_alloc_funcs afsBuffer;
102
103 /* first order of business is to set up memory allocation routines */
104 if (afs != NULL) {
105 if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL)
106 {
107 return NULL;
108 }
109 } else {
110 yajl_set_default_alloc_funcs(&afsBuffer);
111 afs = &afsBuffer;
112 }
113
114 g = (yajl_gen) YA_MALLOC(afs, sizeof(struct yajl_gen_t));
0d9e358 @gno Don't generate numbers for keys. closes #13
gno authored
115 if (!g) return NULL;
116
d553293 move all files to trunk.
lloydh authored
117 memset((void *) g, 0, sizeof(struct yajl_gen_t));
b78dd79 @lloyd * BREAKING API CHANGE: allow client to specify memory allocation rou…
authored
118 /* copy in pointers to allocation routines */
119 memcpy((void *) &(g->alloc), (void *) afs, sizeof(yajl_alloc_funcs));
120
0858bb3 @lloyd rework programmatic configuration of yajl. both generator and parser …
authored
121 g->print = (yajl_print_t)&yajl_buf_append;
122 g->ctx = yajl_buf_alloc(&(g->alloc));
123 g->indentString = " ";
b78dd79 @lloyd * BREAKING API CHANGE: allow client to specify memory allocation rou…
authored
124
d553293 move all files to trunk.
lloydh authored
125 return g;
126 }
127
128 void
263adf5 @lloyd add generator reset, and ability to verify and reformat streams
authored
129 yajl_gen_reset(yajl_gen g, const char * sep)
130 {
131 g->depth = 0;
132 memset((void *) &(g->state), 0, sizeof(g->state));
133 if (sep != NULL) g->print(g->ctx, sep, strlen(sep));
134 }
135
136 void
d553293 move all files to trunk.
lloydh authored
137 yajl_gen_free(yajl_gen g)
138 {
32098df @brimworks Add API so that we can provide our own printer function instead of
brimworks authored
139 if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_free((yajl_buf)g->ctx);
b78dd79 @lloyd * BREAKING API CHANGE: allow client to specify memory allocation rou…
authored
140 YA_FREE(&(g->alloc), g);
d553293 move all files to trunk.
lloydh authored
141 }
142
143 #define INSERT_SEP \
144 if (g->state[g->depth] == yajl_gen_map_key || \
145 g->state[g->depth] == yajl_gen_in_array) { \
32098df @brimworks Add API so that we can provide our own printer function instead of
brimworks authored
146 g->print(g->ctx, ",", 1); \
0858bb3 @lloyd rework programmatic configuration of yajl. both generator and parser …
authored
147 if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); \
d553293 move all files to trunk.
lloydh authored
148 } else if (g->state[g->depth] == yajl_gen_map_val) { \
32098df @brimworks Add API so that we can provide our own printer function instead of
brimworks authored
149 g->print(g->ctx, ":", 1); \
0858bb3 @lloyd rework programmatic configuration of yajl. both generator and parser …
authored
150 if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, " ", 1); \
fc32052 @lloyd white space cleanup
authored
151 }
d553293 move all files to trunk.
lloydh authored
152
2d0df50 @lloyd integrate patches from Robert Varga to eliminate build warnings on 64…
authored
153 #define INSERT_WHITESPACE \
0858bb3 @lloyd rework programmatic configuration of yajl. both generator and parser …
authored
154 if ((g->flags & yajl_gen_beautify)) { \
d553293 move all files to trunk.
lloydh authored
155 if (g->state[g->depth] != yajl_gen_map_val) { \
2d0df50 @lloyd integrate patches from Robert Varga to eliminate build warnings on 64…
authored
156 unsigned int _i; \
157 for (_i=0;_i<g->depth;_i++) \
49116c9 @mirek LLVM warnings
mirek authored
158 g->print(g->ctx, \
159 g->indentString, \
160 (unsigned int)strlen(g->indentString)); \
d553293 move all files to trunk.
lloydh authored
161 } \
162 }
163
164 #define ENSURE_NOT_KEY \
0d9e358 @gno Don't generate numbers for keys. closes #13
gno authored
165 if (g->state[g->depth] == yajl_gen_map_key || \
49116c9 @mirek LLVM warnings
mirek authored
166 g->state[g->depth] == yajl_gen_map_start) { \
0d9e358 @gno Don't generate numbers for keys. closes #13
gno authored
167 return yajl_gen_keys_must_be_strings; \
168 } \
d553293 move all files to trunk.
lloydh authored
169
170 /* check that we're not complete, or in error state. in a valid state
171 * to be generating */
172 #define ENSURE_VALID_STATE \
173 if (g->state[g->depth] == yajl_gen_error) { \
174 return yajl_gen_in_error_state;\
175 } else if (g->state[g->depth] == yajl_gen_complete) { \
176 return yajl_gen_generation_complete; \
177 }
178
179 #define INCREMENT_DEPTH \
180 if (++(g->depth) >= YAJL_MAX_DEPTH) return yajl_max_depth_exceeded;
181
0d9e358 @gno Don't generate numbers for keys. closes #13
gno authored
182 #define DECREMENT_DEPTH \
71ce86b @lloyd add a custom api test facility
authored
183 if (--(g->depth) >= YAJL_MAX_DEPTH) return yajl_gen_generation_complete;
0d9e358 @gno Don't generate numbers for keys. closes #13
gno authored
184
d553293 move all files to trunk.
lloydh authored
185 #define APPENDED_ATOM \
186 switch (g->state[g->depth]) { \
187 case yajl_gen_start: \
188 g->state[g->depth] = yajl_gen_complete; \
189 break; \
190 case yajl_gen_map_start: \
191 case yajl_gen_map_key: \
192 g->state[g->depth] = yajl_gen_map_val; \
193 break; \
194 case yajl_gen_array_start: \
195 g->state[g->depth] = yajl_gen_in_array; \
196 break; \
197 case yajl_gen_map_val: \
198 g->state[g->depth] = yajl_gen_map_key; \
199 break; \
200 default: \
201 break; \
202 } \
203
204 #define FINAL_NEWLINE \
0858bb3 @lloyd rework programmatic configuration of yajl. both generator and parser …
authored
205 if ((g->flags & yajl_gen_beautify) && g->state[g->depth] == yajl_gen_complete) \
206 g->print(g->ctx, "\n", 1);
207
d553293 move all files to trunk.
lloydh authored
208 yajl_gen_status
d564f36 @lloyd re-introduce long long type for integer representation now that we re…
authored
209 yajl_gen_integer(yajl_gen g, long long int number)
d553293 move all files to trunk.
lloydh authored
210 {
211 char i[32];
212 ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
d564f36 @lloyd re-introduce long long type for integer representation now that we re…
authored
213 sprintf(i, "%lld", number);
49116c9 @mirek LLVM warnings
mirek authored
214 g->print(g->ctx, i, (unsigned int)strlen(i));
d553293 move all files to trunk.
lloydh authored
215 APPENDED_ATOM;
216 FINAL_NEWLINE;
217 return yajl_gen_status_ok;
218 }
219
e2d0806 @lloyd look for either _WIN32 or WIN32 on windows. closes #44
authored
220 #if defined(_WIN32) || defined(WIN32)
7452dc0 @lloyd fix win32 compilation after addition of isnan() isinf() checks, addre…
authored
221 #include <float.h>
222 #define isnan _isnan
720f9ae @lloyd fix flipped logic for isinf() definition on windows. [closes #10]
authored
223 #define isinf !_finite
7452dc0 @lloyd fix win32 compilation after addition of isnan() isinf() checks, addre…
authored
224 #endif
225
d553293 move all files to trunk.
lloydh authored
226 yajl_gen_status
227 yajl_gen_double(yajl_gen g, double number)
228 {
229 char i[32];
fc32052 @lloyd white space cleanup
authored
230 ENSURE_VALID_STATE; ENSURE_NOT_KEY;
3d77db1 @lloyd return an error from yajl_gen_double when client passes inf or nan. C…
authored
231 if (isnan(number) || isinf(number)) return yajl_gen_invalid_number;
232 INSERT_SEP; INSERT_WHITESPACE;
63bcb31 @lloyd yajl_gen_double now specifies precision to sprintf as %.20g to avoid …
authored
233 sprintf(i, "%.20g", number);
dca2535 @bovine update yajl_gen_double to enforce that serialized double values alway…
bovine authored
234 if (strspn(i, "0123456789-") == strlen(i)) {
235 strcat(i, ".0");
236 }
49116c9 @mirek LLVM warnings
mirek authored
237 g->print(g->ctx, i, (unsigned int)strlen(i));
d553293 move all files to trunk.
lloydh authored
238 APPENDED_ATOM;
8c00428 introduction of yajl_number callback to allow clients to handle arbit…
llooyd authored
239 FINAL_NEWLINE;
240 return yajl_gen_status_ok;
241 }
242
243 yajl_gen_status
390748e @lloyd use size_t rather than unsigned int for representing buffer lengths f…
authored
244 yajl_gen_number(yajl_gen g, const char * s, size_t l)
8c00428 introduction of yajl_number callback to allow clients to handle arbit…
llooyd authored
245 {
246 ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
32098df @brimworks Add API so that we can provide our own printer function instead of
brimworks authored
247 g->print(g->ctx, s, l);
8c00428 introduction of yajl_number callback to allow clients to handle arbit…
llooyd authored
248 APPENDED_ATOM;
249 FINAL_NEWLINE;
d553293 move all files to trunk.
lloydh authored
250 return yajl_gen_status_ok;
251 }
252
253 yajl_gen_status
254 yajl_gen_string(yajl_gen g, const unsigned char * str,
390748e @lloyd use size_t rather than unsigned int for representing buffer lengths f…
authored
255 size_t len)
d553293 move all files to trunk.
lloydh authored
256 {
9538bda @lloyd Add a generator feature to validate UTF8 strings as they are written …
authored
257 // if validation is enabled, check that the string is valid utf8
258 // XXX: This checking could be done a little faster, in the same pass as
259 // the string encoding
260 if (g->flags & yajl_gen_validate_utf8) {
261 if (!yajl_string_validate_utf8(str, len)) {
262 return yajl_gen_invalid_string;
263 }
264 }
d553293 move all files to trunk.
lloydh authored
265 ENSURE_VALID_STATE; INSERT_SEP; INSERT_WHITESPACE;
32098df @brimworks Add API so that we can provide our own printer function instead of
brimworks authored
266 g->print(g->ctx, "\"", 1);
266dd3e @lloyd add option for escaping the '/' (solidus) character. closes #28
authored
267 yajl_string_encode(g->print, g->ctx, str, len, g->flags & yajl_gen_escape_solidus);
32098df @brimworks Add API so that we can provide our own printer function instead of
brimworks authored
268 g->print(g->ctx, "\"", 1);
d553293 move all files to trunk.
lloydh authored
269 APPENDED_ATOM;
270 FINAL_NEWLINE;
271 return yajl_gen_status_ok;
272 }
273
274 yajl_gen_status
275 yajl_gen_null(yajl_gen g)
276 {
277 ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
32098df @brimworks Add API so that we can provide our own printer function instead of
brimworks authored
278 g->print(g->ctx, "null", strlen("null"));
d553293 move all files to trunk.
lloydh authored
279 APPENDED_ATOM;
280 FINAL_NEWLINE;
281 return yajl_gen_status_ok;
282 }
283
284 yajl_gen_status
285 yajl_gen_bool(yajl_gen g, int boolean)
286 {
287 const char * val = boolean ? "true" : "false";
288
289 ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
49116c9 @mirek LLVM warnings
mirek authored
290 g->print(g->ctx, val, (unsigned int)strlen(val));
d553293 move all files to trunk.
lloydh authored
291 APPENDED_ATOM;
292 FINAL_NEWLINE;
293 return yajl_gen_status_ok;
294 }
295
296 yajl_gen_status
297 yajl_gen_map_open(yajl_gen g)
298 {
299 ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
fc32052 @lloyd white space cleanup
authored
300 INCREMENT_DEPTH;
301
d553293 move all files to trunk.
lloydh authored
302 g->state[g->depth] = yajl_gen_map_start;
32098df @brimworks Add API so that we can provide our own printer function instead of
brimworks authored
303 g->print(g->ctx, "{", 1);
0858bb3 @lloyd rework programmatic configuration of yajl. both generator and parser …
authored
304 if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);
d553293 move all files to trunk.
lloydh authored
305 FINAL_NEWLINE;
306 return yajl_gen_status_ok;
307 }
308
309 yajl_gen_status
310 yajl_gen_map_close(yajl_gen g)
311 {
fc32052 @lloyd white space cleanup
authored
312 ENSURE_VALID_STATE;
0d9e358 @gno Don't generate numbers for keys. closes #13
gno authored
313 DECREMENT_DEPTH;
fc32052 @lloyd white space cleanup
authored
314
0858bb3 @lloyd rework programmatic configuration of yajl. both generator and parser …
authored
315 if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);
d553293 move all files to trunk.
lloydh authored
316 APPENDED_ATOM;
317 INSERT_WHITESPACE;
32098df @brimworks Add API so that we can provide our own printer function instead of
brimworks authored
318 g->print(g->ctx, "}", 1);
d553293 move all files to trunk.
lloydh authored
319 FINAL_NEWLINE;
320 return yajl_gen_status_ok;
321 }
322
323 yajl_gen_status
324 yajl_gen_array_open(yajl_gen g)
325 {
326 ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
fc32052 @lloyd white space cleanup
authored
327 INCREMENT_DEPTH;
d553293 move all files to trunk.
lloydh authored
328 g->state[g->depth] = yajl_gen_array_start;
32098df @brimworks Add API so that we can provide our own printer function instead of
brimworks authored
329 g->print(g->ctx, "[", 1);
0858bb3 @lloyd rework programmatic configuration of yajl. both generator and parser …
authored
330 if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);
d553293 move all files to trunk.
lloydh authored
331 FINAL_NEWLINE;
332 return yajl_gen_status_ok;
333 }
334
335 yajl_gen_status
336 yajl_gen_array_close(yajl_gen g)
337 {
338 ENSURE_VALID_STATE;
0d9e358 @gno Don't generate numbers for keys. closes #13
gno authored
339 DECREMENT_DEPTH;
0858bb3 @lloyd rework programmatic configuration of yajl. both generator and parser …
authored
340 if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);
d553293 move all files to trunk.
lloydh authored
341 APPENDED_ATOM;
342 INSERT_WHITESPACE;
32098df @brimworks Add API so that we can provide our own printer function instead of
brimworks authored
343 g->print(g->ctx, "]", 1);
d553293 move all files to trunk.
lloydh authored
344 FINAL_NEWLINE;
345 return yajl_gen_status_ok;
346 }
347
348 yajl_gen_status
349 yajl_gen_get_buf(yajl_gen g, const unsigned char ** buf,
390748e @lloyd use size_t rather than unsigned int for representing buffer lengths f…
authored
350 size_t * len)
d553293 move all files to trunk.
lloydh authored
351 {
32098df @brimworks Add API so that we can provide our own printer function instead of
brimworks authored
352 if (g->print != (yajl_print_t)&yajl_buf_append) return yajl_gen_no_buf;
353 *buf = yajl_buf_data((yajl_buf)g->ctx);
354 *len = yajl_buf_len((yajl_buf)g->ctx);
d553293 move all files to trunk.
lloydh authored
355 return yajl_gen_status_ok;
356 }
357
358 void
359 yajl_gen_clear(yajl_gen g)
360 {
32098df @brimworks Add API so that we can provide our own printer function instead of
brimworks authored
361 if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_clear((yajl_buf)g->ctx);
d553293 move all files to trunk.
lloydh authored
362 }
Something went wrong with that request. Please try again.