Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86d17a8dc2 | ||
|
|
3988ab2d27 | ||
|
|
f7f7bf5ab2 | ||
|
|
a76ba52f34 | ||
|
|
9febdf333c | ||
|
|
013b8b3f60 | ||
|
|
49fc708d4c | ||
|
|
92f6f0f22c | ||
|
|
e9e34f430e | ||
|
|
ab723c7fb5 | ||
|
|
636d5f60f9 | ||
|
|
c3492973e1 | ||
|
|
e20619e071 | ||
|
|
b44e2be032 | ||
|
|
76d6d700ad | ||
|
|
c96763215d | ||
|
|
4a76900bd7 | ||
|
|
1c0a3b2a55 | ||
|
|
056702e541 | ||
|
|
eab23f05d8 | ||
|
|
0944ac8d91 | ||
|
|
a5a43caa9a | ||
|
|
5456fc59ab | ||
|
|
279d8bf108 | ||
|
|
38e35e0973 | ||
|
|
af18578928 | ||
|
|
c30e92603c | ||
|
|
6ecba84817 | ||
|
|
b90ed1accb | ||
|
|
1111960120 | ||
|
|
7f09f48e7e | ||
|
|
b397711a66 |
64
CHANGES
64
CHANGES
@@ -1,3 +1,67 @@
|
||||
Version 2.1 (in development)
|
||||
============================
|
||||
|
||||
* New features:
|
||||
|
||||
- `json_loadb()`: Decode a string with a given size, useful if the
|
||||
string is not null terminated.
|
||||
|
||||
- Add ``JSON_ENCODE_ANY`` encoding flag to allow encoding any JSON
|
||||
value. By default, only arrays and objects can be encoded. (#19)
|
||||
|
||||
- Add ``JSON_REJECT_DUPLICATES`` decoding flag to issue a decoding
|
||||
error if any JSON object in the input contins duplicate keys. (#3)
|
||||
|
||||
- Add ``JSON_DISABLE_EOF_CHECK`` decoding flag to stop decoding after a
|
||||
valid JSON input. This allows other data after the JSON data.
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- Fix an additional memory leak when memory allocation fails in
|
||||
`json_object_set()` and friends.
|
||||
|
||||
- Clear errno before calling `strtod()` for better portability. (#27)
|
||||
|
||||
* Building:
|
||||
|
||||
- Avoid set-but-not-used warning/error in a test. (#20)
|
||||
|
||||
* Other:
|
||||
|
||||
- Minor clarifications to documentation.
|
||||
|
||||
|
||||
Version 2.0.1
|
||||
=============
|
||||
|
||||
Released 2011-03-31
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- Replace a few `malloc()` and `free()` calls with their
|
||||
counterparts that support custom memory management.
|
||||
|
||||
- Fix object key hashing in json_unpack() strict checking mode.
|
||||
|
||||
- Fix the parentheses in ``JANSSON_VERSION_HEX`` macro.
|
||||
|
||||
- Fix `json_object_size()` return value.
|
||||
|
||||
- Fix a few compilation issues.
|
||||
|
||||
* Portability:
|
||||
|
||||
- Enhance portability of `va_copy()`.
|
||||
|
||||
- Test framework portability enhancements.
|
||||
|
||||
* Documentation:
|
||||
|
||||
- Distribute ``doc/upgrading.rst`` with the source tarball.
|
||||
|
||||
- Build documentation in strict mode in ``make distcheck``.
|
||||
|
||||
|
||||
Version 2.0
|
||||
===========
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
EXTRA_DIST = CHANGES LICENSE README.rst
|
||||
SUBDIRS = doc src test
|
||||
|
||||
check-doc:
|
||||
# "make distcheck" builds the dvi target, so use it to check that the
|
||||
# documentation is built correctly.
|
||||
dvi:
|
||||
$(MAKE) SPHINXOPTS_EXTRA=-W html
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
AC_PREREQ([2.60])
|
||||
AC_INIT([jansson], [2.0], [petri@digip.org])
|
||||
AC_INIT([jansson], [2.1], [petri@digip.org])
|
||||
|
||||
AM_INIT_AUTOMAKE([1.10 foreign])
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
EXTRA_DIST = conf.py apiref.rst changes.rst conformance.rst \
|
||||
gettingstarted.rst github_commits.c index.rst tutorial.rst \
|
||||
ext/refcounting.py
|
||||
upgrading.rst ext/refcounting.py
|
||||
|
||||
SPHINXBUILD = sphinx-build
|
||||
SPHINXOPTS = -d _build/doctrees $(SPHINXOPTS_EXTRA)
|
||||
|
||||
@@ -563,7 +563,10 @@ Unicode string and the value is any JSON value.
|
||||
existing keys. Returns 0 on success or -1 on error.
|
||||
|
||||
|
||||
The following functions implement an iteration protocol for objects:
|
||||
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.
|
||||
|
||||
.. function:: void *json_object_iter(json_t *object)
|
||||
|
||||
@@ -680,8 +683,10 @@ Encoding
|
||||
========
|
||||
|
||||
This sections describes the functions that can be used to encode
|
||||
values to JSON. Only objects and arrays can be encoded, since they are
|
||||
the only valid "root" values of a JSON text.
|
||||
values to JSON. By default, only objects and arrays can be encoded
|
||||
directly, since they are the only valid *root* values of a JSON text.
|
||||
To encode any JSON value, use the ``JSON_ENCODE_ANY`` flag (see
|
||||
below).
|
||||
|
||||
By default, the output has no newlines, and spaces are used between
|
||||
array and object elements for a readable output. This behavior can be
|
||||
@@ -722,6 +727,19 @@ can be ORed together to obtain *flags*.
|
||||
example, decoding a JSON text and then encoding with this flag
|
||||
preserves the order of object keys.
|
||||
|
||||
``JSON_ENCODE_ANY``
|
||||
Specifying this flag makes it possible to encode any JSON value on
|
||||
its own. Without it, only objects and arrays can be passed as the
|
||||
*root* value to the encoding functions.
|
||||
|
||||
**Note:** Encoding any value may be useful in some scenarios, but
|
||||
it's generally discouraged as it violates strict compatiblity with
|
||||
:rfc:`4627`. If you use this flag, don't expect interoperatibility
|
||||
with other JSON systems. Even Jansson itself doesn't have any means
|
||||
to decode JSON texts whose root value is not object or array.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
The following functions perform the actual JSON encoding. The result
|
||||
is in UTF-8.
|
||||
|
||||
@@ -762,14 +780,49 @@ See :ref:`rfc-conformance` for a discussion on Jansson's conformance
|
||||
to the JSON specification. It explains many design decisions that
|
||||
affect especially the behavior of the decoder.
|
||||
|
||||
Each function takes a *flags* parameter that can be used to control
|
||||
the behavior of the decoder. Its default value is 0. The following
|
||||
macros can be ORed together to obtain *flags*.
|
||||
|
||||
``JSON_REJECT_DUPLICATES``
|
||||
Issue a decoding error if any JSON object in the input text
|
||||
contains duplicate keys. Without this flag, the value of the last
|
||||
occurence of each key ends up in the result. Key equivalence is
|
||||
checked byte-by-byte, without special Unicode comparison
|
||||
algorithms.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
``JSON_DISABLE_EOF_CHECK``
|
||||
By default, the decoder expects that its whole input constitutes a
|
||||
valid JSON text, and issues an error if there's extra data after
|
||||
the otherwise valid JSON input. With this flag enabled, the decoder
|
||||
stops after decoding a valid JSON array or object, and thus allows
|
||||
extra data after the JSON text.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
The following functions perform the actual JSON decoding.
|
||||
|
||||
.. function:: json_t *json_loads(const char *input, size_t flags, json_error_t *error)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Decodes the JSON string *input* and returns the array or object it
|
||||
contains, or *NULL* on error, in which case *error* is filled with
|
||||
information about the error. *flags* is currently unused, and
|
||||
should be set to 0.
|
||||
information about the error. *flags* is described above.
|
||||
|
||||
.. function:: json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Decodes the JSON string *buffer*, whose length is *buflen*, and
|
||||
returns the array or object it contains, or *NULL* on error, in
|
||||
which case *error* is filled with information about the error. This
|
||||
is similar to :func:`json_loads()` except that the string doesn't
|
||||
need to be null-terminated. *flags* is described above.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
.. function:: json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
|
||||
|
||||
@@ -777,8 +830,8 @@ affect especially the behavior of the decoder.
|
||||
|
||||
Decodes the JSON text in stream *input* and returns the array or
|
||||
object it contains, or *NULL* on error, in which case *error* is
|
||||
filled with information about the error. *flags* is currently
|
||||
unused, and should be set to 0.
|
||||
filled with information about the error. *flags* is described
|
||||
above.
|
||||
|
||||
.. function:: json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
|
||||
|
||||
@@ -786,8 +839,8 @@ affect especially the behavior of the decoder.
|
||||
|
||||
Decodes the JSON text in file *path* and returns the array or
|
||||
object it contains, or *NULL* on error, in which case *error* is
|
||||
filled with information about the error. *flags* is currently
|
||||
unused, and should be set to 0.
|
||||
filled with information about the error. *flags* is described
|
||||
above.
|
||||
|
||||
|
||||
.. _apiref-pack:
|
||||
|
||||
@@ -48,9 +48,9 @@ copyright = u'2009-2011, Petri Lehtinen'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '2.0'
|
||||
version = '2.1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '2.0'
|
||||
release = '2.1'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
@@ -30,6 +30,12 @@ error::
|
||||
|
||||
All other Unicode codepoints U+0001 through U+10FFFF are allowed.
|
||||
|
||||
Unicode normalization or any other transformation is never performed
|
||||
on any strings (string values or object keys). When checking for
|
||||
equivalence of strings or object keys, the comparison is performed
|
||||
byte by byte between the original UTF-8 representations of the
|
||||
strings.
|
||||
|
||||
Numbers
|
||||
=======
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@ libjansson_la_SOURCES = \
|
||||
value.c
|
||||
libjansson_la_LDFLAGS = \
|
||||
-export-symbols-regex '^json_' \
|
||||
-version-info 4:0:0
|
||||
-version-info 5:0:1
|
||||
|
||||
if GCC
|
||||
# These flags are gcc specific
|
||||
AM_CFLAGS = -Wall -Wextra -Werror
|
||||
AM_CFLAGS = -Wall -Wextra -Wdeclaration-after-statement -Werror
|
||||
endif
|
||||
|
||||
12
src/dump.c
12
src/dump.c
@@ -421,8 +421,10 @@ char *json_dumps(const json_t *json, size_t flags)
|
||||
strbuffer_t strbuff;
|
||||
char *result;
|
||||
|
||||
if(!json_is_array(json) && !json_is_object(json))
|
||||
return NULL;
|
||||
if(!(flags & JSON_ENCODE_ANY)) {
|
||||
if(!json_is_array(json) && !json_is_object(json))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(strbuffer_init(&strbuff))
|
||||
return NULL;
|
||||
@@ -440,8 +442,10 @@ char *json_dumps(const json_t *json, size_t flags)
|
||||
|
||||
int json_dumpf(const json_t *json, FILE *output, size_t flags)
|
||||
{
|
||||
if(!json_is_array(json) && !json_is_object(json))
|
||||
return -1;
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -18,10 +18,12 @@ void jsonp_error_init(json_error_t *error, const char *source)
|
||||
|
||||
void jsonp_error_set_source(json_error_t *error, const char *source)
|
||||
{
|
||||
size_t length;
|
||||
|
||||
if(!error || !source)
|
||||
return;
|
||||
|
||||
size_t length = strlen(source);
|
||||
length = strlen(source);
|
||||
if(length < JSON_ERROR_SOURCE_LENGTH)
|
||||
strcpy(error->source, source);
|
||||
else {
|
||||
|
||||
@@ -126,7 +126,7 @@ static int hashtable_do_del(hashtable_t *hashtable,
|
||||
if(hashtable->free_value)
|
||||
hashtable->free_value(pair->value);
|
||||
|
||||
free(pair);
|
||||
jsonp_free(pair);
|
||||
hashtable->size--;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -21,17 +21,17 @@ extern "C" {
|
||||
/* version */
|
||||
|
||||
#define JANSSON_MAJOR_VERSION 2
|
||||
#define JANSSON_MINOR_VERSION 0
|
||||
#define JANSSON_MINOR_VERSION 1
|
||||
#define JANSSON_MICRO_VERSION 0
|
||||
|
||||
/* Micro version is omitted if it's 0 */
|
||||
#define JANSSON_VERSION "2.0"
|
||||
#define JANSSON_VERSION "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 >= ... */
|
||||
#define JANSSON_VERSION_HEX ((JANSSON_MAJOR_VERSION << 16) | \
|
||||
(JANSSON_MINOR_VERSION << 8) | \
|
||||
(JANSSON_MICRO_VERSION << 0)))
|
||||
(JANSSON_MICRO_VERSION << 0))
|
||||
|
||||
|
||||
/* types */
|
||||
@@ -214,17 +214,25 @@ json_t *json_copy(json_t *value);
|
||||
json_t *json_deep_copy(json_t *value);
|
||||
|
||||
|
||||
/* loading, printing */
|
||||
/* decoding */
|
||||
|
||||
#define JSON_REJECT_DUPLICATES 0x1
|
||||
#define JSON_DISABLE_EOF_CHECK 0x2
|
||||
|
||||
json_t *json_loads(const char *input, size_t flags, json_error_t *error);
|
||||
json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error);
|
||||
json_t *json_loadf(FILE *input, size_t flags, json_error_t *error);
|
||||
json_t *json_load_file(const char *path, size_t flags, json_error_t *error);
|
||||
|
||||
|
||||
/* encoding */
|
||||
|
||||
#define JSON_INDENT(n) (n & 0x1F)
|
||||
#define JSON_COMPACT 0x20
|
||||
#define JSON_ENSURE_ASCII 0x40
|
||||
#define JSON_SORT_KEYS 0x80
|
||||
#define JSON_PRESERVE_ORDER 0x100
|
||||
#define JSON_ENCODE_ANY 0x200
|
||||
|
||||
char *json_dumps(const json_t *json, size_t flags);
|
||||
int json_dumpf(const json_t *json, FILE *output, size_t flags);
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#define JANSSON_PRIVATE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include "jansson.h"
|
||||
#include "hashtable.h"
|
||||
|
||||
@@ -21,6 +20,16 @@
|
||||
#define max(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/* va_copy is a C99 feature. In C89 implementations, it's sometimes
|
||||
available as __va_copy. If not, memcpy() should do the trick. */
|
||||
#ifndef va_copy
|
||||
#ifdef __va_copy
|
||||
#define va_copy __va_copy
|
||||
#else
|
||||
#define va_copy(a, b) memcpy(&(a), &(b), sizeof(va_list))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
hashtable_t hashtable;
|
||||
@@ -57,8 +66,8 @@ typedef struct {
|
||||
#define json_to_real(json_) container_of(json_, json_real_t, json)
|
||||
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
|
||||
|
||||
size_t jsonp_hash_key(const void *ptr);
|
||||
int jsonp_key_equal(const void *ptr1, const void *ptr2);
|
||||
size_t jsonp_hash_str(const void *ptr);
|
||||
int jsonp_str_equal(const void *ptr1, const void *ptr2);
|
||||
|
||||
typedef struct {
|
||||
size_t serial;
|
||||
|
||||
112
src/load.c
112
src/load.c
@@ -12,7 +12,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <jansson.h>
|
||||
@@ -526,6 +525,7 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *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);
|
||||
|
||||
@@ -643,9 +643,9 @@ static void lex_close(lex_t *lex)
|
||||
|
||||
/*** parser ***/
|
||||
|
||||
static json_t *parse_value(lex_t *lex, json_error_t *error);
|
||||
static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error);
|
||||
|
||||
static json_t *parse_object(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *object = json_object();
|
||||
if(!object)
|
||||
@@ -668,6 +668,14 @@ static json_t *parse_object(lex_t *lex, json_error_t *error)
|
||||
if(!key)
|
||||
return NULL;
|
||||
|
||||
if(flags & JSON_REJECT_DUPLICATES) {
|
||||
if(json_object_get(object, key)) {
|
||||
jsonp_free(key);
|
||||
error_set(error, lex, "duplicate object key");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
lex_scan(lex, error);
|
||||
if(lex->token != ':') {
|
||||
jsonp_free(key);
|
||||
@@ -676,7 +684,7 @@ static json_t *parse_object(lex_t *lex, json_error_t *error)
|
||||
}
|
||||
|
||||
lex_scan(lex, error);
|
||||
value = parse_value(lex, error);
|
||||
value = parse_value(lex, flags, error);
|
||||
if(!value) {
|
||||
jsonp_free(key);
|
||||
goto error;
|
||||
@@ -710,7 +718,7 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static json_t *parse_array(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *array = json_array();
|
||||
if(!array)
|
||||
@@ -721,7 +729,7 @@ static json_t *parse_array(lex_t *lex, json_error_t *error)
|
||||
return array;
|
||||
|
||||
while(lex->token) {
|
||||
json_t *elem = parse_value(lex, error);
|
||||
json_t *elem = parse_value(lex, flags, error);
|
||||
if(!elem)
|
||||
goto error;
|
||||
|
||||
@@ -750,7 +758,7 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static json_t *parse_value(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *json;
|
||||
|
||||
@@ -783,11 +791,11 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
|
||||
break;
|
||||
|
||||
case '{':
|
||||
json = parse_object(lex, error);
|
||||
json = parse_object(lex, flags, error);
|
||||
break;
|
||||
|
||||
case '[':
|
||||
json = parse_array(lex, error);
|
||||
json = parse_array(lex, flags, error);
|
||||
break;
|
||||
|
||||
case TOKEN_INVALID:
|
||||
@@ -805,15 +813,30 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
|
||||
return json;
|
||||
}
|
||||
|
||||
static json_t *parse_json(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *result;
|
||||
|
||||
lex_scan(lex, error);
|
||||
if(lex->token != '[' && lex->token != '{') {
|
||||
error_set(error, lex, "'[' or '{' expected");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return parse_value(lex, error);
|
||||
result = parse_value(lex, flags, error);
|
||||
if(!result)
|
||||
return NULL;
|
||||
|
||||
if(!(flags & JSON_DISABLE_EOF_CHECK)) {
|
||||
lex_scan(lex, error);
|
||||
if(lex->token != TOKEN_EOF) {
|
||||
error_set(error, lex, "end of file expected");
|
||||
json_decref(result);
|
||||
result = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
@@ -840,27 +863,56 @@ json_t *json_loads(const char *string, size_t flags, json_error_t *error)
|
||||
{
|
||||
lex_t lex;
|
||||
json_t *result;
|
||||
string_data_t stream_data = {string, 0};
|
||||
string_data_t stream_data;
|
||||
|
||||
(void)flags; /* unused */
|
||||
stream_data.data = string;
|
||||
stream_data.pos = 0;
|
||||
|
||||
if(lex_init(&lex, string_get, (void *)&stream_data))
|
||||
return NULL;
|
||||
|
||||
jsonp_error_init(error, "<string>");
|
||||
result = parse_json(&lex, flags, error);
|
||||
|
||||
result = parse_json(&lex, error);
|
||||
if(!result)
|
||||
goto out;
|
||||
lex_close(&lex);
|
||||
return result;
|
||||
}
|
||||
|
||||
lex_scan(&lex, error);
|
||||
if(lex.token != TOKEN_EOF) {
|
||||
error_set(error, &lex, "end of file expected");
|
||||
json_decref(result);
|
||||
result = NULL;
|
||||
}
|
||||
typedef struct
|
||||
{
|
||||
const char *data;
|
||||
size_t len;
|
||||
size_t pos;
|
||||
} buffer_data_t;
|
||||
|
||||
static int buffer_get(void *data)
|
||||
{
|
||||
char c;
|
||||
buffer_data_t *stream = data;
|
||||
if(stream->pos >= stream->len)
|
||||
return EOF;
|
||||
|
||||
c = stream->data[stream->pos];
|
||||
stream->pos++;
|
||||
return (unsigned char)c;
|
||||
}
|
||||
|
||||
json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error)
|
||||
{
|
||||
lex_t lex;
|
||||
json_t *result;
|
||||
buffer_data_t stream_data;
|
||||
|
||||
stream_data.data = buffer;
|
||||
stream_data.pos = 0;
|
||||
stream_data.len = buflen;
|
||||
|
||||
if(lex_init(&lex, buffer_get, (void *)&stream_data))
|
||||
return NULL;
|
||||
|
||||
jsonp_error_init(error, "<buffer>");
|
||||
result = parse_json(&lex, flags, error);
|
||||
|
||||
out:
|
||||
lex_close(&lex);
|
||||
return result;
|
||||
}
|
||||
@@ -870,7 +922,6 @@ json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
|
||||
lex_t lex;
|
||||
const char *source;
|
||||
json_t *result;
|
||||
(void)flags; /* unused */
|
||||
|
||||
if(lex_init(&lex, (get_func)fgetc, input))
|
||||
return NULL;
|
||||
@@ -881,19 +932,8 @@ json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
|
||||
source = "<stream>";
|
||||
|
||||
jsonp_error_init(error, source);
|
||||
result = parse_json(&lex, flags, error);
|
||||
|
||||
result = parse_json(&lex, error);
|
||||
if(!result)
|
||||
goto out;
|
||||
|
||||
lex_scan(&lex, error);
|
||||
if(lex.token != TOKEN_EOF) {
|
||||
error_set(error, &lex, "end of file expected");
|
||||
json_decref(result);
|
||||
result = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
lex_close(&lex);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
|
||||
*/
|
||||
hashtable_t key_set;
|
||||
|
||||
if(hashtable_init(&key_set, jsonp_hash_key, jsonp_key_equal, NULL, NULL)) {
|
||||
if(hashtable_init(&key_set, jsonp_hash_str, jsonp_str_equal, NULL, NULL)) {
|
||||
set_error(s, "<internal>", "Out of memory");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ int strbuffer_init(strbuffer_t *strbuff)
|
||||
strbuff->size = STRBUFFER_MIN_SIZE;
|
||||
strbuff->length = 0;
|
||||
|
||||
strbuff->value = malloc(strbuff->size);
|
||||
strbuff->value = jsonp_malloc(strbuff->size);
|
||||
if(!strbuff->value)
|
||||
return -1;
|
||||
|
||||
@@ -30,7 +30,7 @@ int strbuffer_init(strbuffer_t *strbuff)
|
||||
|
||||
void strbuffer_close(strbuffer_t *strbuff)
|
||||
{
|
||||
free(strbuff->value);
|
||||
jsonp_free(strbuff->value);
|
||||
strbuff->size = 0;
|
||||
strbuff->length = 0;
|
||||
strbuff->value = NULL;
|
||||
|
||||
40
src/value.c
40
src/value.c
@@ -26,15 +26,10 @@ static JSON_INLINE void json_init(json_t *json, json_type type)
|
||||
|
||||
/*** object ***/
|
||||
|
||||
/* This macro just returns a pointer that's a few bytes backwards from
|
||||
string. This makes it possible to pass a pointer to object_key_t
|
||||
when only the string inside it is used, without actually creating
|
||||
an object_key_t instance. */
|
||||
#define string_to_key(string) container_of(string, object_key_t, key)
|
||||
|
||||
size_t jsonp_hash_key(const void *ptr)
|
||||
/* From http://www.cse.yorku.ca/~oz/hash.html */
|
||||
size_t jsonp_hash_str(const void *ptr)
|
||||
{
|
||||
const char *str = ((const object_key_t *)ptr)->key;
|
||||
const char *str = (const char *)ptr;
|
||||
|
||||
size_t hash = 5381;
|
||||
size_t c;
|
||||
@@ -48,10 +43,26 @@ size_t jsonp_hash_key(const void *ptr)
|
||||
return hash;
|
||||
}
|
||||
|
||||
int jsonp_key_equal(const void *ptr1, const void *ptr2)
|
||||
int jsonp_str_equal(const void *ptr1, const void *ptr2)
|
||||
{
|
||||
return strcmp(((const object_key_t *)ptr1)->key,
|
||||
((const object_key_t *)ptr2)->key) == 0;
|
||||
return strcmp((const char *)ptr1, (const char *)ptr2) == 0;
|
||||
}
|
||||
|
||||
/* This macro just returns a pointer that's a few bytes backwards from
|
||||
string. This makes it possible to pass a pointer to object_key_t
|
||||
when only the string inside it is used, without actually creating
|
||||
an object_key_t instance. */
|
||||
#define string_to_key(string) container_of(string, object_key_t, key)
|
||||
|
||||
static size_t hash_key(const void *ptr)
|
||||
{
|
||||
return jsonp_hash_str(((const object_key_t *)ptr)->key);
|
||||
}
|
||||
|
||||
static int key_equal(const void *ptr1, const void *ptr2)
|
||||
{
|
||||
return jsonp_str_equal(((const object_key_t *)ptr1)->key,
|
||||
((const object_key_t *)ptr2)->key);
|
||||
}
|
||||
|
||||
static void value_decref(void *value)
|
||||
@@ -67,7 +78,7 @@ json_t *json_object(void)
|
||||
json_init(&object->json, JSON_OBJECT);
|
||||
|
||||
if(hashtable_init(&object->hashtable,
|
||||
jsonp_hash_key, jsonp_key_equal,
|
||||
hash_key, key_equal,
|
||||
jsonp_free, value_decref))
|
||||
{
|
||||
jsonp_free(object);
|
||||
@@ -91,7 +102,7 @@ size_t json_object_size(const json_t *json)
|
||||
json_object_t *object;
|
||||
|
||||
if(!json_is_object(json))
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
object = json_to_object(json);
|
||||
return object->hashtable.size;
|
||||
@@ -128,7 +139,10 @@ int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
|
||||
allocated. */
|
||||
k = jsonp_malloc(offsetof(object_key_t, key) + strlen(key) + 1);
|
||||
if(!k)
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
k->serial = object->serial++;
|
||||
strcpy(k->key, key);
|
||||
|
||||
1
test/.gitignore
vendored
1
test/.gitignore
vendored
@@ -6,6 +6,7 @@ suites/api/test_cpp
|
||||
suites/api/test_dump
|
||||
suites/api/test_equal
|
||||
suites/api/test_load
|
||||
suites/api/test_loadb
|
||||
suites/api/test_memory_funcs
|
||||
suites/api/test_number
|
||||
suites/api/test_object
|
||||
|
||||
@@ -30,31 +30,46 @@ for test_path in $suite_srcdir/*; do
|
||||
rm -rf $test_log
|
||||
mkdir -p $test_log
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
echo -n "$test_name... "
|
||||
printf '%s... ' "$test_name"
|
||||
fi
|
||||
|
||||
if run_test; then
|
||||
# Success
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
echo "ok"
|
||||
else
|
||||
echo -n "."
|
||||
fi
|
||||
rm -rf $test_log
|
||||
else
|
||||
# Failure
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
echo "FAILED"
|
||||
else
|
||||
echo -n "F"
|
||||
fi
|
||||
run_test
|
||||
case $? in
|
||||
0)
|
||||
# Success
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
printf 'ok\n'
|
||||
else
|
||||
printf '.'
|
||||
fi
|
||||
rm -rf $test_log
|
||||
;;
|
||||
|
||||
[ $STOP -eq 1 ] && break
|
||||
fi
|
||||
77)
|
||||
# Skip
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
printf 'skipped\n'
|
||||
else
|
||||
printf 'S'
|
||||
fi
|
||||
rm -rf $test_log
|
||||
;;
|
||||
|
||||
*)
|
||||
# Failure
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
printf 'FAILED\n'
|
||||
else
|
||||
printf 'F'
|
||||
fi
|
||||
|
||||
[ $STOP -eq 1 ] && break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ $VERBOSE -eq 0 ]; then
|
||||
echo
|
||||
printf '\n'
|
||||
fi
|
||||
|
||||
if [ -n "$(ls -A $suite_log)" ]; then
|
||||
|
||||
@@ -6,6 +6,7 @@ check_PROGRAMS = \
|
||||
test_dump \
|
||||
test_equal \
|
||||
test_load \
|
||||
test_loadb \
|
||||
test_memory_funcs \
|
||||
test_number \
|
||||
test_object \
|
||||
@@ -17,6 +18,7 @@ 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_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
|
||||
test_number_SOURCES = test_number.c util.h
|
||||
test_object_SOURCES = test_object.c util.h
|
||||
|
||||
@@ -50,6 +50,7 @@ json_dump_file
|
||||
json_loads
|
||||
json_loadf
|
||||
json_load_file
|
||||
json_loadb
|
||||
json_equal
|
||||
json_copy
|
||||
json_deep_copy
|
||||
@@ -89,7 +90,10 @@ EOF
|
||||
|
||||
SOFILE="../src/.libs/libjansson.so"
|
||||
|
||||
nm -D $SOFILE | grep ' T ' | cut -d' ' -f3 | sort >$test_log/output
|
||||
nm -D $SOFILE >/dev/null >$test_log/symbols 2>/dev/null \
|
||||
|| exit 77 # Skip if "nm -D" doesn't seem to work
|
||||
|
||||
grep ' T ' $test_log/symbols | cut -d' ' -f3 | sort >$test_log/output
|
||||
|
||||
if ! cmp -s $test_log/exports $test_log/output; then
|
||||
diff -u $test_log/exports $test_log/output >&2
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
#include <string.h>
|
||||
#include "util.h"
|
||||
|
||||
int main()
|
||||
static void encode_twice()
|
||||
{
|
||||
/* Encode an empty object/array, add an item, encode again */
|
||||
|
||||
json_t *json;
|
||||
char *result;
|
||||
|
||||
/* Encode an empty object/array, add an item, encode again */
|
||||
|
||||
json = json_object();
|
||||
result = json_dumps(json, 0);
|
||||
if(!result || strcmp(result, "{}"))
|
||||
@@ -43,7 +43,10 @@ int main()
|
||||
free(result);
|
||||
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
static void circular_references()
|
||||
{
|
||||
/* Construct a JSON object/array with a circular reference:
|
||||
|
||||
object: {"a": {"b": {"c": <circular reference to $.a>}}}
|
||||
@@ -51,6 +54,10 @@ int main()
|
||||
|
||||
Encode it, remove the circular reference and encode again.
|
||||
*/
|
||||
|
||||
json_t *json;
|
||||
char *result;
|
||||
|
||||
json = json_object();
|
||||
json_object_set_new(json, "a", json_object());
|
||||
json_object_set_new(json_object_get(json, "a"), "b", json_object());
|
||||
@@ -86,6 +93,50 @@ int main()
|
||||
free(result);
|
||||
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
static void encode_other_than_array_or_object()
|
||||
{
|
||||
/* Encoding anything other than array or object should only
|
||||
* succeed if the JSON_ENCODE_ANY flag is used */
|
||||
|
||||
json_t *json;
|
||||
FILE *fp = NULL;
|
||||
char *result;
|
||||
|
||||
json = json_string("foo");
|
||||
if(json_dumps(json, 0) != NULL)
|
||||
fail("json_dumps encoded a string!");
|
||||
if(json_dumpf(json, fp, 0) == 0)
|
||||
fail("json_dumpf encoded a string!");
|
||||
|
||||
result = json_dumps(json, JSON_ENCODE_ANY);
|
||||
if(!result || strcmp(result, "\"foo\"") != 0)
|
||||
fail("json_dumps failed to encode a string with JSON_ENCODE_ANY");
|
||||
|
||||
free(result);
|
||||
json_decref(json);
|
||||
|
||||
json = json_integer(42);
|
||||
if(json_dumps(json, 0) != NULL)
|
||||
fail("json_dumps encoded an integer!");
|
||||
if(json_dumpf(json, fp, 0) == 0)
|
||||
fail("json_dumpf encoded an integer!");
|
||||
|
||||
result = json_dumps(json, JSON_ENCODE_ANY);
|
||||
if(!result || strcmp(result, "42") != 0)
|
||||
fail("json_dumps failed to encode an integer with JSON_ENCODE_ANY");
|
||||
|
||||
free(result);
|
||||
json_decref(json);
|
||||
|
||||
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
encode_twice();
|
||||
circular_references();
|
||||
encode_other_than_array_or_object();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -9,16 +9,52 @@
|
||||
#include <string.h>
|
||||
#include "util.h"
|
||||
|
||||
int main()
|
||||
static void file_not_found()
|
||||
{
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
|
||||
json = json_load_file("/path/to/nonexistent/file.json", 0, &error);
|
||||
if(json)
|
||||
fail("json_load_file returned non-NULL for a nonexistent file");
|
||||
if(error.line != -1)
|
||||
fail("json_load_file returned an invalid line number");
|
||||
if(strcmp(error.text, "unable to open /path/to/nonexistent/file.json: No such file or directory") != 0)
|
||||
fail("json_load_file returned an invalid error message");
|
||||
}
|
||||
|
||||
static void reject_duplicates()
|
||||
{
|
||||
json_error_t error;
|
||||
|
||||
if(json_loads("{\"foo\": 1, \"foo\": 2}", JSON_REJECT_DUPLICATES, &error))
|
||||
fail("json_loads did not detect a duplicate key");
|
||||
check_error("duplicate object key near '\"foo\"'", "<string>", 1, 16, 16);
|
||||
}
|
||||
|
||||
static void disable_eof_check()
|
||||
{
|
||||
json_error_t error;
|
||||
json_t *json;
|
||||
|
||||
const char *text = "{\"foo\": 1} garbage";
|
||||
|
||||
if(json_loads(text, 0, &error))
|
||||
fail("json_loads did not detect garbage after JSON text");
|
||||
check_error("end of file expected near 'garbage'", "<string>", 1, 18, 18);
|
||||
|
||||
json = json_loads(text, JSON_DISABLE_EOF_CHECK, &error);
|
||||
if(!json)
|
||||
fail("json_loads failed with JSON_DISABLE_EOF_CHECK");
|
||||
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
file_not_found();
|
||||
reject_duplicates();
|
||||
disable_eof_check();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
38
test/suites/api/test_loadb.c
Normal file
38
test/suites/api/test_loadb.c
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 "util.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
const char str[] = "[\"A\", {\"B\": \"C\"}, 1, 2, 3]garbage";
|
||||
size_t len = strlen(str) - strlen("garbage");
|
||||
|
||||
json = json_loadb(str, len, 0, &error);
|
||||
if(!json) {
|
||||
fail("json_loadb failed on a valid JSON buffer");
|
||||
}
|
||||
json_decref(json);
|
||||
|
||||
json = json_loadb(str, len - 1, 0, &error);
|
||||
if (json) {
|
||||
json_decref(json);
|
||||
fail("json_loadb should have failed on an incomplete buffer, but it didn't");
|
||||
}
|
||||
if(error.line != 1) {
|
||||
fail("json_loadb returned an invalid line number on fail");
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -122,6 +122,13 @@ int main()
|
||||
fail("json_unpack simple array failed");
|
||||
json_decref(j);
|
||||
|
||||
/* object with many items & strict checking */
|
||||
j = json_pack("{s:i, s:i, s:i}", "a", 1, "b", 2, "c", 3);
|
||||
rv = json_unpack(j, "{s:i, s:i, s:i}", "a", &i1, "b", &i2, "c", &i3);
|
||||
if(rv || i1 != 1 || i2 != 2 || i3 != 3)
|
||||
fail("json_unpack object with many items failed");
|
||||
json_decref(j);
|
||||
|
||||
/*
|
||||
* Invalid cases
|
||||
*/
|
||||
@@ -285,7 +292,7 @@ int main()
|
||||
/* Unpack the same item twice */
|
||||
j = json_pack("{s:s, s:i}", "foo", "bar", "baz", 42);
|
||||
if(!json_unpack_ex(j, &error, 0, "{s:s,s:s!}", "foo", &s, "foo", &s))
|
||||
fail("json_unpack object with strict validation failed");
|
||||
fail("json_unpack object with strict validation failed");
|
||||
check_error("1 object item(s) left unpacked", "<validation>", 1, 10, 10);
|
||||
json_decref(j);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user