2 * This file Copyright (C) 2008-2014 Mnemosyne LLC
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
7 * $Id: variant-json.c 14266 2014-04-27 23:10:01Z jordan $
12 #include <math.h> /* fabs() */
15 #include <errno.h> /* EILSEQ, EINVAL */
17 #include <event2/buffer.h> /* evbuffer_add() */
18 #include <event2/util.h> /* evutil_strtoll () */
20 #define JSONSL_STATE_USER_FIELDS /* no fields */
24 #define __LIBTRANSMISSION_VARIANT_MODULE___
25 #include "transmission.h"
26 #include "ConvertUTF.h"
32 #include "variant-common.h"
34 /* arbitrary value... this is much deeper than our code goes */
37 struct json_wrapper_data
44 struct evbuffer
* keybuf
;
45 struct evbuffer
* strbuf
;
51 get_node (struct jsonsl_st
* jsn
)
54 tr_variant
* node
= NULL
;
55 struct json_wrapper_data
* data
= jsn
->data
;
57 parent
= tr_ptrArrayEmpty (&data
->stack
)
59 : tr_ptrArrayBack (&data
->stack
);
65 else if (tr_variantIsList (parent
))
67 node
= tr_variantListAdd (parent
);
69 else if (tr_variantIsDict (parent
) && (data
->key
!=NULL
))
71 node
= tr_variantDictAdd (parent
, tr_quark_new (data
->key
, data
->keylen
));
82 error_handler (jsonsl_t jsn
,
84 struct jsonsl_state_st
* state UNUSED
,
85 const jsonsl_char_t
* buf
)
87 struct json_wrapper_data
* data
= jsn
->data
;
91 tr_logAddError ("JSON parse failed in %s at pos %"TR_PRIuSIZE
": %s -- remaining text \"%.16s\"",
94 jsonsl_strerror (error
),
99 tr_logAddError ("JSON parse failed at pos %"TR_PRIuSIZE
": %s -- remaining text \"%.16s\"",
101 jsonsl_strerror (error
),
105 data
->error
= EILSEQ
;
109 error_callback (jsonsl_t jsn
,
110 jsonsl_error_t error
,
111 struct jsonsl_state_st
* state
,
114 error_handler (jsn
, error
, state
, at
);
119 action_callback_PUSH (jsonsl_t jsn
,
120 jsonsl_action_t action UNUSED
,
121 struct jsonsl_state_st
* state
,
122 const jsonsl_char_t
* buf UNUSED
)
125 struct json_wrapper_data
* data
= jsn
->data
;
130 data
->has_content
= true;
131 node
= get_node (jsn
);
132 tr_variantInitList (node
, 0);
133 tr_ptrArrayAppend (&data
->stack
, node
);
136 case JSONSL_T_OBJECT
:
137 data
->has_content
= true;
138 node
= get_node (jsn
);
139 tr_variantInitDict (node
, 0);
140 tr_ptrArrayAppend (&data
->stack
, node
);
144 /* nothing else interesting on push */
149 /* like sscanf(in+2, "%4x", &val) but less slow */
151 decode_hex_string (const char * in
, unsigned int * setme
)
153 unsigned int val
= 0;
154 const char * const end
= in
+ 6;
157 assert (in
[0] == '\\');
158 assert (in
[1] == 'u');
164 if (('0'<=*in
) && (*in
<='9'))
166 else if (('a'<=*in
) && (*in
<='f'))
167 val
+= (*in
-'a') + 10u;
168 else if (('A'<=*in
) && (*in
<='F'))
169 val
+= (*in
-'A') + 10u;
180 extract_escaped_string (const char * in
,
183 struct evbuffer
* buf
)
185 const char * const in_end
= in
+ in_len
;
187 evbuffer_drain (buf
, evbuffer_get_length (buf
));
191 bool unescaped
= false;
193 if (*in
=='\\' && in_end
-in
>=2)
197 case 'b' : evbuffer_add (buf
, "\b", 1); in
+=2; unescaped
= true; break;
198 case 'f' : evbuffer_add (buf
, "\f", 1); in
+=2; unescaped
= true; break;
199 case 'n' : evbuffer_add (buf
, "\n", 1); in
+=2; unescaped
= true; break;
200 case 'r' : evbuffer_add (buf
, "\r", 1); in
+=2; unescaped
= true; break;
201 case 't' : evbuffer_add (buf
, "\t", 1); in
+=2; unescaped
= true; break;
202 case '/' : evbuffer_add (buf
, "/" , 1); in
+=2; unescaped
= true; break;
203 case '"' : evbuffer_add (buf
, "\"" , 1); in
+=2; unescaped
= true; break;
204 case '\\': evbuffer_add (buf
, "\\", 1); in
+=2; unescaped
= true; break;
207 if (in_end
- in
>= 6)
209 unsigned int val
= 0;
211 if (decode_hex_string (in
, &val
))
213 UTF32 str32_buf
[2] = { val
, 0 };
214 const UTF32
* str32_walk
= str32_buf
;
215 const UTF32
* str32_end
= str32_buf
+ 1;
216 UTF8 str8_buf
[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
217 UTF8
* str8_walk
= str8_buf
;
218 UTF8
* str8_end
= str8_buf
+ 8;
220 if (ConvertUTF32toUTF8 (&str32_walk
, str32_end
, &str8_walk
, str8_end
, 0) == 0)
222 const size_t len
= str8_walk
- str8_buf
;
223 evbuffer_add (buf
, str8_buf
, len
);
237 evbuffer_add (buf
, in
, 1);
242 *len
= evbuffer_get_length (buf
);
243 return (char*) evbuffer_pullup (buf
, -1);
247 extract_string (jsonsl_t jsn
,
248 struct jsonsl_state_st
* state
,
250 struct evbuffer
* buf
)
253 const char * in_begin
;
257 /* figure out where the string is */
258 in_begin
= jsn
->base
+ state
->pos_begin
;
259 if (*in_begin
== '"')
261 in_end
= jsn
->base
+ state
->pos_cur
;
262 in_len
= in_end
- in_begin
;
264 if (memchr (in_begin
, '\\', in_len
) == NULL
)
266 /* it's not escaped */
272 ret
= extract_escaped_string (in_begin
, in_len
, len
, buf
);
279 action_callback_POP (jsonsl_t jsn
,
280 jsonsl_action_t action UNUSED
,
281 struct jsonsl_state_st
* state
,
282 const jsonsl_char_t
* buf UNUSED
)
284 struct json_wrapper_data
* data
= jsn
->data
;
286 if (state
->type
== JSONSL_T_STRING
)
289 const char * str
= extract_string (jsn
, state
, &len
, data
->strbuf
);
290 tr_variantInitStr (get_node (jsn
), str
, len
);
291 data
->has_content
= true;
293 else if (state
->type
== JSONSL_T_HKEY
)
295 data
->has_content
= true;
296 data
->key
= extract_string (jsn
, state
, &data
->keylen
, data
->keybuf
);
298 else if ((state
->type
== JSONSL_T_LIST
) || (state
->type
== JSONSL_T_OBJECT
))
300 tr_ptrArrayPop (&data
->stack
);
302 else if (state
->type
== JSONSL_T_SPECIAL
)
304 if (state
->special_flags
& JSONSL_SPECIALf_NUMNOINT
)
306 const char * begin
= jsn
->base
+ state
->pos_begin
;
307 data
->has_content
= true;
308 tr_variantInitReal (get_node (jsn
), strtod (begin
, NULL
));
310 else if (state
->special_flags
& JSONSL_SPECIALf_NUMERIC
)
312 const char * begin
= jsn
->base
+ state
->pos_begin
;
313 data
->has_content
= true;
314 tr_variantInitInt (get_node (jsn
), evutil_strtoll (begin
, NULL
, 10));
316 else if (state
->special_flags
& JSONSL_SPECIALf_BOOLEAN
)
318 const bool b
= (state
->special_flags
& JSONSL_SPECIALf_TRUE
) != 0;
319 data
->has_content
= true;
320 tr_variantInitBool (get_node (jsn
), b
);
322 else if (state
->special_flags
& JSONSL_SPECIALf_NULL
)
324 data
->has_content
= true;
325 tr_variantInitQuark (get_node (jsn
), TR_KEY_NONE
);
331 tr_jsonParse (const char * source
,
334 tr_variant
* setme_variant
,
335 const char ** setme_end
)
339 struct json_wrapper_data data
;
341 jsn
= jsonsl_new (MAX_DEPTH
);
342 jsn
->action_callback_PUSH
= action_callback_PUSH
;
343 jsn
->action_callback_POP
= action_callback_POP
;
344 jsn
->error_callback
= error_callback
;
346 jsonsl_enable_all_callbacks (jsn
);
349 data
.has_content
= false;
351 data
.top
= setme_variant
;
352 data
.stack
= TR_PTR_ARRAY_INIT
;
353 data
.source
= source
;
354 data
.keybuf
= evbuffer_new ();
355 data
.strbuf
= evbuffer_new ();
358 jsonsl_feed (jsn
, vbuf
, len
);
360 /* EINVAL if there was no content */
361 if (!data
.error
&& !data
.has_content
)
364 /* maybe set the end ptr */
366 *setme_end
= ((const char*)vbuf
) + jsn
->pos
;
370 evbuffer_free (data
.keybuf
);
371 evbuffer_free (data
.strbuf
);
372 tr_ptrArrayDestruct (&data
.stack
, NULL
);
373 jsonsl_destroy (jsn
);
392 struct evbuffer
* out
;
396 jsonIndent (struct jsonWalk
* data
)
398 static char buf
[1024] = { '\0' };
401 memset (buf
, ' ', sizeof(buf
));
406 evbuffer_add (data
->out
, buf
, tr_list_size(data
->parents
)*4 + 1);
410 jsonChildFunc (struct jsonWalk
* data
)
412 if (data
->parents
&& data
->parents
->data
)
414 struct ParentState
* pstate
= data
->parents
->data
;
416 switch (pstate
->variantType
)
418 case TR_VARIANT_TYPE_DICT
:
420 const int i
= pstate
->childIndex
++;
423 evbuffer_add (data
->out
, ": ", data
->doIndent
? 2 : 1);
427 const bool isLast
= pstate
->childIndex
== pstate
->childCount
;
431 evbuffer_add (data
->out
, ", ", data
->doIndent
? 2 : 1);
438 case TR_VARIANT_TYPE_LIST
:
440 const bool isLast
= ++pstate
->childIndex
== pstate
->childCount
;
443 evbuffer_add (data
->out
, ", ", data
->doIndent
? 2 : 1);
456 jsonPushParent (struct jsonWalk
* data
,
457 const tr_variant
* v
)
459 struct ParentState
* pstate
= tr_new (struct ParentState
, 1);
461 pstate
->variantType
= v
->type
;
462 pstate
->childIndex
= 0;
463 pstate
->childCount
= v
->val
.l
.count
;
464 if (tr_variantIsDict (v
))
465 pstate
->childCount
*= 2;
467 tr_list_prepend (&data
->parents
, pstate
);
471 jsonPopParent (struct jsonWalk
* data
)
473 tr_free (tr_list_pop_front (&data
->parents
));
477 jsonIntFunc (const tr_variant
* val
, void * vdata
)
479 struct jsonWalk
* data
= vdata
;
480 evbuffer_add_printf (data
->out
, "%" PRId64
, val
->val
.i
);
481 jsonChildFunc (data
);
485 jsonBoolFunc (const tr_variant
* val
,
488 struct jsonWalk
* data
= vdata
;
491 evbuffer_add (data
->out
, "true", 4);
493 evbuffer_add (data
->out
, "false", 5);
495 jsonChildFunc (data
);
499 jsonRealFunc (const tr_variant
* val
,
502 struct jsonWalk
* data
= vdata
;
504 if (fabs (val
->val
.d
- (int)val
->val
.d
) < 0.00001)
505 evbuffer_add_printf (data
->out
, "%d", (int)val
->val
.d
);
507 evbuffer_add_printf (data
->out
, "%.4f", tr_truncd (val
->val
.d
, 4));
509 jsonChildFunc (data
);
513 jsonStringFunc (const tr_variant
* val
,
519 struct evbuffer_iovec vec
[1];
520 struct jsonWalk
* data
= vdata
;
523 const unsigned char * it
;
524 const unsigned char * end
;
526 tr_variantGetStr (val
, &str
, &len
);
527 it
= (const unsigned char *) str
;
530 evbuffer_reserve_space (data
->out
, len
* 4, vec
, 1);
531 out
= vec
[0].iov_base
;
532 outend
= out
+ vec
[0].iov_len
;
537 for (; it
!=end
; ++it
)
541 case '\b': *outwalk
++ = '\\'; *outwalk
++ = 'b'; break;
542 case '\f': *outwalk
++ = '\\'; *outwalk
++ = 'f'; break;
543 case '\n': *outwalk
++ = '\\'; *outwalk
++ = 'n'; break;
544 case '\r': *outwalk
++ = '\\'; *outwalk
++ = 'r'; break;
545 case '\t': *outwalk
++ = '\\'; *outwalk
++ = 't'; break;
546 case '"' : *outwalk
++ = '\\'; *outwalk
++ = '"'; break;
547 case '\\': *outwalk
++ = '\\'; *outwalk
++ = '\\'; break;
556 const UTF8
* tmp
= it
;
557 UTF32 buf
[1] = { 0 };
559 ConversionResult result
= ConvertUTF8toUTF32 (&tmp
, end
, &u32
, buf
+ 1, 0);
560 if (((result
==conversionOK
) || (result
==targetExhausted
)) && (tmp
!=it
))
562 outwalk
+= tr_snprintf (outwalk
, outend
-outwalk
, "\\u%04x", (unsigned int)buf
[0]);
571 vec
[0].iov_len
= outwalk
- out
;
572 evbuffer_commit_space (data
->out
, vec
, 1);
574 jsonChildFunc (data
);
578 jsonDictBeginFunc (const tr_variant
* val
,
581 struct jsonWalk
* data
= vdata
;
583 jsonPushParent (data
, val
);
584 evbuffer_add (data
->out
, "{", 1);
585 if (val
->val
.l
.count
)
590 jsonListBeginFunc (const tr_variant
* val
,
593 const size_t nChildren
= tr_variantListSize (val
);
594 struct jsonWalk
* data
= vdata
;
596 jsonPushParent (data
, val
);
597 evbuffer_add (data
->out
, "[", 1);
603 jsonContainerEndFunc (const tr_variant
* val
,
606 struct jsonWalk
* data
= vdata
;
607 bool emptyContainer
= false;
609 jsonPopParent (data
);
613 if (tr_variantIsDict (val
))
614 evbuffer_add (data
->out
, "}", 1);
616 evbuffer_add (data
->out
, "]", 1);
618 jsonChildFunc (data
);
621 static const struct VariantWalkFuncs walk_funcs
= { jsonIntFunc
,
627 jsonContainerEndFunc
};
630 tr_variantToBufJson (const tr_variant
* top
, struct evbuffer
* buf
, bool lean
)
632 struct jsonWalk data
;
634 data
.doIndent
= !lean
;
638 tr_variantWalk (top
, &walk_funcs
, &data
, true);
640 if (evbuffer_get_length (buf
))
641 evbuffer_add_printf (buf
, "\n");