27 Commits
v2.1 ... v2.2.1

Author SHA1 Message Date
Petri Lehtinen
9c6cb42f17 jansson 2.2.1 2011-10-06 21:23:09 +03:00
Petri Lehtinen
ed06777937 Distribute doc/portability.rst 2011-10-06 21:23:09 +03:00
Petri Lehtinen
6362032513 json_load_file: Open the input file in rb mode
For maximum compatibility.
2011-10-04 21:11:31 +03:00
Petri Lehtinen
84f739036d Documentation fixes 2011-10-04 21:04:56 +03:00
Petri Lehtinen
0f358c8eaa doc: Add project status to introduction, add portability chapter
Move the multithreading-and-setlocale documentation to the new
portability chapter.

Fixes GH-36.
2011-10-03 22:42:39 +03:00
Petri Lehtinen
f0d5c04734 Make identifier decoding work under all locales
Replace isxxx() functions from ctype.h with locale-independent macros.

Fixes GH-35.
2011-10-03 21:43:16 +03:00
Petri Lehtinen
fd56deb7dd Use strchr() when searching for a single character 2011-10-03 08:52:30 +03:00
Petri Lehtinen
d7ddbf3661 Make real number encoding and decoding work under all locales
The decimal point '.' is changed to locale's decimal point
before/after JSON conversion to make C standard library's
locale-specific string conversion functions work correctly.

All the tests now call setlocale(LC_ALL, "") on startup to use the
locale set in the environment.

Fixes GH-32.
2011-10-02 21:31:17 +03:00
Petri Lehtinen
b6d0191e51 Implement all other encoding functions using json_dump_callback
This way we can check for JSON_ENCODE_ANY flag in one place only.
2011-09-24 21:10:34 +03:00
Petri Lehtinen
2a70d62251 Merge pull request #33 from bakulf/master
Little fixes
2011-09-20 11:11:55 -07:00
Andrea Marchesini
909874b1b9 scope error. result points to a out-of-score variable. 2011-09-20 13:10:32 +02:00
Andrea Marchesini
ff57dee13d unsed static variable removed 2011-09-20 11:46:29 +02:00
Petri Lehtinen
e4cc77ce52 doc: Clarify the result of 's' format of json_unpack()
Fixes GH-31.
2011-09-12 21:22:20 +03:00
Petri Lehtinen
889f295958 jansson 2.2 2011-09-02 21:39:40 +03:00
Petri Lehtinen
68809cd913 doc: json_dump_callback was added in v2.2 2011-09-02 21:29:01 +03:00
Petri Lehtinen
910fb92267 Merge branch '2.1' 2011-08-08 21:00:47 +03:00
Petri Lehtinen
f241e14cab doc: Explain the non-constness of the first parameter of json_unpack() et al
Fixes GH-30.
2011-08-08 20:59:10 +03:00
Petri Lehtinen
7e9c293986 Merge branch '2.1' 2011-06-30 21:48:51 +03:00
Petri Lehtinen
d43464a1ec Merge branch 'issue-29'
Fixes GH-29.
2011-06-30 21:48:33 +03:00
Petri Lehtinen
c7079a25eb doc: Add documentation for json_dump_callback() 2011-06-30 21:47:37 +03:00
JKL
54f38d250c test file for new json_dump_callback function 2011-06-30 21:47:12 +03:00
JKL
c7d543d36c new typedef json_dump_callback_t, function json_dump_callback 2011-06-30 21:47:12 +03:00
Petri Lehtinen
7fab57dcef doc: Fix several typos and other little errors 2011-06-30 21:04:49 +03:00
JKL
cd9757512d use size_t for strbuffer writes, and avoid integer overflow 2011-06-30 20:45:18 +03:00
Petri Lehtinen
c0193bfb7f Check that target is string and value is not NULL in json_string_set() 2011-06-17 21:42:19 +03:00
Petri Lehtinen
6e1f4bb560 doc: Clarify that removing from containers decrements the refcount
While at it, reword the object iteration protocol description a bit.
2011-06-17 20:53:32 +03:00
Petri Lehtinen
1358d0bfac CHANGES: Fix v2.1 release date 2011-06-10 21:53:14 +03:00
38 changed files with 503 additions and 134 deletions

54
CHANGES
View File

@@ -1,5 +1,55 @@
Version 2.1 (in development)
============================
Version 2.2.1
=============
Released 2011-10-06
* Bug fixes:
- Fix real number encoding and decoding under non-C locales. (#32)
- Fix identifier decoding under non-UTF-8 locales. (#35)
- `json_load_file()`: Open the input file in binary mode for maximum
compatiblity.
* Documentation:
- Clarify the lifecycle of the result of the ``s`` fromat of
`json_unpack()`. (#31)
- Add some portability info. (#36)
- Little clarifications here and there.
* Other:
- Some style fixes, issues detected by static analyzers.
Version 2.2
===========
Released 2011-09-03
* New features:
- `json_dump_callback()`: Pass the encoder output to a callback
function in chunks.
* Bug fixes:
- `json_string_set()`: Check that target is a string and value is
not NULL.
* Other:
- Documentation typo fixes and clarifications.
Version 2.1
===========
Released 2011-06-10
* New features:

View File

@@ -1,5 +1,5 @@
AC_PREREQ([2.60])
AC_INIT([jansson], [2.1], [petri@digip.org])
AC_INIT([jansson], [2.2.1], [petri@digip.org])
AM_INIT_AUTOMAKE([1.10 foreign])
@@ -14,6 +14,7 @@ AM_CONDITIONAL([GCC], [test x$GCC = xyes])
# Checks for libraries.
# Checks for header files.
AC_CHECK_HEADERS([locale.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_INT32_T
@@ -34,6 +35,12 @@ esac
AC_SUBST([json_inline])
# Checks for library functions.
AC_CHECK_FUNCS([setlocale localeconv])
case "$ac_cv_header_locale_h$ac_cv_func_localeconv" in
yesyes) json_have_localeconv=1;;
*) json_have_localeconv=0;;
esac
AC_SUBST([json_have_localeconv])
AC_CONFIG_FILES([
jansson.pc

View File

@@ -1,6 +1,6 @@
EXTRA_DIST = conf.py apiref.rst changes.rst conformance.rst \
gettingstarted.rst github_commits.c index.rst tutorial.rst \
upgrading.rst ext/refcounting.py
gettingstarted.rst github_commits.c index.rst portability.rst \
tutorial.rst upgrading.rst ext/refcounting.py
SPHINXBUILD = sphinx-build
SPHINXOPTS = -d _build/doctrees $(SPHINXOPTS_EXTRA)

View File

@@ -481,12 +481,13 @@ A JSON array is an ordered collection of other JSON values.
Removes the element in *array* at position *index*, shifting the
elements after *index* one position towards the start of the array.
Returns 0 on success and -1 on error.
Returns 0 on success and -1 on error. The reference count of the
removed value is decremented.
.. function:: int json_array_clear(json_t *array)
Removes all elements from *array*. Returns 0 on sucess and -1 on
error.
error. The reference count of all removed values are decremented.
.. function:: int json_array_extend(json_t *array, json_t *other_array)
@@ -549,13 +550,14 @@ Unicode string and the value is any JSON value.
.. function:: int json_object_del(json_t *object, const char *key)
Delete *key* from *object* if it exists. Returns 0 on success, or
-1 if *key* was not found.
-1 if *key* was not found. The reference count of the removed value
is decremented.
.. function:: int json_object_clear(json_t *object)
Remove all elements from *object*. Returns 0 on success and -1 if
*object* is not a JSON object.
*object* is not a JSON object. The reference count of all removed
values are decremented.
.. function:: int json_object_update(json_t *object, json_t *other)
@@ -566,7 +568,7 @@ Unicode string and the value is any JSON value.
The following functions implement an iteration protocol for objects,
allowing to iterate through all key-value pairs in an object. The
items are not returned in any particular order, as this would require
sorting due to the internal object representation.
sorting due to the internal hashtable implementation.
.. function:: void *json_object_iter(json_t *object)
@@ -701,9 +703,9 @@ can be ORed together to obtain *flags*.
``JSON_INDENT(n)``
Pretty-print the result, using newlines between array and object
items, and indenting with *n* spaces. The valid range for *n* is
between 0 and 32, other values result in an undefined output. If
``JSON_INDENT`` is not used or *n* is 0, no newlines are inserted
between array and object items.
between 0 and 31 (inclusive), other values result in an undefined
output. If ``JSON_INDENT`` is not used or *n* is 0, no newlines are
inserted between array and object items.
``JSON_COMPACT``
This flag enables a compact representation, i.e. sets the separator
@@ -763,6 +765,30 @@ is in UTF-8.
*path* already exists, it is overwritten. *flags* is described
above. Returns 0 on success and -1 on error.
.. type:: json_dump_callback_t
A typedef for a function that's called by
:func:`json_dump_callback()`::
typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);
*buffer* points to a buffer containing a chunk of output, *size* is
the length of the buffer, and *data* is the corresponding
:func:`json_dump_callback()` argument passed through.
On error, the function should return -1 to stop the encoding
process. On success, it should return 0.
.. versionadded:: 2.2
.. function:: int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
Call *callback* repeatedly, passing a chunk of the JSON
representation of *root* each time. *flags* is described above.
Returns 0 on success and -1 on error.
.. versionadded:: 2.2
.. _apiref-decoding:
@@ -833,6 +859,16 @@ The following functions perform the actual JSON decoding.
filled with information about the error. *flags* is described
above.
This function will start reading the input from whatever position
the input file was, without attempting to seek first. If an error
occurs, the file position will be left indeterminate. On success,
the file position will be at EOF, unless ``JSON_DISABLE_EOF_CHECK``
flag was used. In this case, the file position will be at the first
character after the last ``]`` or ``}`` in the JSON input. This
allows calling :func:`json_loadf()` on the same ``FILE`` object
multiple times, if the input consists of consecutive JSON texts,
possibly separated by whitespace.
.. function:: json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
.. refcounting:: new
@@ -848,14 +884,14 @@ The following functions perform the actual JSON decoding.
Building Values
===============
This sectinon describes functions that help to create, or *pack*,
This section describes functions that help to create, or *pack*,
complex JSON values, especially nested objects and arrays. Value
building is based on a *format string* that is used to tell the
functions about the expected arguments.
For example, the format string ``"i"`` specifies a single integer
value, while the format string ``"[ssb]"`` or the equivalent ``"[s, s,
b]"`` specifies an array value with two integers and a boolean as its
b]"`` specifies an array value with two strings and a boolean as its
items::
/* Create the JSON integer 42 */
@@ -890,7 +926,7 @@ denotes the C type that is expected as the corresponding argument.
``o`` (any value) [json_t \*]
Output any given JSON value as-is. If the value is added to an
array or object, the reference to the value passed to ``o`` is
stealed by the container.
stolen by the container.
``O`` (any value) [json_t \*]
Like ``o``, but the argument's reference count is incremented.
@@ -940,10 +976,10 @@ More examples::
json_pack("{}");
/* Build the JSON object {"foo": 42, "bar": 7} */
json_pack("{sisb}", "foo", 42, "bar", 7);
json_pack("{sisi}", "foo", 42, "bar", 7);
/* Like above, ':', ',' and whitespace are ignored */
json_pack("{s:i, s:b}", "foo", 42, "bar", 7);
json_pack("{s:i, s:i}", "foo", 42, "bar", 7);
/* Build the JSON array [[1, 2], {"cool": true}] */
json_pack("[[i,i],{s:b]]", 1, 2, "cool", 1);
@@ -971,7 +1007,9 @@ type whose address should be passed.
``s`` (string) [const char \*]
Convert a JSON string to a pointer to a NULL terminated UTF-8
string.
string. The resulting string is extracted by using
:func:`json_string_value()` internally, so it exists as long as
there are still references to the corresponding JSON string.
``n`` (null)
Expect a JSON null value. Nothing is extracted.
@@ -1045,6 +1083,20 @@ The following functions compose the parsing and validation API:
behaviour of the unpacker, see below for the flags. Returns 0 on
success and -1 on failure.
.. note::
The first argument of all unpack functions is ``json_t *root``
instead of ``const json_t *root``, because the use of ``O`` format
character causes the reference count of ``root``, or some value
reachable from ``root``, to be increased. Furthermore, the ``o``
format character may be used to extract a value as-is, which allows
modifying the structure or contents of a value reachable from
``root``.
If the ``O`` and ``o`` format character are not used, it's
perfectly safe to cast a ``const json_t *`` variable to plain
``json_t *`` when used with these functions.
The following unpacking flags are available:
``JSON_STRICT``
@@ -1149,6 +1201,8 @@ copied in a recursive fashion.
Returns a deep copy of *value*, or *NULL* on error.
.. _apiref-custom-memory-allocation:
Custom Memory Allocation
========================
@@ -1217,5 +1271,5 @@ JSON structures by zeroing all memory when freed::
For more information about the issues of storing sensitive data in
memory, see
http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/protect-secrets.html.
The page also examplains the :func:`guaranteed_memset()` function used
The page also explains the :func:`guaranteed_memset()` function used
in the example and gives a sample implementation for it.

View File

@@ -48,9 +48,9 @@ copyright = u'2009-2011, Petri Lehtinen'
# built documents.
#
# The short X.Y version.
version = '2.1'
version = '2.2.1'
# The full version, including alpha/beta/rc tags.
release = '2.1'
release = version
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@@ -5,8 +5,7 @@ RFC Conformance
***************
JSON is specified in :rfc:`4627`, *"The application/json Media Type
for JavaScript Object Notation (JSON)"*. This chapter discusses
Jansson's conformance to this specification.
for JavaScript Object Notation (JSON)"*.
Character Encoding
==================

View File

@@ -22,6 +22,11 @@ data. Its main features and design principles are:
Jansson is licensed under the `MIT license`_; see LICENSE in the
source distribution for details.
Jansson is used in production and its API is stable. It works on
numerous platforms, including numerous Unix like systems and Windows.
It's suitable for use on any system, including desktop, server, and
small embedded systems.
.. _`MIT license`: http://www.opensource.org/licenses/mit-license.php
.. _Jansson: http://www.digip.org/jansson/
@@ -36,6 +41,7 @@ Contents
upgrading
tutorial
conformance
portability
apiref
changes

42
doc/portability.rst Normal file
View File

@@ -0,0 +1,42 @@
***********
Portability
***********
Thread safety
-------------
Jansson is thread safe and has no mutable global state. The only
exception are the memory allocation functions, that should be set at
most once, and only on program startup. See
:ref:`apiref-custom-memory-allocation`.
There's no locking performed inside Jansson's code, so a multithreaded
program must perform its own locking if JSON values are shared by
multiple threads. Jansson's reference counting semantics may make this
a bit harder than it seems, as it's possible to have a reference to a
value that's also stored inside a list or object. Modifying the
container (adding or removing values) may trigger concurrent access to
such values, as containers manage the reference count of their
contained values. Bugs involving concurrent incrementing or
decrementing of deference counts may be hard to track.
Locale
------
Jansson works fine under any locale.
However, if the host program is multithreaded and uses ``setlocale()``
to switch the locale in one thread while Jansson is currently encoding
or decoding JSON data in another thread, the result may be wrong or
the program may even crash.
Jansson uses locale specific functions for certain string conversions
in the encoder and decoder, and then converts the locale specific
values to/from the JSON representation. This fails if the locale
changes between the string conversion and the locale-to-JSON
conversion. This can only happen in multithreaded programs that use
``setlocale()``, because ``setlocale()`` switches the locale for all
running threads, not only the thread that calls ``setlocale()``.
If your program uses ``setlocale()`` as described above, consider
using the thread-safe ``uselocale()`` instead.

View File

@@ -12,12 +12,13 @@ libjansson_la_SOURCES = \
pack_unpack.c \
strbuffer.c \
strbuffer.h \
strconv.c \
utf.c \
utf.h \
value.c
libjansson_la_LDFLAGS = \
-export-symbols-regex '^json_' \
-version-info 5:0:1
-version-info 6:1:2
if GCC
# These flags are gcc specific

View File

@@ -19,8 +19,6 @@
#define MAX_INTEGER_STR_LENGTH 100
#define MAX_REAL_STR_LENGTH 100
typedef int (*dump_func)(const char *buffer, int size, void *data);
struct string
{
char *buffer;
@@ -28,12 +26,12 @@ struct string
int size;
};
static int dump_to_strbuffer(const char *buffer, int size, void *data)
static int dump_to_strbuffer(const char *buffer, size_t size, void *data)
{
return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
}
static int dump_to_file(const char *buffer, int size, void *data)
static int dump_to_file(const char *buffer, size_t size, void *data)
{
FILE *dest = (FILE *)data;
if(fwrite(buffer, size, 1, dest) != 1)
@@ -44,7 +42,7 @@ static int dump_to_file(const char *buffer, int size, void *data)
/* 32 spaces (the maximum indentation size) */
static char whitespace[] = " ";
static int dump_indent(size_t flags, int depth, int space, dump_func dump, void *data)
static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data)
{
if(JSON_INDENT(flags) > 0)
{
@@ -66,7 +64,7 @@ static int dump_indent(size_t flags, int depth, int space, dump_func dump, void
return 0;
}
static int dump_string(const char *str, int ascii, dump_func dump, void *data)
static int dump_string(const char *str, int ascii, json_dump_callback_t dump, void *data)
{
const char *pos, *end;
int32_t codepoint;
@@ -166,7 +164,7 @@ static int object_key_compare_serials(const void *key1, const void *key2)
}
static int do_dump(const json_t *json, size_t flags, int depth,
dump_func dump, void *data)
json_dump_callback_t dump, void *data)
{
int ascii = flags & JSON_ENSURE_ASCII ? 1 : 0;
@@ -198,26 +196,12 @@ static int do_dump(const json_t *json, size_t flags, int depth,
{
char buffer[MAX_REAL_STR_LENGTH];
int size;
double value = json_real_value(json);
size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%.17g",
json_real_value(json));
if(size >= MAX_REAL_STR_LENGTH)
size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value);
if(size < 0)
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);
}
@@ -415,39 +399,26 @@ static int do_dump(const json_t *json, size_t flags, int depth,
}
}
char *json_dumps(const json_t *json, size_t flags)
{
strbuffer_t strbuff;
char *result;
if(!(flags & JSON_ENCODE_ANY)) {
if(!json_is_array(json) && !json_is_object(json))
return NULL;
}
if(strbuffer_init(&strbuff))
return NULL;
if(do_dump(json, flags, 0, dump_to_strbuffer, (void *)&strbuff)) {
strbuffer_close(&strbuff);
return NULL;
}
if(json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags))
result = NULL;
else
result = jsonp_strdup(strbuffer_value(&strbuff));
result = jsonp_strdup(strbuffer_value(&strbuff));
strbuffer_close(&strbuff);
return result;
}
int json_dumpf(const json_t *json, FILE *output, size_t flags)
{
if(!(flags & JSON_ENCODE_ANY)) {
if(!json_is_array(json) && !json_is_object(json))
return -1;
}
return do_dump(json, flags, 0, dump_to_file, (void *)output);
return json_dump_callback(json, dump_to_file, (void *)output, flags);
}
int json_dump_file(const json_t *json, const char *path, size_t flags)
@@ -463,3 +434,13 @@ int json_dump_file(const json_t *json, const char *path, size_t flags)
fclose(output);
return result;
}
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
{
if(!(flags & JSON_ENCODE_ANY)) {
if(!json_is_array(json) && !json_is_object(json))
return -1;
}
return do_dump(json, flags, 0, callback, data);
}

View File

@@ -62,7 +62,6 @@ static size_t primes[] = {
12582917, 25165843, 50331653, 100663319, 201326611, 402653189,
805306457, 1610612741
};
static const size_t num_primes = sizeof(primes) / sizeof(size_t);
static JSON_INLINE size_t num_buckets(hashtable_t *hashtable)
{

View File

@@ -21,11 +21,11 @@ extern "C" {
/* version */
#define JANSSON_MAJOR_VERSION 2
#define JANSSON_MINOR_VERSION 1
#define JANSSON_MICRO_VERSION 0
#define JANSSON_MINOR_VERSION 2
#define JANSSON_MICRO_VERSION 1
/* Micro version is omitted if it's 0 */
#define JANSSON_VERSION "2.1"
#define JANSSON_VERSION "2.2.1"
/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this
for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */
@@ -234,10 +234,12 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error);
#define JSON_PRESERVE_ORDER 0x100
#define JSON_ENCODE_ANY 0x200
typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);
char *json_dumps(const json_t *json, size_t flags);
int json_dumpf(const json_t *json, FILE *output, size_t flags);
int json_dump_file(const json_t *json, const char *path, size_t flags);
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags);
/* custom memory allocation */

View File

@@ -31,4 +31,8 @@
JSON_INTEGER_IS_LONG_LONG is defined to 1, otherwise to 0. */
#define JSON_INTEGER_IS_LONG_LONG @json_have_long_long@
/* If locale.h and localeconv() are available, define to 1,
otherwise to 0. */
#define JSON_HAVE_LOCALECONV @json_have_localeconv@
#endif

View File

@@ -31,4 +31,8 @@
JSON_INTEGER_IS_LONG_LONG is defined to 1, otherwise to 0. */
#define JSON_INTEGER_IS_LONG_LONG 1
/* If locale.h and localeconv() are available, define to 1,
otherwise to 0. */
#define JSON_HAVE_LOCALECONV 1
#endif

View File

@@ -11,6 +11,7 @@
#include <stddef.h>
#include "jansson.h"
#include "hashtable.h"
#include "strbuffer.h"
#define container_of(ptr_, type_, member_) \
((type_ *)((char *)ptr_ - offsetof(type_, member_)))
@@ -83,6 +84,10 @@ void jsonp_error_set(json_error_t *error, int line, int column,
void jsonp_error_vset(json_error_t *error, int line, int column,
size_t position, const char *msg, va_list ap);
/* Locale independent string<->double conversions */
int jsonp_strtod(strbuffer_t *strbuffer, double *out);
int jsonp_dtostr(char *buffer, size_t size, double value);
/* Wrappers for custom memory functions */
void* jsonp_malloc(size_t size);
void jsonp_free(void *ptr);

View File

@@ -6,7 +6,6 @@
*/
#define _GNU_SOURCE
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
@@ -32,6 +31,14 @@
#define TOKEN_FALSE 260
#define TOKEN_NULL 261
/* Locale independent versions of isxxx() functions */
#define l_isupper(c) ('A' <= c && c <= 'Z')
#define l_islower(c) ('a' <= c && c <= 'z')
#define l_isalpha(c) (l_isupper(c) || l_islower(c))
#define l_isdigit(c) ('0' <= c && c <= '9')
#define l_isxdigit(c) \
(l_isdigit(c) || 'A' <= c || c <= 'F' || 'a' <= c || c <= 'f')
/* Read one byte from stream, convert to unsigned char, then int, and
return. return EOF on end of file. This corresponds to the
behaviour of fgetc(). */
@@ -69,6 +76,7 @@ static void error_set(json_error_t *error, const lex_t *lex,
{
va_list ap;
char msg_text[JSON_ERROR_TEXT_LENGTH];
char msg_with_context[JSON_ERROR_TEXT_LENGTH];
int line = -1, col = -1;
size_t pos = 0;
@@ -84,7 +92,6 @@ static void error_set(json_error_t *error, const lex_t *lex,
if(lex)
{
const char *saved_text = strbuffer_value(&lex->saved_text);
char msg_with_context[JSON_ERROR_TEXT_LENGTH];
line = lex->stream.line;
col = lex->stream.column;
@@ -268,11 +275,11 @@ static int32_t decode_unicode_escape(const char *str)
for(i = 1; i <= 4; i++) {
char c = str[i];
value <<= 4;
if(isdigit(c))
if(l_isdigit(c))
value += c - '0';
else if(islower(c))
else if(l_islower(c))
value += c - 'a' + 10;
else if(isupper(c))
else if(l_isupper(c))
value += c - 'A' + 10;
else
assert(0);
@@ -317,7 +324,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
if(c == 'u') {
c = lex_get_save(lex, error);
for(i = 0; i < 4; i++) {
if(!isxdigit(c)) {
if(!l_isxdigit(c)) {
error_set(error, lex, "invalid escape");
goto out;
}
@@ -455,14 +462,14 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
if(c == '0') {
c = lex_get_save(lex, error);
if(isdigit(c)) {
if(l_isdigit(c)) {
lex_unget_unsave(lex, c);
goto out;
}
}
else if(isdigit(c)) {
else if(l_isdigit(c)) {
c = lex_get_save(lex, error);
while(isdigit(c))
while(l_isdigit(c))
c = lex_get_save(lex, error);
}
else {
@@ -496,14 +503,14 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
if(c == '.') {
c = lex_get(lex, error);
if(!isdigit(c)) {
if(!l_isdigit(c)) {
lex_unget(lex, c);
goto out;
}
lex_save(lex, c);
c = lex_get_save(lex, error);
while(isdigit(c))
while(l_isdigit(c))
c = lex_get_save(lex, error);
}
@@ -512,24 +519,19 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
if(c == '+' || c == '-')
c = lex_get_save(lex, error);
if(!isdigit(c)) {
if(!l_isdigit(c)) {
lex_unget_unsave(lex, c);
goto out;
}
c = lex_get_save(lex, error);
while(isdigit(c))
while(l_isdigit(c))
c = lex_get_save(lex, error);
}
lex_unget_unsave(lex, c);
saved_text = strbuffer_value(&lex->saved_text);
errno = 0;
value = strtod(saved_text, &end);
assert(end == saved_text + lex->saved_text.length);
if(errno == ERANGE && value != 0) {
if(jsonp_strtod(&lex->saved_text, &value)) {
error_set(error, lex, "real number overflow");
goto out;
}
@@ -575,17 +577,17 @@ static int lex_scan(lex_t *lex, json_error_t *error)
else if(c == '"')
lex_scan_string(lex, error);
else if(isdigit(c) || c == '-') {
else if(l_isdigit(c) || c == '-') {
if(lex_scan_number(lex, c, error))
goto out;
}
else if(isupper(c) || islower(c)) {
else if(l_isalpha(c)) {
/* eat up the whole identifier for clearer error messages */
const char *saved_text;
c = lex_get_save(lex, error);
while(isupper(c) || islower(c))
while(l_isalpha(c))
c = lex_get_save(lex, error);
lex_unget_unsave(lex, c);
@@ -945,7 +947,7 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
jsonp_error_init(error, path);
fp = fopen(path, "r");
fp = fopen(path, "rb");
if(!fp)
{
error_set(error, NULL, "unable to open %s: %s",

View File

@@ -13,6 +13,7 @@
#define STRBUFFER_MIN_SIZE 16
#define STRBUFFER_FACTOR 2
#define STRBUFFER_SIZE_MAX ((size_t)-1)
int strbuffer_init(strbuffer_t *strbuff)
{
@@ -64,13 +65,19 @@ int strbuffer_append_byte(strbuffer_t *strbuff, char byte)
return strbuffer_append_bytes(strbuff, &byte, 1);
}
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, int size)
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size)
{
if(strbuff->length + size >= strbuff->size)
if(size >= strbuff->size - strbuff->length)
{
size_t new_size;
char *new_value;
/* avoid integer overflow */
if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR
|| size > STRBUFFER_SIZE_MAX - 1
|| strbuff->length > STRBUFFER_SIZE_MAX - 1 - size)
return -1;
new_size = max(strbuff->size * STRBUFFER_FACTOR,
strbuff->length + size + 1);

View File

@@ -10,8 +10,8 @@
typedef struct {
char *value;
int length; /* bytes used */
int size; /* bytes allocated */
size_t length; /* bytes used */
size_t size; /* bytes allocated */
} strbuffer_t;
int strbuffer_init(strbuffer_t *strbuff);
@@ -24,7 +24,7 @@ char *strbuffer_steal_value(strbuffer_t *strbuff);
int strbuffer_append(strbuffer_t *strbuff, const char *string);
int strbuffer_append_byte(strbuffer_t *strbuff, char byte);
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, int size);
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size);
char strbuffer_pop(strbuffer_t *strbuff);

108
src/strconv.c Normal file
View File

@@ -0,0 +1,108 @@
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "jansson_private.h"
#include "strbuffer.h"
#if JSON_HAVE_LOCALECONV
#include <locale.h>
/*
- This code assumes that the decimal separator is exactly one
character.
- If setlocale() is called by another thread between the call to
localeconv() and the call to sprintf() or strtod(), the result may
be wrong. setlocale() is not thread-safe and should not be used
this way. Multi-threaded programs should use uselocale() instead.
*/
static void to_locale(strbuffer_t *strbuffer)
{
const char *point;
char *pos;
point = localeconv()->decimal_point;
if(*point == '.') {
/* No conversion needed */
return;
}
pos = strchr(strbuffer->value, '.');
if(pos)
*pos = *point;
}
static void from_locale(char *buffer)
{
const char *point;
char *pos;
point = localeconv()->decimal_point;
if(*point == '.') {
/* No conversion needed */
return;
}
pos = strchr(buffer, *point);
if(pos)
*pos = '.';
}
#endif
int jsonp_strtod(strbuffer_t *strbuffer, double *out)
{
double value;
char *end;
#if JSON_HAVE_LOCALECONV
to_locale(strbuffer);
#endif
errno = 0;
value = strtod(strbuffer->value, &end);
assert(end == strbuffer->value + strbuffer->length);
if(errno == ERANGE && value != 0) {
/* Overflow */
return -1;
}
*out = value;
return 0;
}
int jsonp_dtostr(char *buffer, size_t size, double value)
{
int ret;
size_t length;
ret = snprintf(buffer, size, "%.17g", value);
if(ret < 0)
return -1;
length = (size_t)ret;
if(length >= size)
return -1;
#if JSON_HAVE_LOCALECONV
from_locale(buffer);
#endif
/* 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(length + 2 >= size) {
/* No space to append ".0" */
return -1;
}
buffer[length] = '.';
buffer[length + 1] = '0';
length += 2;
}
return (int)length;
}

View File

@@ -703,6 +703,9 @@ int json_string_set_nocheck(json_t *json, const char *value)
char *dup;
json_string_t *string;
if(!json_is_string(json) || !value)
return -1;
dup = jsonp_strdup(value);
if(!dup)
return -1;

1
test/.gitignore vendored
View File

@@ -4,6 +4,7 @@ suites/api/test_array
suites/api/test_copy
suites/api/test_cpp
suites/api/test_dump
suites/api/test_dump_callback
suites/api/test_equal
suites/api/test_load
suites/api/test_loadb

View File

@@ -5,12 +5,20 @@
* it under the terms of the MIT license. See LICENSE for details.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <jansson.h>
#if HAVE_LOCALE_H
#include <locale.h>
#endif
static int getenv_int(const char *name)
{
char *value, *end;
@@ -55,6 +63,10 @@ int main(int argc, char *argv[])
json_t *json;
json_error_t error;
#if HAVE_SETLOCALE
setlocale(LC_ALL, "");
#endif
if(argc != 1) {
fprintf(stderr, "usage: %s\n", argv[0]);
return 2;

View File

@@ -4,6 +4,7 @@ check_PROGRAMS = \
test_array \
test_copy \
test_dump \
test_dump_callback \
test_equal \
test_load \
test_loadb \
@@ -17,6 +18,7 @@ check_PROGRAMS = \
test_array_SOURCES = test_array.c util.h
test_copy_SOURCES = test_copy.c util.h
test_dump_SOURCES = test_dump.c util.h
test_dump_callback_SOURCES = test_dump_callback.c util.h
test_load_SOURCES = test_load.c util.h
test_loadb_SOURCES = test_loadb.c util.h
test_memory_funcs_SOURCES = test_memory_funcs.c util.h

View File

@@ -47,6 +47,7 @@ json_object_iter_set_new
json_dumps
json_dumpf
json_dump_file
json_dump_callback
json_loads
json_loadf
json_load_file

View File

@@ -387,7 +387,7 @@ static void test_circular()
}
int main()
static void run_tests()
{
test_misc();
test_insert();
@@ -395,6 +395,4 @@ int main()
test_clear();
test_extend();
test_circular();
return 0;
}

View File

@@ -307,7 +307,7 @@ static void test_deep_copy_object(void)
json_decref(copy);
}
int main()
static void run_tests()
{
test_copy_simple();
test_deep_copy_simple();
@@ -315,5 +315,4 @@ int main()
test_deep_copy_array();
test_copy_object();
test_deep_copy_object();
return 0;
}

View File

@@ -133,10 +133,9 @@ static void encode_other_than_array_or_object()
}
int main()
static void run_tests()
{
encode_twice();
circular_references();
encode_other_than_array_or_object();
return 0;
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <jansson.h>
#include <string.h>
#include <stdlib.h>
#include "util.h"
struct my_sink {
char *buf;
size_t off;
size_t cap;
};
static int my_writer(const char *buffer, size_t len, void *data) {
struct my_sink *s = data;
if (len > s->cap - s->off) {
return -1;
}
memcpy(s->buf + s->off, buffer, len);
s->off += len;
return 0;
}
static void run_tests()
{
struct my_sink s;
json_t *json;
const char str[] = "[\"A\", {\"B\": \"C\", \"e\": false}, 1, null, \"foo\"]";
char *dumped_to_string;
json = json_loads(str, 0, NULL);
if(!json) {
fail("json_loads failed");
}
dumped_to_string = json_dumps(json, 0);
if (!dumped_to_string) {
json_decref(json);
fail("json_dumps failed");
}
s.off = 0;
s.cap = strlen(dumped_to_string);
s.buf = malloc(s.cap);
if (!s.buf) {
json_decref(json);
free(dumped_to_string);
fail("malloc failed");
}
if (json_dump_callback(json, my_writer, &s, 0) == -1) {
json_decref(json);
free(dumped_to_string);
free(s.buf);
fail("json_dump_callback failed on an exact-length sink buffer");
}
if (strncmp(dumped_to_string, s.buf, s.off) != 0) {
json_decref(json);
free(dumped_to_string);
free(s.buf);
fail("json_dump_callback and json_dumps did not produce identical output");
}
s.off = 1;
if (json_dump_callback(json, my_writer, &s, 0) != -1) {
json_decref(json);
free(dumped_to_string);
free(s.buf);
fail("json_dump_callback succeeded on a short buffer when it should have failed");
}
json_decref(json);
free(dumped_to_string);
free(s.buf);
}

View File

@@ -180,11 +180,10 @@ static void test_equal_complex()
/* TODO: There's no negative test case here */
}
int main()
static void run_tests()
{
test_equal_simple();
test_equal_array();
test_equal_object();
test_equal_complex();
return 0;
}

View File

@@ -50,11 +50,9 @@ static void disable_eof_check()
json_decref(json);
}
int main()
static void run_tests()
{
file_not_found();
reject_duplicates();
disable_eof_check();
return 0;
}

View File

@@ -9,7 +9,7 @@
#include <string.h>
#include "util.h"
int main()
static void run_tests()
{
json_t *json;
json_error_t error;
@@ -33,6 +33,4 @@ int main()
if(strcmp(error.text, "']' expected near end of file") != 0) {
fail("json_loadb returned an invalid error message for an unclosed top-level array");
}
return 0;
}

View File

@@ -75,10 +75,8 @@ static void test_secure_funcs(void)
create_and_free_complex_object();
}
int main()
static void run_tests()
{
test_simple();
test_secure_funcs();
return 0;
}

View File

@@ -8,7 +8,7 @@
#include <jansson.h>
#include "util.h"
int main()
static void run_tests()
{
json_t *integer, *real;
int i;
@@ -39,6 +39,4 @@ int main()
json_decref(integer);
json_decref(real);
return 0;
}

View File

@@ -437,7 +437,7 @@ static void test_preserve_order()
json_decref(object);
}
int main()
static void run_tests()
{
test_misc();
test_clear();
@@ -446,6 +446,4 @@ int main()
test_set_nocheck();
test_iterators();
test_preserve_order();
return 0;
}

View File

@@ -11,7 +11,7 @@
#include <stdio.h>
#include "util.h"
int main()
static void run_tests()
{
json_t *value;
int i;
@@ -227,6 +227,4 @@ int main()
if(json_pack_ex(&error, 0, "{s:s}", "foo", "\xff\xff"))
fail("json_pack failed to catch invalid UTF-8 in a string");
check_error("Invalid UTF-8 string", "<args>", 1, 4, 4);
return 0;
}

View File

@@ -10,7 +10,7 @@
#include "util.h"
/* Call the simple functions not covered by other tests of the public API */
int main()
static void run_tests()
{
json_t *value;
@@ -180,6 +180,4 @@ int main()
json_incref(value);
if(value->refcount != (size_t)-1)
fail("refcounting null works incorrectly");
return 0;
}

View File

@@ -11,7 +11,7 @@
#include <stdio.h>
#include "util.h"
int main()
static void run_tests()
{
json_t *j, *j2;
int i1, i2, i3;
@@ -336,6 +336,4 @@ int main()
fail("json_unpack nested array with strict validation failed");
check_error("1 array item(s) left unpacked", "<validation>", 1, 5, 5);
json_decref(j);
return 0;
}

View File

@@ -8,8 +8,16 @@
#ifndef UTIL_H
#define UTIL_H
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#if HAVE_LOCALE_H
#include <locale.h>
#endif
#include <jansson.h>
#define failhdr fprintf(stderr, "%s:%s:%d: ", __FILE__, __FUNCTION__, __LINE__)
@@ -52,4 +60,15 @@
} \
} while(0)
static void run_tests();
int main() {
#ifdef HAVE_SETLOCALE
setlocale(LC_ALL, "");
#endif
run_tests();
return 0;
}
#endif