Merge branch '1.1'
authorPetri Lehtinen <petri@digip.org>
Sun, 20 Dec 2009 19:18:27 +0000 (21:18 +0200)
committerPetri Lehtinen <petri@digip.org>
Mon, 21 Dec 2009 10:52:25 +0000 (12:52 +0200)
Conflicts:
configure.ac
doc/conf.py

1  2 
configure.ac
doc/conf.py
src/dump.c
src/load.c

diff --combined configure.ac
@@@ -1,5 -1,5 +1,5 @@@
  AC_PREREQ([2.59])
- AC_INIT([jansson], [1.1.2+], [petri@digip.org])
 -AC_INIT([jansson], [1.1.3], [petri@digip.org])
++AC_INIT([jansson], [1.1.3+], [petri@digip.org])
  
  AM_INIT_AUTOMAKE([1.10 foreign])
  
@@@ -23,8 -23,7 +23,8 @@@ AC_CONFIG_FILES(
          doc/Makefile
          src/Makefile
          test/Makefile
 -        test/testdata/Makefile
 -        test/testprogs/Makefile
 +        test/bin/Makefile
 +        test/suites/Makefile
 +        test/suites/api/Makefile
  ])
  AC_OUTPUT
diff --combined doc/conf.py
@@@ -52,7 -52,7 +52,7 @@@ copyright = u'2009, Petri Lehtinen
  # The short X.Y version.
  version = '1.1'
  # The full version, including alpha/beta/rc tags.
- release = '1.1.2+'
 -release = '1.1.3'
++release = '1.1.3+'
  
  # The language for content autogenerated by Sphinx. Refer to documentation
  # for a list of supported languages.
@@@ -69,7 -69,7 +69,7 @@@
  
  # List of directories, relative to source directory, that shouldn't be searched
  # for source files.
 -exclude_trees = ['.build']
 +exclude_trees = ['_build']
  
  # The reST default role (used for this markup: `text`) to use for all documents.
  #default_role = None
diff --combined src/dump.c
@@@ -9,13 -9,10 +9,13 @@@
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
 +#include <stdint.h>
 +#include <assert.h>
  
  #include <jansson.h>
  #include "jansson_private.h"
  #include "strbuffer.h"
 +#include "utf.h"
  
  #define MAX_INTEGER_STR_LENGTH  100
  #define MAX_REAL_STR_LENGTH     100
@@@ -45,7 -42,7 +45,7 @@@ static int dump_to_file(const char *buf
  /* 256 spaces (the maximum indentation size) */
  static char whitespace[] = "                                                                                                                                                                                                                                                                ";
  
 -static int dump_indent(unsigned long flags, int depth, dump_func dump, void *data)
 +static int dump_indent(unsigned long flags, int depth, int space, dump_func dump, void *data)
  {
      if(JSON_INDENT(flags) > 0)
      {
                  return -1;
          }
      }
 +    else if(space && !(flags & JSON_COMPACT))
 +    {
 +        return dump(" ", 1, data);
 +    }
      return 0;
  }
  
 -static int dump_string(const char *str, dump_func dump, void *data)
 +static int dump_string(const char *str, int ascii, dump_func dump, void *data)
  {
 -    const char *end;
 +    const char *pos, *end;
 +    int32_t codepoint;
  
      if(dump("\"", 1, data))
          return -1;
  
 -    end = str;
 +    end = pos = str;
      while(1)
      {
          const char *text;
 -        char seq[7];
 +        char seq[13];
          int length;
  
 -        while(*end && *end != '\\' && *end != '"' && (unsigned char)*end > 0x1F)
 -            end++;
 +        while(*end)
 +        {
 +            end = utf8_iterate(pos, &codepoint);
 +            if(!end)
 +                return -1;
 +
 +            /* mandatory escape or control char */
 +            if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
 +                break;
 +
 +            /* non-ASCII */
 +            if(ascii && codepoint > 0x7F)
 +                break;
 +
 +            pos = end;
 +        }
  
 -        if(end != str) {
 -            if(dump(str, end - str, data))
 +        if(pos != str) {
 +            if(dump(str, pos - str, data))
                  return -1;
          }
  
 -        if(!*end)
 +        if(end == pos)
              break;
  
          /* handle \, ", and control codes */
          length = 2;
 -        switch(*end)
 +        switch(codepoint)
          {
              case '\\': text = "\\\\"; break;
              case '\"': text = "\\\""; break;
              case '\t': text = "\\t"; break;
              default:
              {
 -                sprintf(seq, "\\u00%02x", *end);
 +                /* codepoint is in BMP */
 +                if(codepoint < 0x10000)
 +                {
 +                    sprintf(seq, "\\u%04x", codepoint);
 +                    length = 6;
 +                }
 +
 +                /* not in BMP -> construct a UTF-16 surrogate pair */
 +                else
 +                {
 +                    int32_t first, last;
 +
 +                    codepoint -= 0x10000;
 +                    first = 0xD800 | ((codepoint & 0xffc00) >> 10);
 +                    last = 0xDC00 | (codepoint & 0x003ff);
 +
 +                    sprintf(seq, "\\u%04x\\u%04x", first, last);
 +                    length = 12;
 +                }
 +
                  text = seq;
 -                length = 6;
                  break;
              }
          }
          if(dump(text, length, data))
              return -1;
  
 -        end++;
 -        str = end;
 +        str = pos = end;
      }
  
      return dump("\"", 1, data);
  }
  
 +static int object_key_cmp(const void *key1, const void *key2)
 +{
 +    return strcmp(*(const char **)key1, *(const char **)key2);
 +}
 +
  static int do_dump(const json_t *json, unsigned long flags, int depth,
                     dump_func dump, void *data)
  {
 +    int ascii = flags & JSON_ENSURE_ASCII ? 1 : 0;
 +
      switch(json_typeof(json)) {
          case JSON_NULL:
              return dump("null", 4, data);
              char buffer[MAX_REAL_STR_LENGTH];
              int size;
  
-             size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%0.17f", json_real_value(json));
+             size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%.17g",
+                             json_real_value(json));
              if(size >= MAX_REAL_STR_LENGTH)
                  return -1;
  
+             /* Make sure there's a dot or 'e' in the output. Otherwise
+                a real is converted to an integer when decoding */
+             if(strchr(buffer, '.') == NULL &&
+                strchr(buffer, 'e') == NULL)
+             {
+                 if(size + 2 >= MAX_REAL_STR_LENGTH) {
+                     /* No space to append ".0" */
+                     return -1;
+                 }
+                 buffer[size] = '.';
+                 buffer[size + 1] = '0';
+                 size += 2;
+             }
              return dump(buffer, size, data);
          }
  
          case JSON_STRING:
 -            return dump_string(json_string_value(json), dump, data);
 +            return dump_string(json_string_value(json), ascii, dump, data);
  
          case JSON_ARRAY:
          {
                  return -1;
              if(n == 0)
                  return dump("]", 1, data);
 -            if(dump_indent(flags, depth + 1, dump, data))
 +            if(dump_indent(flags, depth + 1, 0, dump, data))
                  return -1;
  
              for(i = 0; i < n; ++i) {
                  if(i < n - 1)
                  {
                      if(dump(",", 1, data) ||
 -                       dump_indent(flags, depth + 1, dump, data))
 +                       dump_indent(flags, depth + 1, 1, dump, data))
                          return -1;
                  }
                  else
                  {
 -                    if(dump_indent(flags, depth, dump, data))
 +                    if(dump_indent(flags, depth, 0, dump, data))
                          return -1;
                  }
              }
          {
              json_object_t *object;
              void *iter;
 +            const char *separator;
 +            int separator_length;
 +
 +            if(flags & JSON_COMPACT) {
 +                separator = ":";
 +                separator_length = 1;
 +            }
 +            else {
 +                separator = ": ";
 +                separator_length = 2;
 +            }
  
              /* detect circular references */
              object = json_to_object(json);
                  return -1;
              if(!iter)
                  return dump("}", 1, data);
 -            if(dump_indent(flags, depth + 1, dump, data))
 +            if(dump_indent(flags, depth + 1, 0, dump, data))
                  return -1;
  
 -            while(iter)
 +            if(flags & JSON_SORT_KEYS)
              {
 -                void *next = json_object_iter_next((json_t *)json, iter);
 +                /* Sort keys */
  
 -                dump_string(json_object_iter_key(iter), dump, data);
 -                if(dump(": ", 2, data) ||
 -                   do_dump(json_object_iter_value(iter), flags, depth + 1,
 -                           dump, data))
 +                const char **keys;
 +                unsigned int size;
 +                unsigned int i;
 +
 +                size = json_object_size(json);
 +                keys = malloc(size * sizeof(const char *));
 +                if(!keys)
                      return -1;
  
 -                if(next)
 +                i = 0;
 +                while(iter)
                  {
 -                    if(dump(",", 1, data) ||
 -                       dump_indent(flags, depth + 1, dump, data))
 -                        return -1;
 +                    keys[i] = json_object_iter_key(iter);
 +                    iter = json_object_iter_next((json_t *)json, iter);
 +                    i++;
                  }
 -                else
 +                assert(i == size);
 +
 +                qsort(keys, size, sizeof(const char *), object_key_cmp);
 +
 +                for(i = 0; i < size; i++)
                  {
 -                    if(dump_indent(flags, depth, dump, data))
 +                    const char *key;
 +                    json_t *value;
 +
 +                    key = keys[i];
 +                    value = json_object_get(json, key);
 +                    assert(value);
 +
 +                    dump_string(key, ascii, dump, data);
 +                    if(dump(separator, separator_length, data) ||
 +                       do_dump(value, flags, depth + 1, dump, data))
 +                    {
 +                        free(keys);
                          return -1;
 +                    }
 +
 +                    if(i < size - 1)
 +                    {
 +                        if(dump(",", 1, data) ||
 +                           dump_indent(flags, depth + 1, 1, dump, data))
 +                        {
 +                            free(keys);
 +                            return -1;
 +                        }
 +                    }
 +                    else
 +                    {
 +                        if(dump_indent(flags, depth, 0, dump, data))
 +                        {
 +                            free(keys);
 +                            return -1;
 +                        }
 +                    }
                  }
  
 -                iter = next;
 +                free(keys);
 +            }
 +            else
 +            {
 +                /* Don't sort keys */
 +
 +                while(iter)
 +                {
 +                    void *next = json_object_iter_next((json_t *)json, iter);
 +
 +                    dump_string(json_object_iter_key(iter), ascii, dump, data);
 +                    if(dump(separator, separator_length, data) ||
 +                       do_dump(json_object_iter_value(iter), flags, depth + 1,
 +                               dump, data))
 +                        return -1;
 +
 +                    if(next)
 +                    {
 +                        if(dump(",", 1, data) ||
 +                           dump_indent(flags, depth + 1, 1, dump, data))
 +                            return -1;
 +                    }
 +                    else
 +                    {
 +                        if(dump_indent(flags, depth, 0, dump, data))
 +                            return -1;
 +                    }
 +
 +                    iter = next;
 +                }
              }
  
              object->visited = 0;
@@@ -394,6 -285,11 +409,6 @@@ char *json_dumps(const json_t *json, un
          return NULL;
      }
  
 -    if(dump_to_strbuffer("\n", 1, (void *)&strbuff)) {
 -        strbuffer_close(&strbuff);
 -        return NULL;
 -    }
 -
      result = strdup(strbuffer_value(&strbuff));
      strbuffer_close(&strbuff);
  
@@@ -405,7 -301,9 +420,7 @@@ int json_dumpf(const json_t *json, FIL
      if(!json_is_array(json) && !json_is_object(json))
          return -1;
  
 -    if(do_dump(json, flags, 0, dump_to_file, (void *)output))
 -        return -1;
 -    return dump_to_file("\n", 1, (void *)output);
 +    return do_dump(json, flags, 0, dump_to_file, (void *)output);
  }
  
  int json_dump_file(const json_t *json, const char *path, unsigned long flags)
diff --combined src/load.c
@@@ -14,7 -14,6 +14,7 @@@
  #include <string.h>
  #include <stdarg.h>
  #include <assert.h>
 +#include <stdint.h>
  
  #include <jansson.h>
  #include "jansson_private.h"
@@@ -149,7 -148,7 +149,7 @@@ static char stream_get(stream_t *stream
              for(i = 1; i < count; i++)
                  stream->buffer[i] = stream->get(stream->data);
  
 -            if(!utf8_check_full(stream->buffer, count))
 +            if(!utf8_check_full(stream->buffer, count, NULL))
                  goto out;
  
              stream->stream_pos += count;
@@@ -222,10 -221,10 +222,10 @@@ static void lex_save_cached(lex_t *lex
  }
  
  /* assumes that str points to 'u' plus at least 4 valid hex digits */
 -static int decode_unicode_escape(const char *str)
 +static int32_t decode_unicode_escape(const char *str)
  {
      int i;
 -    int value = 0;
 +    int32_t value = 0;
  
      assert(str[0] == 'u');
  
@@@ -326,7 -325,7 +326,7 @@@ static void lex_scan_string(lex_t *lex
              if(*p == 'u') {
                  char buffer[4];
                  int length;
 -                int value;
 +                int32_t value;
  
                  value = decode_unicode_escape(p);
                  p += 5;
                  if(0xD800 <= value && value <= 0xDBFF) {
                      /* surrogate pair */
                      if(*p == '\\' && *(p + 1) == 'u') {
 -                        int value2 = decode_unicode_escape(++p);
 +                        int32_t value2 = decode_unicode_escape(++p);
                          p += 5;
  
                          if(0xDC00 <= value2 && value2 <= 0xDFFF) {
@@@ -772,7 -771,7 +772,7 @@@ static json_t *parse_value(lex_t *lex, 
      return json;
  }
  
- json_t *parse_json(lex_t *lex, json_error_t *error)
static json_t *parse_json(lex_t *lex, json_error_t *error)
  {
      error_init(error);