source: trunk/libtransmission/variant-json.c @ 13669

Last change on this file since 13669 was 13669, checked in by jordan, 9 years ago

copyediting: indentation, whitespace

  • Property svn:keywords set to Date Rev Author Id
File size: 15.5 KB
Line 
1/*
2 * This file Copyright (C) Mnemosyne LLC
3 *
4 * This file is licensed by the GPL version 2. Works owned by the
5 * Transmission project are granted a special exemption to clause 2 (b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: variant-json.c 13669 2012-12-14 16:04:44Z jordan $
11 */
12
13#include <assert.h>
14#include <ctype.h>
15#include <math.h> /* fabs() */
16#include <stdio.h>
17#include <string.h>
18#include <errno.h> /* EILSEQ, EINVAL */
19
20#include <locale.h> /* setlocale() */
21
22#include <event2/buffer.h> /* evbuffer_add() */
23#include <event2/util.h> /* evutil_strtoll () */
24
25#define JSONSL_STATE_USER_FIELDS /* no fields */
26#include "jsonsl.h"
27#include "jsonsl.c"
28
29#define __LIBTRANSMISSION_VARIANT_MODULE___
30#include "transmission.h"
31#include "ConvertUTF.h"
32#include "list.h"
33#include "ptrarray.h"
34#include "utils.h"
35#include "variant.h"
36#include "variant-common.h"
37
38/* arbitrary value... this is much deeper than our code goes */
39#define MAX_DEPTH 64
40
41struct json_wrapper_data
42{
43  int error;
44  bool has_content;
45  tr_variant * top;
46  char * key;
47  const char * source;
48  tr_ptrArray stack;
49};
50
51static tr_variant*
52get_node (struct jsonsl_st * jsn)
53{
54  tr_variant * parent;
55  tr_variant * node = NULL;
56  struct json_wrapper_data * data = jsn->data;
57
58  parent = tr_ptrArrayEmpty (&data->stack)
59         ? NULL
60         : tr_ptrArrayBack (&data->stack);
61
62  if (!parent)
63    {
64      node = data->top;
65    }
66  else if (tr_variantIsList (parent))
67    {
68      node = tr_variantListAdd (parent);
69    }
70  else if (tr_variantIsDict (parent) && (data->key!=NULL))
71    {
72      node = tr_variantDictAdd (parent, data->key);
73      tr_free (data->key);
74      data->key = NULL;
75    }
76
77  return node;
78}
79
80
81static void
82error_handler (jsonsl_t                  jsn,
83               jsonsl_error_t            error,
84               struct jsonsl_state_st  * state   UNUSED,
85               const jsonsl_char_t     * buf)
86{
87  struct json_wrapper_data * data = jsn->data;
88
89  if (data->source)
90    {
91      tr_err ("JSON parse failed in %s at pos %zu: %s -- remaining text \"%.16s\"",
92              data->source,
93              jsn->pos,
94              jsonsl_strerror (error),
95              buf);
96    }
97  else
98    {
99      tr_err ("JSON parse failed at pos %zu: %s -- remaining text \"%.16s\"",
100              jsn->pos,
101              jsonsl_strerror (error),
102              buf);
103    }
104
105  data->error = EILSEQ;
106}
107
108static int
109error_callback (jsonsl_t                  jsn,
110                jsonsl_error_t            error,
111                struct jsonsl_state_st  * state,
112                jsonsl_char_t           * at)
113{
114  error_handler (jsn, error, state, at);
115  return 0; /* bail */
116}
117
118static void
119action_callback_PUSH (jsonsl_t                  jsn,
120                      jsonsl_action_t           action  UNUSED,
121                      struct jsonsl_state_st  * state,
122                      const jsonsl_char_t     * buf     UNUSED)
123{
124  tr_variant * node;
125  struct json_wrapper_data * data = jsn->data;
126
127  switch (state->type)
128    {
129      case JSONSL_T_LIST:
130        data->has_content = true;
131        node = get_node (jsn);
132        tr_variantInitList (node, 0);
133        tr_ptrArrayAppend (&data->stack, node);
134        break;
135
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);
141        break;
142
143      default:
144        /* nothing else interesting on push */
145        break;
146    }
147}
148
149/* like sscanf(in+2, "%4x", &val) but less slow */
150static bool
151decode_hex_string (const char * in, unsigned int * setme)
152{
153  bool success;
154  char buf[5];
155  char * end;
156
157  assert (in != NULL);
158  assert (in[0] == '\\');
159  assert (in[1] == 'u');
160
161  memcpy (buf, in+2, 4);
162  buf[4] = '\0';
163  *setme = strtoul (buf, &end, 16);
164  success = end == buf+4;
165
166  return success;
167}
168
169static char*
170extract_string (jsonsl_t jsn, struct jsonsl_state_st * state, size_t * len)
171{
172  const char * in_begin;
173  const char * in_end;
174  const char * in_it;
175  size_t out_buflen;
176  char * out_buf;
177  char * out_it;
178
179  in_begin = jsn->base + state->pos_begin;
180  if (*in_begin == '"')
181    in_begin++;
182  in_end = jsn->base + state->pos_cur;
183
184  out_buflen = (in_end-in_begin)*3 + 1;
185  out_buf = tr_new0 (char, out_buflen);
186  out_it = out_buf;
187
188  for (in_it=in_begin; in_it!=in_end;)
189    {
190      bool unescaped = false;
191
192      if (*in_it=='\\' && in_end-in_it>=2)
193        {
194          switch (in_it[1])
195            {
196              case 'b' : *out_it++ = '\b'; in_it+=2; unescaped = true; break;
197              case 'f' : *out_it++ = '\f'; in_it+=2; unescaped = true; break;
198              case 'n' : *out_it++ = '\n'; in_it+=2; unescaped = true; break;
199              case 'r' : *out_it++ = '\r'; in_it+=2; unescaped = true; break;
200              case 't' : *out_it++ = '\t'; in_it+=2; unescaped = true; break;
201              case '/' : *out_it++ = '/' ; in_it+=2; unescaped = true; break;
202              case '"' : *out_it++ = '"' ; in_it+=2; unescaped = true; break;
203              case '\\': *out_it++ = '\\'; in_it+=2; unescaped = true; break;
204              case 'u':
205                {
206                  if (in_end - in_it >= 6)
207                    {
208                      unsigned int val = 0;
209                      if (decode_hex_string (in_it, &val))
210                        {
211                          UTF32 str32_buf[2] = { val, 0 };
212                          const UTF32 * str32_walk = str32_buf;
213                          const UTF32 * str32_end = str32_buf + 1;
214                          UTF8 str8_buf[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
215                          UTF8 * str8_walk = str8_buf;
216                          UTF8 * str8_end = str8_buf + 8;
217
218                          if (ConvertUTF32toUTF8 (&str32_walk, str32_end, &str8_walk, str8_end, 0) == 0)
219                            {
220                              const size_t len = str8_walk - str8_buf;
221                              memcpy (out_it, str8_buf, len);
222                              out_it += len;
223                              unescaped = true;
224                            }
225
226                          in_it += 6;
227                          break;
228                        }
229                    }
230                }
231            }
232        }
233
234      if (!unescaped)
235        *out_it++ = *in_it++;
236    }
237
238  if (len != NULL)
239    *len = out_it - out_buf;
240
241  return out_buf;
242}
243
244static void
245action_callback_POP (jsonsl_t                  jsn,
246                     jsonsl_action_t           action  UNUSED,
247                     struct jsonsl_state_st  * state,
248                     const jsonsl_char_t     * buf     UNUSED)
249{
250  struct json_wrapper_data * data = jsn->data;
251
252  if (state->type == JSONSL_T_STRING)
253    {
254      size_t len = 0;
255      char * str = extract_string (jsn, state, &len);
256      tr_variantInitStr (get_node (jsn), str, len);
257      data->has_content = true;
258      tr_free (str);
259    }
260  else if (state->type == JSONSL_T_HKEY)
261    {
262      char * str = extract_string (jsn, state, NULL);
263      data->has_content = true;
264      data->key = str;
265    }
266  else if ((state->type == JSONSL_T_LIST) || (state->type == JSONSL_T_OBJECT))
267    {
268      tr_ptrArrayPop (&data->stack);
269    }
270  else if (state->type == JSONSL_T_SPECIAL)
271    {
272      if (state->special_flags & JSONSL_SPECIALf_NUMNOINT)
273        {
274          const char * begin = jsn->base + state->pos_begin;
275          data->has_content = true;
276          tr_variantInitReal (get_node (jsn), strtod (begin, NULL));
277        }
278      else if (state->special_flags & JSONSL_SPECIALf_NUMERIC)
279        {
280          const char * begin = jsn->base + state->pos_begin;
281          data->has_content = true;
282          tr_variantInitInt (get_node (jsn), evutil_strtoll (begin, NULL, 10));
283        }
284      else if (state->special_flags & JSONSL_SPECIALf_BOOLEAN)
285        {
286          const bool b = (state->special_flags & JSONSL_SPECIALf_TRUE) != 0;
287          data->has_content = true;
288          tr_variantInitBool (get_node (jsn), b);
289        }
290      else if (state->special_flags & JSONSL_SPECIALf_NULL)
291        {
292          data->has_content = true;
293          tr_variantInitStr (get_node (jsn), "", 0);
294        }
295    }
296}
297
298int
299tr_jsonParse (const char     * source,
300              const void     * vbuf,
301              size_t           len,
302              tr_variant        * setme_benc,
303              const char ** setme_end)
304{
305  int error;
306  jsonsl_t jsn;
307  struct json_wrapper_data data;
308
309  jsn = jsonsl_new (MAX_DEPTH);
310  jsn->action_callback_PUSH = action_callback_PUSH;
311  jsn->action_callback_POP = action_callback_POP;
312  jsn->error_callback = error_callback;
313  jsn->data = &data;
314  jsonsl_enable_all_callbacks (jsn);
315
316  data.error = 0;
317  data.has_content = false;
318  data.key = NULL;
319  data.top = setme_benc;
320  data.stack = TR_PTR_ARRAY_INIT;
321  data.source = source;
322
323  /* parse it */
324  jsonsl_feed (jsn, vbuf, len);
325
326  /* EINVAL if there was no content */
327  if (!data.error && !data.has_content)
328    data.error = EINVAL;
329
330  /* maybe set the end ptr */
331  if (setme_end)
332    *setme_end = ((const char*)vbuf) + jsn->pos;
333
334  /* cleanup */
335  error = data.error;
336  tr_ptrArrayDestruct (&data.stack, NULL);
337  jsonsl_destroy (jsn);
338  return error;
339}
340
341/****
342*****
343****/
344
345struct ParentState
346{
347  int bencType;
348  int childIndex;
349  int childCount;
350};
351
352struct jsonWalk
353{
354  bool doIndent;
355  tr_list * parents;
356  struct evbuffer *  out;
357};
358
359static void
360jsonIndent (struct jsonWalk * data)
361{
362  if (data->doIndent)
363    {
364      char buf[1024];
365      const int width = tr_list_size (data->parents) * 4;
366
367      buf[0] = '\n';
368      memset (buf+1, ' ', width);
369      evbuffer_add (data->out, buf, 1+width);
370    }
371}
372
373static void
374jsonChildFunc (struct jsonWalk * data)
375{
376  if (data->parents && data->parents->data)
377    {
378      struct ParentState * pstate = data->parents->data;
379
380      switch (pstate->bencType)
381        {
382          case TR_VARIANT_TYPE_DICT:
383            {
384              const int i = pstate->childIndex++;
385              if (! (i % 2))
386                {
387                  evbuffer_add (data->out, ": ", data->doIndent ? 2 : 1);
388                }
389              else
390                {
391                  const bool isLast = pstate->childIndex == pstate->childCount;
392
393                  if (!isLast)
394                    {
395                      evbuffer_add (data->out, ", ", data->doIndent ? 2 : 1);
396                      jsonIndent (data);
397                    }
398                }
399              break;
400            }
401
402          case TR_VARIANT_TYPE_LIST:
403            {
404              const bool isLast = ++pstate->childIndex == pstate->childCount;
405              if (!isLast)
406                {
407                  evbuffer_add (data->out, ", ", data->doIndent ? 2 : 1);
408                  jsonIndent (data);
409                }
410              break;
411            }
412
413          default:
414            break;
415        }
416    }
417}
418
419static void
420jsonPushParent (struct jsonWalk  * data,
421                const tr_variant * benc)
422{
423  struct ParentState * pstate = tr_new (struct ParentState, 1);
424
425  pstate->bencType = benc->type;
426  pstate->childIndex = 0;
427  pstate->childCount = benc->val.l.count;
428  tr_list_prepend (&data->parents, pstate);
429}
430
431static void
432jsonPopParent (struct jsonWalk * data)
433{
434  tr_free (tr_list_pop_front (&data->parents));
435}
436
437static void
438jsonIntFunc (const tr_variant * val, void * vdata)
439{
440  struct jsonWalk * data = vdata;
441  evbuffer_add_printf (data->out, "%" PRId64, val->val.i);
442  jsonChildFunc (data);
443}
444
445static void
446jsonBoolFunc (const tr_variant * val, void * vdata)
447{
448  struct jsonWalk * data = vdata;
449
450  if (val->val.b)
451    evbuffer_add (data->out, "true", 4);
452  else
453    evbuffer_add (data->out, "false", 5);
454
455  jsonChildFunc (data);
456}
457
458static void
459jsonRealFunc (const tr_variant * val, void * vdata)
460{
461  struct jsonWalk * data = vdata;
462  char locale[128];
463
464  if (fabs (val->val.d - (int)val->val.d) < 0.00001)
465    {
466      evbuffer_add_printf (data->out, "%d", (int)val->val.d);
467    }
468  else
469    {
470      /* json requires a '.' decimal point regardless of locale */
471      tr_strlcpy (locale, setlocale (LC_NUMERIC, NULL), sizeof (locale));
472      setlocale (LC_NUMERIC, "POSIX");
473      evbuffer_add_printf (data->out, "%.4f", tr_truncd (val->val.d, 4));
474      setlocale (LC_NUMERIC, locale);
475    }
476
477  jsonChildFunc (data);
478}
479
480static void
481jsonStringFunc (const tr_variant * val, void * vdata)
482{
483  char * out;
484  char * outwalk;
485  char * outend;
486  struct evbuffer_iovec vec[1];
487  struct jsonWalk * data = vdata;
488  const char * str;
489  size_t len;
490  const unsigned char * it;
491  const unsigned char * end;
492
493  tr_variantGetStr (val, &str, &len);
494  it = (const unsigned char *) str;
495  end = it + len;
496
497  evbuffer_reserve_space (data->out, len * 4, vec, 1);
498  out = vec[0].iov_base;
499  outend = out + vec[0].iov_len;
500
501  outwalk = out;
502  *outwalk++ = '"';
503
504  for (; it!=end; ++it)
505    {
506      switch (*it)
507        {
508          case '\b': *outwalk++ = '\\'; *outwalk++ = 'b'; break;
509          case '\f': *outwalk++ = '\\'; *outwalk++ = 'f'; break;
510          case '\n': *outwalk++ = '\\'; *outwalk++ = 'n'; break;
511          case '\r': *outwalk++ = '\\'; *outwalk++ = 'r'; break;
512          case '\t': *outwalk++ = '\\'; *outwalk++ = 't'; break;
513          case '"' : *outwalk++ = '\\'; *outwalk++ = '"'; break;
514          case '\\': *outwalk++ = '\\'; *outwalk++ = '\\'; break;
515
516          default:
517            if (isascii (*it))
518              {
519                *outwalk++ = *it;
520              }
521            else
522              {
523                const UTF8 * tmp = it;
524                UTF32 buf[1] = { 0 };
525                UTF32 * u32 = buf;
526                ConversionResult result = ConvertUTF8toUTF32 (&tmp, end, &u32, buf + 1, 0);
527                if (((result==conversionOK) || (result==targetExhausted)) && (tmp!=it))
528                  {
529                    outwalk += tr_snprintf (outwalk, outend-outwalk, "\\u%04x", (unsigned int)buf[0]);
530                    it = tmp - 1;
531                  }
532              }
533            break;
534        }
535    }
536
537  *outwalk++ = '"';
538  vec[0].iov_len = outwalk - out;
539  evbuffer_commit_space (data->out, vec, 1);
540
541  jsonChildFunc (data);
542}
543
544static void
545jsonDictBeginFunc (const tr_variant * val,
546                   void *          vdata)
547{
548  struct jsonWalk * data = vdata;
549
550  jsonPushParent (data, val);
551  evbuffer_add (data->out, "{", 1);
552  if (val->val.l.count)
553    jsonIndent (data);
554}
555
556static void
557jsonListBeginFunc (const tr_variant * val,
558                   void *          vdata)
559{
560  const size_t nChildren = tr_variantListSize (val);
561  struct jsonWalk * data = vdata;
562
563  jsonPushParent (data, val);
564  evbuffer_add (data->out, "[", 1);
565  if (nChildren)
566    jsonIndent (data);
567}
568
569static void
570jsonContainerEndFunc (const tr_variant * val,
571                      void *          vdata)
572{
573  struct jsonWalk * data = vdata;
574  int emptyContainer = false;
575
576  jsonPopParent (data);
577
578  if (!emptyContainer)
579    jsonIndent (data);
580  if (tr_variantIsDict (val))
581    evbuffer_add (data->out, "}", 1);
582  else /* list */
583    evbuffer_add (data->out, "]", 1);
584
585  jsonChildFunc (data);
586}
587
588static const struct VariantWalkFuncs walk_funcs = { jsonIntFunc,
589                                                    jsonBoolFunc,
590                                                    jsonRealFunc,
591                                                    jsonStringFunc,
592                                                    jsonDictBeginFunc,
593                                                    jsonListBeginFunc,
594                                                    jsonContainerEndFunc };
595
596void
597tr_variantToBufJson (const tr_variant * top, struct evbuffer * buf, bool lean)
598{
599  struct jsonWalk data;
600  data.doIndent = !lean;
601  data.out = buf;
602  data.parents = NULL;
603  tr_variantWalk (top, &walk_funcs, &data, true);
604  if (evbuffer_get_length (buf))
605    evbuffer_add_printf (buf, "\n");
606}
Note: See TracBrowser for help on using the repository browser.