source: trunk/libtransmission/variant-json.c

Last change on this file was 14644, checked in by mikedld, 5 years ago

Remove useless checks and definitions (C99)

Now that MSVC support for C99 is quite good, remove previously needed but
now unused checks and definitions, like PRI* format macros (including
PRIdMAX and TR_PRIuSIZE, replaced with %jd and %zu) and inline macro.
Also, remove ssize_t typedef and replace few occurences with ev_ssize_t.
Also, remove check for stdbool.h availability (guaranteed by C99) and
include it unconditionally (except when in C++ mode).

  • Property svn:keywords set to Date Rev Author Id
File size: 16.3 KB
Line 
1/*
2 * This file Copyright (C) 2008-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: variant-json.c 14644 2015-12-29 19:37:31Z mikedld $
8 */
9
10#include <assert.h>
11#include <ctype.h>
12#include <math.h> /* fabs() */
13#include <stdio.h>
14#include <string.h>
15#include <errno.h> /* EILSEQ, EINVAL */
16
17#include <event2/buffer.h> /* evbuffer_add() */
18#include <event2/util.h> /* evutil_strtoll () */
19
20#define JSONSL_STATE_USER_FIELDS /* no fields */
21#include "jsonsl.h"
22#include "jsonsl.c"
23
24#define __LIBTRANSMISSION_VARIANT_MODULE__
25#include "transmission.h"
26#include "ConvertUTF.h"
27#include "list.h"
28#include "log.h"
29#include "ptrarray.h"
30#include "utils.h"
31#include "variant.h"
32#include "variant-common.h"
33
34/* arbitrary value... this is much deeper than our code goes */
35#define MAX_DEPTH 64
36
37struct json_wrapper_data
38{
39  int error;
40  bool has_content;
41  tr_variant * top;
42  const char * key;
43  size_t keylen;
44  struct evbuffer * keybuf;
45  struct evbuffer * strbuf;
46  const char * source;
47  tr_ptrArray stack;
48};
49
50static tr_variant*
51get_node (struct jsonsl_st * jsn)
52{
53  tr_variant * parent;
54  tr_variant * node = NULL;
55  struct json_wrapper_data * data = jsn->data;
56
57  parent = tr_ptrArrayEmpty (&data->stack)
58         ? NULL
59         : tr_ptrArrayBack (&data->stack);
60
61  if (!parent)
62    {
63      node = data->top;
64    }
65  else if (tr_variantIsList (parent))
66    {
67      node = tr_variantListAdd (parent);
68    }
69  else if (tr_variantIsDict (parent) && (data->key!=NULL))
70    {
71      node = tr_variantDictAdd (parent, tr_quark_new (data->key, data->keylen));
72
73      data->key = NULL;
74      data->keylen = 0;
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_logAddError ("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_logAddError ("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  unsigned int val = 0;
154  const char * const end = in + 6;
155
156  assert (in != NULL);
157  assert (in[0] == '\\');
158  assert (in[1] == 'u');
159  in += 2;
160
161  do
162    {
163      val <<= 4;
164      if (('0'<=*in) && (*in<='9'))
165        val += (*in-'0');
166      else if (('a'<=*in) && (*in<='f'))
167        val += (*in-'a') + 10u;
168      else if (('A'<=*in) && (*in<='F'))
169        val += (*in-'A') + 10u;
170      else
171        return false;
172    }
173  while (++in != end);
174
175  *setme = val;
176  return true;
177}
178
179static char*
180extract_escaped_string (const char       * in,
181                        size_t             in_len,
182                        size_t           * len,
183                        struct evbuffer  * buf)
184{
185  const char * const in_end = in + in_len;
186
187  evbuffer_drain (buf, evbuffer_get_length (buf));
188
189  while (in < in_end)
190    {
191      bool unescaped = false;
192
193      if (*in=='\\' && in_end-in>=2)
194        {
195          switch (in[1])
196            {
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;
205              case 'u':
206                {
207                  if (in_end - in >= 6)
208                    {
209                      unsigned int val = 0;
210
211                      if (decode_hex_string (in, &val))
212                        {
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;
219
220                          if (ConvertUTF32toUTF8 (&str32_walk, str32_end, &str8_walk, str8_end, 0) == 0)
221                            {
222                              const size_t len = str8_walk - str8_buf;
223                              evbuffer_add (buf, str8_buf, len);
224                              unescaped = true;
225                            }
226
227                          in += 6;
228                          break;
229                        }
230                    }
231                }
232            }
233        }
234
235      if (!unescaped)
236        {
237          evbuffer_add (buf, in, 1);
238          ++in;
239        }
240    }
241
242  *len = evbuffer_get_length (buf);
243  return (char*) evbuffer_pullup (buf, -1);
244}
245
246static const char *
247extract_string (jsonsl_t                  jsn,
248                struct jsonsl_state_st  * state,
249                size_t                  * len,
250                struct evbuffer         * buf)
251{
252  const char * ret;
253  const char * in_begin;
254  const char * in_end;
255  size_t in_len;
256
257  /* figure out where the string is */
258  in_begin = jsn->base + state->pos_begin;
259  if (*in_begin == '"')
260    in_begin++;
261  in_end = jsn->base + state->pos_cur;
262  in_len = in_end - in_begin;
263
264  if (memchr (in_begin, '\\', in_len) == NULL)
265    {
266      /* it's not escaped */
267      ret = in_begin;
268      *len = in_len;
269    }
270  else
271    {
272      ret = extract_escaped_string (in_begin, in_len, len, buf);
273    }
274
275  return ret;
276}
277
278static void
279action_callback_POP (jsonsl_t                  jsn,
280                     jsonsl_action_t           action  UNUSED,
281                     struct jsonsl_state_st  * state,
282                     const jsonsl_char_t     * buf     UNUSED)
283{
284  struct json_wrapper_data * data = jsn->data;
285
286  if (state->type == JSONSL_T_STRING)
287    {
288      size_t len;
289      const char * str = extract_string (jsn, state, &len, data->strbuf);
290      tr_variantInitStr (get_node (jsn), str, len);
291      data->has_content = true;
292    }
293  else if (state->type == JSONSL_T_HKEY)
294    {
295      data->has_content = true;
296      data->key = extract_string (jsn, state, &data->keylen, data->keybuf);
297    }
298  else if ((state->type == JSONSL_T_LIST) || (state->type == JSONSL_T_OBJECT))
299    {
300      tr_ptrArrayPop (&data->stack);
301    }
302  else if (state->type == JSONSL_T_SPECIAL)
303    {
304      if (state->special_flags & JSONSL_SPECIALf_NUMNOINT)
305        {
306          const char * begin = jsn->base + state->pos_begin;
307          data->has_content = true;
308          tr_variantInitReal (get_node (jsn), strtod (begin, NULL));
309        }
310      else if (state->special_flags & JSONSL_SPECIALf_NUMERIC)
311        {
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));
315        }
316      else if (state->special_flags & JSONSL_SPECIALf_BOOLEAN)
317        {
318          const bool b = (state->special_flags & JSONSL_SPECIALf_TRUE) != 0;
319          data->has_content = true;
320          tr_variantInitBool (get_node (jsn), b);
321        }
322      else if (state->special_flags & JSONSL_SPECIALf_NULL)
323        {
324          data->has_content = true;
325          tr_variantInitQuark (get_node (jsn), TR_KEY_NONE);
326        }
327    }
328}
329
330int
331tr_jsonParse (const char     * source,
332              const void     * vbuf,
333              size_t           len,
334              tr_variant     * setme_variant,
335              const char    ** setme_end)
336{
337  int error;
338  jsonsl_t jsn;
339  struct json_wrapper_data data;
340
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;
345  jsn->data = &data;
346  jsonsl_enable_all_callbacks (jsn);
347
348  data.error = 0;
349  data.has_content = false;
350  data.key = NULL;
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 ();
356
357  /* parse it */
358  jsonsl_feed (jsn, vbuf, len);
359
360  /* EINVAL if there was no content */
361  if (!data.error && !data.has_content)
362    data.error = EINVAL;
363
364  /* maybe set the end ptr */
365  if (setme_end)
366    *setme_end = ((const char*)vbuf) + jsn->pos;
367
368  /* cleanup */
369  error = data.error;
370  evbuffer_free (data.keybuf);
371  evbuffer_free (data.strbuf);
372  tr_ptrArrayDestruct (&data.stack, NULL);
373  jsonsl_destroy (jsn);
374  return error;
375}
376
377/****
378*****
379****/
380
381struct ParentState
382{
383  int variantType;
384  int childIndex;
385  int childCount;
386};
387
388struct jsonWalk
389{
390  bool doIndent;
391  tr_list * parents;
392  struct evbuffer *  out;
393};
394
395static void
396jsonIndent (struct jsonWalk * data)
397{
398  static char buf[1024] = { '\0' };
399  if (!*buf)
400    {
401      memset (buf, ' ', sizeof(buf));
402      buf[0] = '\n';
403    }
404
405  if (data->doIndent)
406    evbuffer_add (data->out, buf, tr_list_size(data->parents)*4 + 1);
407}
408
409static void
410jsonChildFunc (struct jsonWalk * data)
411{
412  if (data->parents && data->parents->data)
413    {
414      struct ParentState * pstate = data->parents->data;
415
416      switch (pstate->variantType)
417        {
418          case TR_VARIANT_TYPE_DICT:
419            {
420              const int i = pstate->childIndex++;
421              if (! (i % 2))
422                {
423                  evbuffer_add (data->out, ": ", data->doIndent ? 2 : 1);
424                }
425              else
426                {
427                  const bool isLast = pstate->childIndex == pstate->childCount;
428
429                  if (!isLast)
430                    {
431                      evbuffer_add (data->out, ",", 1);
432                      jsonIndent (data);
433                    }
434                }
435              break;
436            }
437
438          case TR_VARIANT_TYPE_LIST:
439            {
440              const bool isLast = ++pstate->childIndex == pstate->childCount;
441              if (!isLast)
442                {
443                  evbuffer_add (data->out, ",", 1);
444                  jsonIndent (data);
445                }
446              break;
447            }
448
449          default:
450            break;
451        }
452    }
453}
454
455static void
456jsonPushParent (struct jsonWalk  * data,
457                const tr_variant * v)
458{
459  struct ParentState * pstate = tr_new (struct ParentState, 1);
460
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;
466
467  tr_list_prepend (&data->parents, pstate);
468}
469
470static void
471jsonPopParent (struct jsonWalk * data)
472{
473  tr_free (tr_list_pop_front (&data->parents));
474}
475
476static void
477jsonIntFunc (const tr_variant * val, void * vdata)
478{
479  struct jsonWalk * data = vdata;
480  evbuffer_add_printf (data->out, "%" PRId64, val->val.i);
481  jsonChildFunc (data);
482}
483
484static void
485jsonBoolFunc (const tr_variant * val,
486              void             * vdata)
487{
488  struct jsonWalk * data = vdata;
489
490  if (val->val.b)
491    evbuffer_add (data->out, "true", 4);
492  else
493    evbuffer_add (data->out, "false", 5);
494
495  jsonChildFunc (data);
496}
497
498static void
499jsonRealFunc (const tr_variant * val,
500              void             * vdata)
501{
502  struct jsonWalk * data = vdata;
503
504  if (fabs (val->val.d - (int)val->val.d) < 0.00001)
505    evbuffer_add_printf (data->out, "%d", (int)val->val.d);
506  else
507    evbuffer_add_printf (data->out, "%.4f", tr_truncd (val->val.d, 4));
508
509  jsonChildFunc (data);
510}
511
512static void
513jsonStringFunc (const tr_variant * val,
514                void             * vdata)
515{
516  char * out;
517  char * outwalk;
518  char * outend;
519  struct evbuffer_iovec vec[1];
520  struct jsonWalk * data = vdata;
521  const char * str;
522  size_t len;
523  const unsigned char * it;
524  const unsigned char * end;
525
526  tr_variantGetStr (val, &str, &len);
527  it = (const unsigned char *) str;
528  end = it + len;
529
530  evbuffer_reserve_space (data->out, len * 4, vec, 1);
531  out = vec[0].iov_base;
532  outend = out + vec[0].iov_len;
533
534  outwalk = out;
535  *outwalk++ = '"';
536
537  for (; it!=end; ++it)
538    {
539      switch (*it)
540        {
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;
548
549          default:
550            if (isprint (*it))
551              {
552                *outwalk++ = *it;
553              }
554            else
555              {
556                const UTF8 * tmp = it;
557                UTF32 buf[1] = { 0 };
558                UTF32 * u32 = buf;
559                ConversionResult result = ConvertUTF8toUTF32 (&tmp, end, &u32, buf + 1, 0);
560                if (((result==conversionOK) || (result==targetExhausted)) && (tmp!=it))
561                  {
562                    outwalk += tr_snprintf (outwalk, outend-outwalk, "\\u%04x", (unsigned int)buf[0]);
563                    it = tmp - 1;
564                  }
565              }
566            break;
567        }
568    }
569
570  *outwalk++ = '"';
571  vec[0].iov_len = outwalk - out;
572  evbuffer_commit_space (data->out, vec, 1);
573
574  jsonChildFunc (data);
575}
576
577static void
578jsonDictBeginFunc (const tr_variant * val,
579                   void             * vdata)
580{
581  struct jsonWalk * data = vdata;
582
583  jsonPushParent (data, val);
584  evbuffer_add (data->out, "{", 1);
585  if (val->val.l.count)
586    jsonIndent (data);
587}
588
589static void
590jsonListBeginFunc (const tr_variant * val,
591                   void             * vdata)
592{
593  const size_t nChildren = tr_variantListSize (val);
594  struct jsonWalk * data = vdata;
595
596  jsonPushParent (data, val);
597  evbuffer_add (data->out, "[", 1);
598  if (nChildren)
599    jsonIndent (data);
600}
601
602static void
603jsonContainerEndFunc (const tr_variant * val,
604                      void             * vdata)
605{
606  struct jsonWalk * data = vdata;
607  bool emptyContainer = false;
608
609  jsonPopParent (data);
610
611  if (!emptyContainer)
612    jsonIndent (data);
613  if (tr_variantIsDict (val))
614    evbuffer_add (data->out, "}", 1);
615  else /* list */
616    evbuffer_add (data->out, "]", 1);
617
618  jsonChildFunc (data);
619}
620
621static const struct VariantWalkFuncs walk_funcs = { jsonIntFunc,
622                                                    jsonBoolFunc,
623                                                    jsonRealFunc,
624                                                    jsonStringFunc,
625                                                    jsonDictBeginFunc,
626                                                    jsonListBeginFunc,
627                                                    jsonContainerEndFunc };
628
629void
630tr_variantToBufJson (const tr_variant * top, struct evbuffer * buf, bool lean)
631{
632  struct jsonWalk data;
633
634  data.doIndent = !lean;
635  data.out = buf;
636  data.parents = NULL;
637
638  tr_variantWalk (top, &walk_funcs, &data, true);
639
640  if (evbuffer_get_length (buf))
641    evbuffer_add_printf (buf, "\n");
642}
Note: See TracBrowser for help on using the repository browser.