32 Commits
v2.0 ... v2.1

Author SHA1 Message Date
Petri Lehtinen
86d17a8dc2 jansson 2.1 2011-06-10 21:30:11 +03:00
Petri Lehtinen
3988ab2d27 Use `literal` syntax with macro and flag names in CHANGES 2011-06-07 21:40:09 +03:00
Petri Lehtinen
f7f7bf5ab2 Prepare for relase: Add CHANGES entry for v2.1 2011-06-07 21:33:59 +03:00
Petri Lehtinen
a76ba52f34 Add JSON_DISABLE_EOF_CHECK decoding flag
With this flag enabled, the decoder stops after a valid JSON input and
thus allows extra data after it.

Fixes GH-25.
2011-05-29 21:27:15 +03:00
Petri Lehtinen
9febdf333c Reduce code duplication in the decoder 2011-05-29 12:53:25 +03:00
Petri Lehtinen
013b8b3f60 Clear errno before calling strtod()
Fixes GH-27.
2011-05-24 09:59:41 +03:00
Petri Lehtinen
49fc708d4c Add JSON_REJECT_DUPLICATES decoding flag
With this flag, a decoding error is issued if any JSON object in the
input contains duplicate keys.

Fixes GH-3.
2011-05-15 13:57:49 +03:00
Petri Lehtinen
92f6f0f22c doc: Clarify that Unicode normalization or comparison algorithms are not used 2011-05-15 12:39:10 +03:00
Petri Lehtinen
e9e34f430e doc: Add "New in version 2.1" notes to new features 2011-05-14 20:52:14 +03:00
Petri Lehtinen
ab723c7fb5 docs: Add a note that object iteration doesn't give any particular order
Closes GH-15.
2011-05-14 13:04:35 +03:00
Petri Lehtinen
636d5f60f9 Add JSON_ENCODE_ANY flag to allow encoding any JSON value
Closes GH-19.
2011-05-14 12:57:12 +03:00
Petri Lehtinen
c3492973e1 Merge branch '2.0' 2011-04-21 13:15:58 +03:00
Petri Lehtinen
e20619e071 Fix a leak when memory allocation fails in json_object_set() & friends 2011-04-21 13:15:22 +03:00
Petri Lehtinen
b44e2be032 Add json_loadb() for decoding possibly non null-terminated strings
Thanks to Jonathan Landis for the initial patch.
2011-04-10 21:01:50 +03:00
Petri Lehtinen
76d6d700ad Merge branch '2.0' 2011-04-05 15:38:37 +03:00
Jim Meyering
c96763215d Avoid set-but-not-used warning/error in a test
Closes GH-20.
2011-04-05 15:36:13 +03:00
Petri Lehtinen
4a76900bd7 Merge branch '2.0'
Conflicts:
	doc/conf.py
	src/jansson.h
2011-03-31 21:26:19 +03:00
Petri Lehtinen
1c0a3b2a55 jansson 2.0.1 2011-03-31 16:59:26 +03:00
Petri Lehtinen
056702e541 Merge branch '2.0' 2011-03-31 16:39:33 +03:00
Petri Lehtinen
eab23f05d8 Fix a few malloc() and free() calls
Replace them with jsonp_malloc() and jsonp_free() to support the
custom memory allocation.
2011-03-31 16:37:43 +03:00
Petri Lehtinen
0944ac8d91 Fix invalid object key hashing in json_unpack() strict checking mode
The keys which are stored temporarily to a hashtable to make sure that
all object keys are unpacked, were hashed with the object key hashing
function. It is meant to compute hashes for object_key_t values, and
it works incorrectly for plain strings.

Fixed by introducing suitable functions for hashing and comparing
strings for string-keyed hashtables.
2011-03-30 12:57:48 +03:00
Petri Lehtinen
a5a43caa9a Merge branch '2.0' 2011-03-27 21:04:29 +03:00
Petri Lehtinen
5456fc59ab Set JANSSON_MICRO_VERSION to 255 (0xFF) for git master
Also, set documentation release to "2.0+git". This shows up in the
automatically built HTML documentation.

These changes makes it easier for library users to know that this
version is under development.

Closes GH-14.
2011-03-27 21:03:04 +03:00
Petri Lehtinen
279d8bf108 Use the correct number of parens in JANSSON_VERSION_HEX macro 2011-03-27 14:12:40 +03:00
Petri Lehtinen
38e35e0973 Test framework enhancements, fix the check_exports test
* Use "printf" instead of "echo -n" as it's more portable
* Treat a test as skipped if it exits with exit status of 77
* Skip the check_exports test if "nm -D" doesn't work
2011-03-27 13:34:43 +03:00
Petri Lehtinen
af18578928 Use the correct number of parens in JANSSON_VERSION_HEX macro 2011-03-23 08:25:50 +02:00
Petri Lehtinen
c30e92603c ANSI C requires struct initializers to be constant expressions
Closes GH-17.
2011-03-21 09:28:14 +02:00
Petri Lehtinen
6ecba84817 Fix json_object_size() return value
Return 0 as documented if the argument is not a JSON object.

Closes GH-18.
2011-03-21 08:22:34 +02:00
Petri Lehtinen
b90ed1accb Enhance portability of va_copy()
va_copy() is a C99 feature. In C89 implementations, it's sometimes
available as __va_copy(). If not, memcpy() should do the trick.
2011-03-20 21:23:37 +02:00
Petri Lehtinen
1111960120 Fix a declaration after statement
While at it, add -Wdeclaration-after-statement to AM_CFLAGS to catch
these in the future.
2011-03-10 21:28:53 +02:00
Petri Lehtinen
7f09f48e7e Distribute doc/upgrading.rst
Closes GH-16.
2011-03-10 21:26:18 +02:00
Petri Lehtinen
b397711a66 Check documentation in make distcheck
It seems that the only way to do this is to use the dvi make target,
as it's built in make distcheck after running configure.
2011-03-10 21:24:45 +02:00
25 changed files with 462 additions and 106 deletions

64
CHANGES
View File

@@ -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
===========

View File

@@ -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

View File

@@ -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])

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 \
ext/refcounting.py
upgrading.rst ext/refcounting.py
SPHINXBUILD = sphinx-build
SPHINXOPTS = -d _build/doctrees $(SPHINXOPTS_EXTRA)

View File

@@ -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:

View File

@@ -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.

View File

@@ -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
=======

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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
View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View 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;
}

View File

@@ -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);