Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c2ca3fae6 | ||
|
|
2ae279e0d4 | ||
|
|
4c6cb6afd1 | ||
|
|
78594e9bd3 | ||
|
|
e921e63b54 | ||
|
|
f9febb64c5 | ||
|
|
b76ee75aad | ||
|
|
b077d7988e | ||
|
|
7f8684828d | ||
|
|
93ac06c902 | ||
|
|
b21f07b35c | ||
|
|
508873de9b | ||
|
|
aeb5b481c9 | ||
|
|
9db34dc31a | ||
|
|
95a468cebb | ||
|
|
22173c1d8b | ||
|
|
dd2fe1ebe8 | ||
|
|
6637b976ed | ||
|
|
f5202bedef | ||
|
|
2db2f2cfb6 | ||
|
|
e7a5dc58e6 | ||
|
|
3889af476b | ||
|
|
34fb97998c | ||
|
|
ec96cbf016 | ||
|
|
19a606d736 | ||
|
|
3add1cf361 | ||
|
|
50031440a3 | ||
|
|
d67aeb9739 | ||
|
|
7c707a73a2 | ||
|
|
330e892ff6 | ||
|
|
d857fd08a5 | ||
|
|
e0a88d19d1 | ||
|
|
842bc2128b | ||
|
|
ca6d26a1c2 | ||
|
|
17d913307e | ||
|
|
f236c14dc5 | ||
|
|
bf01067a8a | ||
|
|
d3959a8ce7 | ||
|
|
f243930b68 | ||
|
|
d1b07171cc | ||
|
|
15d992cb6a | ||
|
|
59c58ea26c | ||
|
|
f95bb423a3 | ||
|
|
9448ed3ef7 | ||
|
|
7c7a1ed01e | ||
|
|
5ff8ae8052 | ||
|
|
8d53f447bf | ||
|
|
325101e525 | ||
|
|
1b02f3546c | ||
|
|
cd96049d10 | ||
|
|
a83cd30c31 | ||
|
|
a00988f663 | ||
|
|
86dc1d629b | ||
|
|
b67e130fda | ||
|
|
4cd777712b | ||
|
|
79009e62c1 | ||
|
|
76999799ed | ||
|
|
22af193a51 | ||
|
|
951d091f07 | ||
|
|
185e107d24 | ||
|
|
ca7703fbd1 | ||
|
|
1e00cd58a5 | ||
|
|
40bb7bf437 | ||
|
|
9d648a87cb | ||
|
|
8de850be95 | ||
|
|
cbacac5975 | ||
|
|
b3e1fe2ec5 | ||
|
|
7728716759 | ||
|
|
5406c2b3d3 | ||
|
|
743af38e7f | ||
|
|
9d16ec755c | ||
|
|
04d550b02e | ||
|
|
55d2566539 | ||
|
|
7ee974e91c | ||
|
|
c288188a0f | ||
|
|
057ba29a27 | ||
|
|
234ee47281 | ||
|
|
98a8c1aebf | ||
|
|
42e546d065 | ||
|
|
6ffb04f0d4 | ||
|
|
fd259ff68c | ||
|
|
9611780907 | ||
|
|
93c5892bc3 | ||
|
|
1095ca956b | ||
|
|
05654bfcba | ||
|
|
89d09813c3 | ||
|
|
5ac4914642 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -22,3 +22,4 @@ missing
|
||||
*.la
|
||||
stamp-h1
|
||||
*.pyc
|
||||
*.pc
|
||||
|
||||
129
CHANGES
129
CHANGES
@@ -1,11 +1,117 @@
|
||||
Version 1.0.4, released 2009-10-11
|
||||
Version 1.2
|
||||
===========
|
||||
|
||||
Released 2010-01-21
|
||||
|
||||
* New functions:
|
||||
|
||||
- `json_equal()`: Test whether two JSON values are equal
|
||||
- `json_copy()` and `json_deep_copy()`: Make shallow and deep copies
|
||||
of JSON values
|
||||
- Add a version of all functions taking a string argument that
|
||||
doesn't check for valid UTF-8: `json_string_nocheck()`,
|
||||
`json_string_set_nocheck()`, `json_object_set_nocheck()`,
|
||||
`json_object_set_new_nocheck()`
|
||||
|
||||
* New encoding flags:
|
||||
|
||||
- ``JSON_SORT_KEYS``: Sort objects by key
|
||||
- ``JSON_ENSURE_ASCII``: Escape all non-ASCII Unicode characters
|
||||
- ``JSON_COMPACT``: Use a compact representation with all unneeded
|
||||
whitespace stripped
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- Revise and unify whitespace usage in encoder: Add spaces between
|
||||
array and object items, never append newline to output.
|
||||
- Remove const qualifier from the ``json_t`` parameter in
|
||||
`json_string_set()`, `json_integer_set()` and `json_real_set`.
|
||||
- Use ``int32_t`` internally for representing Unicode code points
|
||||
(int is not enough on all platforms)
|
||||
|
||||
* Other changes:
|
||||
|
||||
- Convert ``CHANGES`` (this file) to reStructured text and add it to
|
||||
HTML documentation
|
||||
- The test system has been refactored. Python is no longer required
|
||||
to run the tests.
|
||||
- Documentation can now be built by invoking ``make html``
|
||||
- Support for pkg-config
|
||||
|
||||
|
||||
Version 1.1.3
|
||||
=============
|
||||
|
||||
Released 2009-12-18
|
||||
|
||||
* Encode reals correctly, so that first encoding and then decoding a
|
||||
real always produces the same value
|
||||
* Don't export private symbols in ``libjansson.so``
|
||||
|
||||
|
||||
Version 1.1.2
|
||||
=============
|
||||
|
||||
Released 2009-11-08
|
||||
|
||||
* Fix a bug where an error message was not produced if the input file
|
||||
could not be opened in `json_load_file()`
|
||||
* Fix an assertion failure in decoder caused by a minus sign without a
|
||||
digit after it
|
||||
* Remove an unneeded include of ``stdint.h`` in ``jansson.h``
|
||||
|
||||
|
||||
Version 1.1.1
|
||||
=============
|
||||
|
||||
Released 2009-10-26
|
||||
|
||||
* All documentation files were not distributed with v1.1; build
|
||||
documentation in make distcheck to prevent this in the future
|
||||
* Fix v1.1 release date in ``CHANGES``
|
||||
|
||||
|
||||
Version 1.1
|
||||
===========
|
||||
|
||||
Released 2009-10-20
|
||||
|
||||
* API additions and improvements:
|
||||
|
||||
- Extend array and object APIs
|
||||
- Add functions to modify integer, real and string values
|
||||
- Improve argument validation
|
||||
- Use unsigned int instead of ``uint32_t`` for encoding flags
|
||||
|
||||
* Enhance documentation
|
||||
|
||||
- Add getting started guide and tutorial
|
||||
- Fix some typos
|
||||
- General clarifications and cleanup
|
||||
|
||||
* Check for integer and real overflows and underflows in decoder
|
||||
* Make singleton values thread-safe (``true``, ``false`` and ``null``)
|
||||
* Enhance circular reference handling
|
||||
* Don't define ``-std=c99`` in ``AM_CFLAGS``
|
||||
* Add C++ guards to ``jansson.h``
|
||||
* Minor performance and portability improvements
|
||||
* Expand test coverage
|
||||
|
||||
|
||||
Version 1.0.4
|
||||
=============
|
||||
|
||||
Released 2009-10-11
|
||||
|
||||
* Relax Autoconf version requirement to 2.59
|
||||
* Make Jansson compile on platforms where plain char is unsigned
|
||||
* Make Jansson compile on platforms where plain ``char`` is unsigned
|
||||
* Fix API tests for object
|
||||
|
||||
|
||||
Version 1.0.3, released 2009-09-14
|
||||
Version 1.0.3
|
||||
=============
|
||||
|
||||
Released 2009-09-14
|
||||
|
||||
* Check for integer and real overflows and underflows in decoder
|
||||
* Use the Python json module for tests, or simplejson if the json
|
||||
@@ -13,16 +119,25 @@ Version 1.0.3, released 2009-09-14
|
||||
* Distribute changelog (this file)
|
||||
|
||||
|
||||
Version 1.0.2, released 2009-09-08
|
||||
Version 1.0.2
|
||||
=============
|
||||
|
||||
Released 2009-09-08
|
||||
|
||||
* Handle EOF correctly in decoder
|
||||
|
||||
|
||||
Version 1.0.1, released 2009-09-04
|
||||
Version 1.0.1
|
||||
=============
|
||||
|
||||
* Fixed broken json_is_boolean()
|
||||
Released 2009-09-04
|
||||
|
||||
* Fixed broken `json_is_boolean()`
|
||||
|
||||
|
||||
Version 1.0, released 2009-08-25
|
||||
Version 1.0
|
||||
===========
|
||||
|
||||
Released 2009-08-25
|
||||
|
||||
* Initial release
|
||||
|
||||
@@ -1,2 +1,7 @@
|
||||
EXTRA_DIST = CHANGES LICENSE README.rst
|
||||
SUBDIRS = doc src test
|
||||
|
||||
check-local: html
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = jansson.pc
|
||||
|
||||
14
README.rst
14
README.rst
@@ -6,14 +6,14 @@ data. Its main features and design principles are:
|
||||
|
||||
- Simple and intuitive API and data model
|
||||
|
||||
- Good documentation
|
||||
- Comprehensive documentation
|
||||
|
||||
- No dependencies on other libraries
|
||||
|
||||
- Full Unicode support (UTF-8)
|
||||
|
||||
- Extensive test suite
|
||||
|
||||
- No dependencies on other libraries
|
||||
|
||||
Jansson is licensed under the `MIT license`_; see LICENSE in the
|
||||
source distribution for details.
|
||||
|
||||
@@ -36,8 +36,6 @@ To run the test suite, invoke::
|
||||
|
||||
$ make check
|
||||
|
||||
Python_ is required to run the tests.
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
@@ -49,15 +47,13 @@ available at http://www.digip.org/jansson/doc/.
|
||||
|
||||
To generate HTML documentation yourself, invoke::
|
||||
|
||||
cd doc/
|
||||
sphinx-build . .build/html
|
||||
make html
|
||||
|
||||
... and point your browser to ``.build/html/index.html``. Sphinx_ is
|
||||
and point your browser to ``doc/_build/html/index.html``. Sphinx_ is
|
||||
required to generate the documentation.
|
||||
|
||||
|
||||
.. _Jansson: http://www.digip.org/jansson/
|
||||
.. _`MIT license`: http://www.opensource.org/licenses/mit-license.php
|
||||
.. _Python: http://www.python.org/
|
||||
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
|
||||
.. _Sphinx: http://sphinx.pocoo.org/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
AC_PREREQ([2.59])
|
||||
AC_INIT([jansson], [1.0.4], [petri@digip.org])
|
||||
AC_INIT([jansson], [1.2], [petri@digip.org])
|
||||
|
||||
AM_INIT_AUTOMAKE([1.10 foreign])
|
||||
|
||||
@@ -19,11 +19,13 @@ AC_PROG_LIBTOOL
|
||||
# Checks for library functions.
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
jansson.pc
|
||||
Makefile
|
||||
doc/Makefile
|
||||
src/Makefile
|
||||
test/Makefile
|
||||
test/testdata/Makefile
|
||||
test/testprogs/Makefile
|
||||
test/bin/Makefile
|
||||
test/suites/Makefile
|
||||
test/suites/api/Makefile
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
||||
2
doc/.gitignore
vendored
2
doc/.gitignore
vendored
@@ -1 +1 @@
|
||||
.build/
|
||||
_build/
|
||||
|
||||
@@ -1,4 +1,19 @@
|
||||
EXTRA_DIST = conf.py apiref.rst index.rst ext/refcounting.py
|
||||
EXTRA_DIST = conf.py apiref.rst changes.rst gettingstarted.rst \
|
||||
github_commits.c index.rst tutorial.rst ext/refcounting.py
|
||||
|
||||
SPHINXBUILD = sphinx-build
|
||||
SPHINXOPTS = -d _build/doctrees -W
|
||||
|
||||
html-local:
|
||||
$(SPHINXBUILD) -b html $(SPHINXOPTS) $(srcdir) _build/html
|
||||
|
||||
install-html-local: html
|
||||
mkdir -p $(DESTDIR)$(htmldir)
|
||||
cp -r _build/html $(DESTDIR)$(htmldir)
|
||||
|
||||
uninstall-local:
|
||||
rm -rf $(DESTDIR)$(htmldir)
|
||||
|
||||
clean-local:
|
||||
rm -rf .build
|
||||
rm -rf _build
|
||||
rm -f ext/refcounting.pyc changes.rst
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
To build the documentation, invoke
|
||||
|
||||
sphinx-build . .build/html
|
||||
make html
|
||||
|
||||
in this directory. Then point your browser to .build/html/index.html.
|
||||
Then point your browser to _build/html/index.html.
|
||||
|
||||
407
doc/apiref.rst
407
doc/apiref.rst
@@ -1,3 +1,5 @@
|
||||
.. _apiref:
|
||||
|
||||
*************
|
||||
API Reference
|
||||
*************
|
||||
@@ -41,6 +43,12 @@ Objects of :ctype:`json_t` are always used through a pointer. There
|
||||
are APIs for querying the type, manipulating the reference count, and
|
||||
for constructing and manipulating values of different types.
|
||||
|
||||
Unless noted otherwise, all API functions return an error value if an
|
||||
error occurs. Depending on the function's signature, the error value
|
||||
is either *NULL* or -1. Invalid arguments or invalid input are
|
||||
apparent sources for errors. Memory allocation and I/O operations may
|
||||
also cause errors.
|
||||
|
||||
|
||||
Type
|
||||
----
|
||||
@@ -80,8 +88,8 @@ functions:
|
||||
.. cfunction:: int json_typeof(const json_t *json)
|
||||
|
||||
Return the type of the JSON value (a :ctype:`json_type` cast to
|
||||
:ctype:`int`). This function is actually implemented as a macro for
|
||||
speed.
|
||||
:ctype:`int`). *json* MUST NOT be *NULL*. This function is actually
|
||||
implemented as a macro for speed.
|
||||
|
||||
.. cfunction:: json_is_object(const json_t *json)
|
||||
json_is_array(const json_t *json)
|
||||
@@ -93,19 +101,22 @@ functions:
|
||||
json_is_null(const json_t *json)
|
||||
|
||||
These functions (actually macros) return true (non-zero) for values
|
||||
of the given type, and false (zero) for values of other types.
|
||||
of the given type, and false (zero) for values of other types and
|
||||
for *NULL*.
|
||||
|
||||
.. cfunction:: json_is_number(const json_t *json)
|
||||
|
||||
Returns true for values of types :const:`JSON_INTEGER` and
|
||||
:const:`JSON_REAL`, and false for other types.
|
||||
:const:`JSON_REAL`, and false for other types and for *NULL*.
|
||||
|
||||
.. cfunction:: json_is_boolean(const json_t *json)
|
||||
|
||||
Returns true for types :const:`JSON_TRUE` and :const:`JSON_FALSE`,
|
||||
and false for values of other types.
|
||||
and false for values of other types and for *NULL*.
|
||||
|
||||
|
||||
.. _apiref-reference-count:
|
||||
|
||||
Reference Count
|
||||
---------------
|
||||
|
||||
@@ -121,7 +132,8 @@ The following functions are used to manipulate the reference count.
|
||||
|
||||
.. cfunction:: json_t *json_incref(json_t *json)
|
||||
|
||||
Increment the reference count of *json*.
|
||||
Increment the reference count of *json* if it's not non-*NULL*.
|
||||
Returns *json*.
|
||||
|
||||
.. cfunction:: void json_decref(json_t *json)
|
||||
|
||||
@@ -151,29 +163,60 @@ will return a new or borrowed reference or steal a reference to its
|
||||
argument.
|
||||
|
||||
|
||||
Circular References
|
||||
-------------------
|
||||
|
||||
A circular reference is created when an object or an array is,
|
||||
directly or indirectly, inserted inside itself. The direct case is
|
||||
simple::
|
||||
|
||||
json_t *obj = json_object();
|
||||
json_object_set(obj, "foo", obj);
|
||||
|
||||
Jansson will refuse to do this, and :cfunc:`json_object_set()` (and
|
||||
all the other such functions for objects and arrays) will return with
|
||||
an error status. The indirect case is the dangerous one::
|
||||
|
||||
json_t *arr1 = json_array(), *arr2 = json_array();
|
||||
json_array_append(arr1, arr2);
|
||||
json_array_append(arr2, arr1);
|
||||
|
||||
In this example, the array ``arr2`` is contained in the array
|
||||
``arr1``, and vice versa. Jansson cannot check for this kind of
|
||||
indirect circular references without a performance hit, so it's up to
|
||||
the user to avoid them.
|
||||
|
||||
If a circular reference is created, the memory consumed by the values
|
||||
cannot be freed by :cfunc:`json_decref()`. The reference counts never
|
||||
drops to zero because the values are keeping the circular reference to
|
||||
themselves. Moreover, trying to encode the values with any of the
|
||||
encoding functions will fail. The encoder detects circular references
|
||||
and returns an error status.
|
||||
|
||||
|
||||
True, False and Null
|
||||
====================
|
||||
|
||||
These values are implemented as singletons, so each of these functions
|
||||
returns the same value each time.
|
||||
|
||||
.. cfunction:: json_t *json_true(void)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns a value of the type :const:`JSON_TRUE`, or *NULL* on
|
||||
error.
|
||||
Returns the JSON true value.
|
||||
|
||||
.. cfunction:: json_t *json_false(void)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns a value of the type :const:`JSON_FALSE`, or *NULL* on
|
||||
error.
|
||||
Returns the JSON false value.
|
||||
|
||||
.. cfunction:: json_t *json_null(void)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns a value of the type :const:`JSON_NULL`, or *NULL* on
|
||||
error.
|
||||
Returns the JSON null value.
|
||||
|
||||
|
||||
String
|
||||
@@ -183,13 +226,40 @@ String
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns a new value of the type :const:`JSON_STRING`, or *NULL* on
|
||||
error. *value* must be a valid UTF-8 encoded Unicode string.
|
||||
Returns a new JSON string, or *NULL* on error. *value* must be a
|
||||
valid UTF-8 encoded Unicode string.
|
||||
|
||||
.. cfunction:: const char *json_string_value(const json_t *json)
|
||||
.. cfunction:: json_t *json_string_nocheck(const char *value)
|
||||
|
||||
Returns the associated value of a :const:`JSON_STRING` value as a
|
||||
null terminated UTF-8 encoded string.
|
||||
.. refcounting:: new
|
||||
|
||||
Like :cfunc:`json_string`, but doesn't check that *value* is valid
|
||||
UTF-8. Use this function only if you are certain that this really
|
||||
is the case (e.g. you have already checked it by other means).
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
.. cfunction:: const char *json_string_value(const json_t *string)
|
||||
|
||||
Returns the associated value of *string* as a null terminated UTF-8
|
||||
encoded string, or *NULL* if *string* is not a JSON string.
|
||||
|
||||
.. cfunction:: int json_string_set(const json_t *string, const char *value)
|
||||
|
||||
Sets the associated value of *string* to *value*. *value* must be a
|
||||
valid UTF-8 encoded Unicode string. Returns 0 on success and -1 on
|
||||
error.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: int json_string_set_nocheck(const json_t *string, const char *value)
|
||||
|
||||
Like :cfunc:`json_string_set`, but doesn't check that *value* is
|
||||
valid UTF-8. Use this function only if you are certain that this
|
||||
really is the case (e.g. you have already checked it by other
|
||||
means).
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
|
||||
Number
|
||||
@@ -199,33 +269,46 @@ Number
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns a new value of the type :const:`JSON_INTEGER`, or *NULL* on
|
||||
error.
|
||||
Returns a new JSON integer, or *NULL* on error.
|
||||
|
||||
.. cfunction:: int json_integer_value(const json_t *json)
|
||||
.. cfunction:: int json_integer_value(const json_t *integer)
|
||||
|
||||
Returns the associated integer value of values of the type
|
||||
:const:`JSON_INTEGER`, or 0 for values of other types.
|
||||
Returns the associated value of *integer*, or 0 if *json* is not a
|
||||
JSON integer.
|
||||
|
||||
.. cfunction:: int json_integer_set(const json_t *integer, int value)
|
||||
|
||||
Sets the associated value of *integer* to *value*. Returns 0 on
|
||||
success and -1 if *integer* is not a JSON integer.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: json_t *json_real(double value)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns a new value of the type :const:`JSON_REAL`, or *NULL* on
|
||||
error.
|
||||
Returns a new JSON real, or *NULL* on error.
|
||||
|
||||
.. cfunction:: double json_real_value(const json_t *json)
|
||||
.. cfunction:: double json_real_value(const json_t *real)
|
||||
|
||||
Returns the associated real value of values of the type
|
||||
:const:`JSON_INTEGER`, or 0 for values of other types.
|
||||
Returns the associated value of *real*, or 0.0 if *real* is not a
|
||||
JSON real.
|
||||
|
||||
.. cfunction:: int json_real_set(const json_t *real, double value)
|
||||
|
||||
Sets the associated value of *real* to *value*. Returns 0 on
|
||||
success and -1 if *real* is not a JSON real.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
In addition to the functions above, there's a common query function
|
||||
for integers and reals:
|
||||
|
||||
.. cfunction:: double json_number_value(const json_t *json)
|
||||
|
||||
Returns the value of either ``JSON_INTEGER`` or ``JSON_REAL``, cast
|
||||
to double regardless of the actual type.
|
||||
Returns the associated value of the JSON integer or JSON real
|
||||
*json*, cast to double regardless of the actual type. If *json* is
|
||||
neither JSON real nor JSON integer, 0.0 is returned.
|
||||
|
||||
|
||||
Array
|
||||
@@ -237,33 +320,90 @@ A JSON array is an ordered collection of other JSON values.
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns a new value of the type :const:`JSON_ARRAY`, or *NULL* on
|
||||
error. Initially, the array is empty.
|
||||
Returns a new JSON array, or *NULL* on error. Initially, the array
|
||||
is empty.
|
||||
|
||||
.. cfunction:: unsigned int json_array_size(const json_t *array)
|
||||
|
||||
Returns the number of elements in *array*.
|
||||
Returns the number of elements in *array*, or 0 if *array* is NULL
|
||||
or not a JSON array.
|
||||
|
||||
.. cfunction:: json_t *json_array_get(const json_t *array, unsigned int index)
|
||||
|
||||
.. refcounting:: borrow
|
||||
|
||||
Returns the element in *array* at position *index*, or *NULL* if
|
||||
*index* is out of range. The valid range for *index* is from 0 to
|
||||
the return value of :cfunc:`json_array_size()` minus 1.
|
||||
Returns the element in *array* at position *index*. The valid range
|
||||
for *index* is from 0 to the return value of
|
||||
:cfunc:`json_array_size()` minus 1. If *array* is not a JSON array,
|
||||
if *array* is *NULL*, or if *index* is out of range, *NULL* is
|
||||
returned.
|
||||
|
||||
.. cfunction:: int json_array_set(json_t *array, unsigned int index, json_t *value)
|
||||
|
||||
Replaces the element in *array* at position *index* with *value*.
|
||||
Returns 0 on success, or -1 if *index* is out of range. The valid
|
||||
range for *index* is from 0 to the return value of
|
||||
:cfunc:`json_array_size()` minus 1.
|
||||
The valid range for *index* is from 0 to the return value of
|
||||
:cfunc:`json_array_size()` minus 1. Returns 0 on success and -1 on
|
||||
error.
|
||||
|
||||
.. cfunction:: int json_array_set_new(json_t *array, unsigned int index, json_t *value)
|
||||
|
||||
Like :cfunc:`json_array_set()` but steals the reference to *value*.
|
||||
This is useful when *value* is newly created and not used after
|
||||
the call.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: int json_array_append(json_t *array, json_t *value)
|
||||
|
||||
Appends *value* to the end of *array*, growing the size of *array*
|
||||
by 1. Returns 0 on success and -1 on error.
|
||||
|
||||
.. cfunction:: int json_array_append_new(json_t *array, json_t *value)
|
||||
|
||||
Like :cfunc:`json_array_append()` but steals the reference to
|
||||
*value*. This is useful when *value* is newly created and not used
|
||||
after the call.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: int json_array_insert(json_t *array, unsigned int index, json_t *value)
|
||||
|
||||
Inserts *value* to *array* at position *index*, shifting the
|
||||
elements at *index* and after it one position towards the end of
|
||||
the array. Returns 0 on success and -1 on error.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: int json_array_insert_new(json_t *array, unsigned int index, json_t *value)
|
||||
|
||||
Like :cfunc:`json_array_insert()` but steals the reference to
|
||||
*value*. This is useful when *value* is newly created and not used
|
||||
after the call.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: int json_array_remove(json_t *array, unsigned int index)
|
||||
|
||||
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.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: int json_array_clear(json_t *array)
|
||||
|
||||
Removes all elements from *array*. Returns 0 on sucess and -1 on
|
||||
error.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: int json_array_extend(json_t *array, json_t *other_array)
|
||||
|
||||
Appends all elements in *other_array* to the end of *array*.
|
||||
Returns 0 on success and -1 on error.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
|
||||
Object
|
||||
======
|
||||
@@ -275,8 +415,15 @@ Unicode string and the value is any JSON value.
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns a new value of the type :const:`JSON_OBJECT`, or *NULL* on
|
||||
error. Initially, the object is empty.
|
||||
Returns a new JSON object, or *NULL* on error. Initially, the
|
||||
object is empty.
|
||||
|
||||
.. cfunction:: unsigned int json_object_size(const json_t *object)
|
||||
|
||||
Returns the number of elements in *object*, or 0 if *object* is not
|
||||
a JSON object.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: json_t *json_object_get(const json_t *object, const char *key)
|
||||
|
||||
@@ -288,9 +435,35 @@ Unicode string and the value is any JSON value.
|
||||
.. cfunction:: int json_object_set(json_t *object, const char *key, json_t *value)
|
||||
|
||||
Set the value of *key* to *value* in *object*. *key* must be a
|
||||
valid terminated UTF-8 encoded Unicode string. If there already is
|
||||
a value for *key*, it is replaced by the new value. Returns 0 on
|
||||
success and -1 on error.
|
||||
valid null terminated UTF-8 encoded Unicode string. If there
|
||||
already is a value for *key*, it is replaced by the new value.
|
||||
Returns 0 on success and -1 on error.
|
||||
|
||||
.. cfunction:: int json_object_set_nocheck(json_t *object, const char *key, json_t *value)
|
||||
|
||||
Like :cfunc:`json_object_set`, but doesn't check that *key* is
|
||||
valid UTF-8. Use this function only if you are certain that this
|
||||
really is the case (e.g. you have already checked it by other
|
||||
means).
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
.. cfunction:: int json_object_set_new(json_t *object, const char *key, json_t *value)
|
||||
|
||||
Like :cfunc:`json_object_set()` but steals the reference to
|
||||
*value*. This is useful when *value* is newly created and not used
|
||||
after the call.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value)
|
||||
|
||||
Like :cfunc:`json_object_set_new`, but doesn't check that *key* is
|
||||
valid UTF-8. Use this function only if you are certain that this
|
||||
really is the case (e.g. you have already checked it by other
|
||||
means).
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
.. cfunction:: int json_object_del(json_t *object, const char *key)
|
||||
|
||||
@@ -298,6 +471,21 @@ Unicode string and the value is any JSON value.
|
||||
-1 if *key* was not found.
|
||||
|
||||
|
||||
.. cfunction:: 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.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. cfunction:: int json_object_update(json_t *object, json_t *other)
|
||||
|
||||
Update *object* with the key-value pairs from *other*, overwriting
|
||||
existing keys. Returns 0 on success or -1 on error.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
|
||||
The following functions implement an iteration protocol for objects:
|
||||
|
||||
.. cfunction:: void *json_object_iter(json_t *object)
|
||||
@@ -321,6 +509,20 @@ The following functions implement an iteration protocol for objects:
|
||||
|
||||
Extract the associated value from *iter*.
|
||||
|
||||
The iteration protocol can be used for example as follows::
|
||||
|
||||
/* obj is a JSON object */
|
||||
const char *key;
|
||||
json_t *value;
|
||||
void *iter = json_object_iter(obj);
|
||||
while(iter)
|
||||
{
|
||||
key = json_object_iter_key(iter);
|
||||
value = json_object_iter_value(iter);
|
||||
/* use key and value ... */
|
||||
iter = json_object_iter_next(obj, iter);
|
||||
}
|
||||
|
||||
|
||||
Encoding
|
||||
========
|
||||
@@ -329,32 +531,63 @@ 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.
|
||||
|
||||
By default, the output has no newlines, and spaces are used between
|
||||
array and object elements for a readable output. This behavior can be
|
||||
altered by using the ``JSON_INDENT`` and ``JSON_COMPACT`` flags
|
||||
described below. A newline is never appended to the end of the encoded
|
||||
JSON data.
|
||||
|
||||
Each function takes a *flags* parameter that controls some aspects of
|
||||
how the data is encoded. Its default value is 0. The following macros
|
||||
can be ORed together to obtain *flags*.
|
||||
|
||||
``JSON_INDENT(n)``
|
||||
Pretty-print the result, indenting arrays and objects by *n*
|
||||
spaces. The valid range for *n* is between 0 and 255, other values
|
||||
result in an undefined output. If ``JSON_INDENT`` is not used or
|
||||
*n* is 0, no pretty-printing is done and the result is a compact
|
||||
representation.
|
||||
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 255, 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
|
||||
between array and object items to ``","`` and between object keys
|
||||
and values to ``":"``. Without this flag, the corresponding
|
||||
separators are ``", "`` and ``": "`` for more readable output.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
``JSON_ENSURE_ASCII``
|
||||
If this flag is used, the output is guaranteed to consist only of
|
||||
ASCII characters. This is achived by escaping all Unicode
|
||||
characters outside the ASCII range.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
``JSON_SORT_KEYS``
|
||||
If this flag is used, all the objects in output are sorted by key.
|
||||
This is useful e.g. if two JSON texts are diffed or visually
|
||||
compared.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
The following functions perform the actual JSON encoding. The result
|
||||
is in UTF-8.
|
||||
|
||||
.. cfunction:: char *json_dumps(const json_t *root, uint32_t flags)
|
||||
.. cfunction:: char *json_dumps(const json_t *root, unsigned long flags)
|
||||
|
||||
Returns the JSON representation of *root* as a string, or *NULL* on
|
||||
error. *flags* is described above. The return value must be freed
|
||||
by the caller using :cfunc:`free()`.
|
||||
|
||||
.. cfunction:: int json_dumpf(const json_t *root, FILE *output, uint32_t flags)
|
||||
.. cfunction:: int json_dumpf(const json_t *root, FILE *output, unsigned long flags)
|
||||
|
||||
Write the JSON representation of *root* to the stream *output*.
|
||||
*flags* is described above. Returns 0 on success and -1 on error.
|
||||
If an error occurs, something may have already been written to
|
||||
*output*. In this case, the output is undefined and most likely not
|
||||
valid JSON.
|
||||
|
||||
.. cfunction:: int json_dump_file(const json_t *json, const char *path, uint32_t flags)
|
||||
.. cfunction:: int json_dump_file(const json_t *json, const char *path, unsigned long flags)
|
||||
|
||||
Write the JSON representation of *root* to the file *path*. If
|
||||
*path* already exists, it is overwritten. *flags* is described
|
||||
@@ -440,3 +673,75 @@ The following functions perform the actual JSON decoding.
|
||||
object it contains, or *NULL* on error, in which case *error* is
|
||||
filled with information about the error. See above for discussion
|
||||
on the *error* parameter.
|
||||
|
||||
|
||||
Equality
|
||||
========
|
||||
|
||||
Testing for equality of two JSON values cannot, in general, be
|
||||
achieved using the ``==`` operator. Equality in the terms of the
|
||||
``==`` operator states that the two :ctype:`json_t` pointers point to
|
||||
exactly the same JSON value. However, two JSON values can be equal not
|
||||
only if they are exactly the same value, but also if they have equal
|
||||
"contents":
|
||||
|
||||
* Two integer or real values are equal if their contained numeric
|
||||
values are equal. An integer value is never equal to a real value,
|
||||
though.
|
||||
|
||||
* Two strings are equal if their contained UTF-8 strings are equal.
|
||||
|
||||
* Two arrays are equal if they have the same number of elements and
|
||||
each element in the first array is equal to the corresponding
|
||||
element in the second array.
|
||||
|
||||
* Two objects are equal if they have exactly the same keys and the
|
||||
value for each key in the first object is equal to the value of the
|
||||
corresponding key in the second object.
|
||||
|
||||
* Two true, false or null values have no "contents", so they are equal
|
||||
if their types are equal. (Because these values are singletons,
|
||||
their equality can actually be tested with ``==``.)
|
||||
|
||||
The following function can be used to test whether two JSON values are
|
||||
equal.
|
||||
|
||||
.. cfunction:: int json_equal(json_t *value1, json_t *value2)
|
||||
|
||||
Returns 1 if *value1* and *value2* are equal, as defined above.
|
||||
Returns 0 if they are inequal or one or both of the pointers are
|
||||
*NULL*.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
|
||||
Copying
|
||||
=======
|
||||
|
||||
Because of reference counting, passing JSON values around doesn't
|
||||
require copying them. But sometimes a fresh copy of a JSON value is
|
||||
needed. For example, if you need to modify an array, but still want to
|
||||
use the original afterwards, you should take a copy of it first.
|
||||
|
||||
Jansson supports two kinds of copying: shallow and deep. There is a
|
||||
difference between these methods only for arrays and objects. Shallow
|
||||
copying only copies the first level value (array or object) and uses
|
||||
the same child values in the copied value. Deep copying makes a fresh
|
||||
copy of the child values, too. Moreover, all the child values are deep
|
||||
copied in a recursive fashion.
|
||||
|
||||
.. cfunction:: json_t *json_copy(json_t *value)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns a shallow copy of *value*, or *NULL* on error.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
.. cfunction:: json_t *json_deep_copy(json_t *value)
|
||||
|
||||
.. refcounting:: new
|
||||
|
||||
Returns a deep copy of *value*, or *NULL* on error.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
5
doc/changes.rst
Normal file
5
doc/changes.rst
Normal file
@@ -0,0 +1,5 @@
|
||||
******************
|
||||
Changes in Jansson
|
||||
******************
|
||||
|
||||
.. include:: ../CHANGES
|
||||
@@ -50,9 +50,9 @@ copyright = u'2009, Petri Lehtinen'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '1.0'
|
||||
version = '1.2'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '1.0.4'
|
||||
release = '1.2'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
@@ -69,10 +69,10 @@ release = '1.0.4'
|
||||
|
||||
# List of directories, relative to source directory, that shouldn't be searched
|
||||
# for source files.
|
||||
exclude_trees = ['.build']
|
||||
exclude_trees = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
default_role = 'cfunc'
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
108
doc/gettingstarted.rst
Normal file
108
doc/gettingstarted.rst
Normal file
@@ -0,0 +1,108 @@
|
||||
***************
|
||||
Getting Started
|
||||
***************
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
Compiling and Installing Jansson
|
||||
================================
|
||||
|
||||
The Jansson source is available at
|
||||
http://www.digip.org/jansson/releases/.
|
||||
|
||||
Unpack the source tarball and change to the source directory:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
bunzip2 -c jansson-|release|.tar.bz2 | tar xf -
|
||||
cd jansson-|release|
|
||||
|
||||
The source uses GNU Autotools (autoconf_, automake_, libtool_), so
|
||||
compiling and installing is extremely simple::
|
||||
|
||||
./configure
|
||||
make
|
||||
make check
|
||||
make install
|
||||
|
||||
To change the destination directory (``/usr/local`` by default), use
|
||||
the ``--prefix=DIR`` argument to ``./configure``. See ``./configure
|
||||
--help`` for the list of all possible installation options. (There are
|
||||
no options to customize the resulting Jansson binary.)
|
||||
|
||||
The command ``make check`` runs the test suite distributed with
|
||||
Jansson. This step is not strictly necessary, but it may find possible
|
||||
problems that Jansson has on your platform. If any problems are found,
|
||||
please report them.
|
||||
|
||||
If you obtained the source from a Git repository (or any other source
|
||||
control system), there's no ``./configure`` script as it's not kept in
|
||||
version control. To create the script, Autotools needs to be
|
||||
bootstrapped. There are many ways to do this, but the easiest one is
|
||||
to use ``autoreconf``::
|
||||
|
||||
autoreconf -vi
|
||||
|
||||
This command creates the ``./configure`` script, which can then be
|
||||
used as described above.
|
||||
|
||||
.. _autoconf: http://www.gnu.org/software/autoconf/
|
||||
.. _automake: http://www.gnu.org/software/automake/
|
||||
.. _libtool: http://www.gnu.org/software/libtool/
|
||||
|
||||
|
||||
Installing Prebuilt Binary Packages
|
||||
-----------------------------------
|
||||
|
||||
Binary ``.deb`` packages for Ubuntu are available in `this PPA`_ at
|
||||
Launchpad_. Follow the instructions in the PPA ("Technical details
|
||||
about this PPA" link) to take the PPA into use. Then install the -dev
|
||||
package::
|
||||
|
||||
sudo apt-get install libjansson-dev
|
||||
|
||||
.. _this PPA: http://launchpad.net/~petri/+archive/ppa
|
||||
.. _Launchpad: http://launchpad.net/
|
||||
|
||||
|
||||
Building the Documentation
|
||||
--------------------------
|
||||
|
||||
(This subsection describes how to build the HTML documentation you are
|
||||
currently reading, so it can be safely skipped.)
|
||||
|
||||
Documentation is in the ``doc/`` subdirectory. It's written in
|
||||
reStructuredText_ with Sphinx_ annotations. To generate the HTML
|
||||
documentation, invoke::
|
||||
|
||||
make html
|
||||
|
||||
and point your browser to ``doc/_build/html/index.html``. Sphinx_ is
|
||||
required to generate the documentation.
|
||||
|
||||
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
|
||||
.. _Sphinx: http://sphinx.pocoo.org/
|
||||
|
||||
|
||||
Compiling Programs That Use Jansson
|
||||
===================================
|
||||
|
||||
Jansson involves one C header file, :file:`jansson.h`, so it's enough
|
||||
to put the line
|
||||
|
||||
::
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
in the beginning of every source file that uses Jansson.
|
||||
|
||||
There's also just one library to link with, ``libjansson``. Compile and
|
||||
link the program as follows::
|
||||
|
||||
cc -o prog prog.c -ljansson
|
||||
|
||||
Starting from version 1.2, there's also support for pkg-config_::
|
||||
|
||||
cc -o prog prog.c `pkg-config --cflags --libs jansson`
|
||||
|
||||
.. _pkg-config: http://pkg-config.freedesktop.org/
|
||||
171
doc/github_commits.c
Normal file
171
doc/github_commits.c
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (c) 2009 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <jansson.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#define BUFFER_SIZE (256 * 1024) /* 256 KB */
|
||||
|
||||
#define URL_FORMAT "http://github.com/api/v2/json/commits/list/%s/%s/master"
|
||||
#define URL_SIZE 256
|
||||
|
||||
/* Return the offset of the first newline in text or the length of
|
||||
text if there's no newline */
|
||||
static int newline_offset(const char *text)
|
||||
{
|
||||
const char *newline = strchr(text, '\n');
|
||||
if(!newline)
|
||||
return strlen(text);
|
||||
else
|
||||
return (int)(newline - text);
|
||||
}
|
||||
|
||||
struct write_result
|
||||
{
|
||||
char *data;
|
||||
int pos;
|
||||
};
|
||||
|
||||
static size_t write_response(void *ptr, size_t size, size_t nmemb, void *stream)
|
||||
{
|
||||
struct write_result *result = (struct write_result *)stream;
|
||||
|
||||
if(result->pos + size * nmemb >= BUFFER_SIZE - 1)
|
||||
{
|
||||
fprintf(stderr, "error: too small buffer\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(result->data + result->pos, ptr, size * nmemb);
|
||||
result->pos += size * nmemb;
|
||||
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
static char *request(const char *url)
|
||||
{
|
||||
CURL *curl;
|
||||
CURLcode status;
|
||||
char *data;
|
||||
long code;
|
||||
|
||||
curl = curl_easy_init();
|
||||
data = malloc(BUFFER_SIZE);
|
||||
if(!curl || !data)
|
||||
return NULL;
|
||||
|
||||
struct write_result write_result = {
|
||||
.data = data,
|
||||
.pos = 0
|
||||
};
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_response);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_result);
|
||||
|
||||
status = curl_easy_perform(curl);
|
||||
if(status != 0)
|
||||
{
|
||||
fprintf(stderr, "error: unable to request data from %s:\n", url);
|
||||
fprintf(stderr, "%s\n", curl_easy_strerror(status));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
|
||||
if(code != 200)
|
||||
{
|
||||
fprintf(stderr, "error: server responded with code %ld\n", code);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
curl_global_cleanup();
|
||||
|
||||
/* zero-terminate the result */
|
||||
data[write_result.pos] = '\0';
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
char *text;
|
||||
char url[URL_SIZE];
|
||||
|
||||
json_t *root;
|
||||
json_error_t error;
|
||||
json_t *commits;
|
||||
|
||||
if(argc != 3)
|
||||
{
|
||||
fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]);
|
||||
fprintf(stderr, "List commits at USER's REPOSITORY.\n\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
snprintf(url, URL_SIZE, URL_FORMAT, argv[1], argv[2]);
|
||||
|
||||
text = request(url);
|
||||
if(!text)
|
||||
return 1;
|
||||
|
||||
root = json_loads(text, &error);
|
||||
free(text);
|
||||
|
||||
if(!root)
|
||||
{
|
||||
fprintf(stderr, "error: on line %d: %s\n", error.line, error.text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
commits = json_object_get(root, "commits");
|
||||
if(!json_is_array(commits))
|
||||
{
|
||||
fprintf(stderr, "error: commits is not an array\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for(i = 0; i < json_array_size(commits); i++)
|
||||
{
|
||||
json_t *commit, *id, *message;
|
||||
const char *message_text;
|
||||
|
||||
commit = json_array_get(commits, i);
|
||||
if(!json_is_object(commit))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d is not an object\n", i + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
id = json_object_get(commit, "id");
|
||||
if(!json_is_string(id))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: id is not a string\n", i + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
message = json_object_get(commit, "message");
|
||||
if(!json_is_string(message))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: message is not a string\n", i + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
message_text = json_string_value(message);
|
||||
printf("%.8s %.*s\n",
|
||||
json_string_value(id),
|
||||
newline_offset(message_text),
|
||||
message_text);
|
||||
}
|
||||
|
||||
json_decref(root);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,16 +1,41 @@
|
||||
Overview
|
||||
========
|
||||
Jansson Documentation
|
||||
=====================
|
||||
|
||||
This is the documentation for Jansson_ |release|, last updated |today|.
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Jansson_ is a C library for encoding, decoding and manipulating JSON
|
||||
data. Its main features and design principles are:
|
||||
|
||||
- Simple and intuitive API and data model
|
||||
|
||||
- Comprehensive documentation
|
||||
|
||||
- No dependencies on other libraries
|
||||
|
||||
- Full Unicode support (UTF-8)
|
||||
|
||||
- Extensive test suite
|
||||
|
||||
Jansson is licensed under the `MIT license`_; see LICENSE in the
|
||||
source distribution for details.
|
||||
|
||||
|
||||
.. _`MIT license`: http://www.opensource.org/licenses/mit-license.php
|
||||
.. _Jansson: http://www.digip.org/jansson/
|
||||
|
||||
**Contents:**
|
||||
Contents
|
||||
--------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
gettingstarted
|
||||
tutorial
|
||||
apiref
|
||||
changes
|
||||
|
||||
|
||||
Indices and Tables
|
||||
|
||||
275
doc/tutorial.rst
Normal file
275
doc/tutorial.rst
Normal file
@@ -0,0 +1,275 @@
|
||||
.. _tutorial:
|
||||
|
||||
********
|
||||
Tutorial
|
||||
********
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
In this tutorial, we create a program that fetches the latest commits
|
||||
of a repository in GitHub_ over the web. One of the response formats
|
||||
supported by `GitHub API`_ is JSON, so the result can be parsed using
|
||||
Jansson.
|
||||
|
||||
To stick to the the scope of this tutorial, we will only cover the the
|
||||
parts of the program related to handling JSON data. For the best user
|
||||
experience, the full source code is available:
|
||||
:download:`github_commits.c`. To compile it (on Unix-like systems with
|
||||
gcc), use the following command::
|
||||
|
||||
gcc -o github_commits github_commits.c -ljansson -lcurl
|
||||
|
||||
libcurl_ is used to communicate over the web, so it is required to
|
||||
compile the program.
|
||||
|
||||
The command line syntax is::
|
||||
|
||||
github_commits USER REPOSITORY
|
||||
|
||||
``USER`` is a GitHub user ID and ``REPOSITORY`` is the repository
|
||||
name. Please note that the GitHub API is rate limited, so if you run
|
||||
the program too many times within a short period of time, the sever
|
||||
starts to respond with an error.
|
||||
|
||||
.. _GitHub: http://github.com/
|
||||
.. _GitHub API: http://develop.github.com/
|
||||
.. _libcurl: http://curl.haxx.se/
|
||||
|
||||
|
||||
.. _tutorial-github-commits-api:
|
||||
|
||||
The GitHub Commits API
|
||||
======================
|
||||
|
||||
The `GitHub commits API`_ is used by sending HTTP requests to URLs
|
||||
starting with ``http://github.com/api/v2/json/commits/``. Our program
|
||||
only lists the latest commits, so the rest of the URL is
|
||||
``list/USER/REPOSITORY/BRANCH``, where ``USER``, ``REPOSITORY`` and
|
||||
``BRANCH`` are the GitHub user ID, the name of the repository, and the
|
||||
name of the branch whose commits are to be listed, respectively.
|
||||
|
||||
GitHub responds with a JSON object of the following form:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
{
|
||||
"commits": [
|
||||
{
|
||||
"id": "<the commit ID>",
|
||||
"message": "<the commit message>",
|
||||
<more fields, not important to this tutorial>
|
||||
},
|
||||
{
|
||||
"id": "<the commit ID>",
|
||||
"message": "<the commit message>",
|
||||
<more fields, not important to this tutorial>
|
||||
},
|
||||
<more commits...>
|
||||
]
|
||||
}
|
||||
|
||||
In our program, the HTTP request is sent using the following
|
||||
function::
|
||||
|
||||
static char *request(const char *url);
|
||||
|
||||
It takes the URL as a parameter, preforms a HTTP GET request, and
|
||||
returns a newly allocated string that contains the response body. If
|
||||
the request fails, an error message is printed to stderr and the
|
||||
return value is *NULL*. For full details, refer to :download:`the code
|
||||
<github_commits.c>`, as the actual implementation is not important
|
||||
here.
|
||||
|
||||
.. _GitHub commits API: http://develop.github.com/p/commits.html
|
||||
|
||||
.. _tutorial-the-program:
|
||||
|
||||
The Program
|
||||
===========
|
||||
|
||||
First the includes::
|
||||
|
||||
#include <string.h>
|
||||
#include <jansson.h>
|
||||
|
||||
Like all the programs using Jansson, we need to include
|
||||
:file:`jansson.h`.
|
||||
|
||||
The following definitions are used to build the GitHub commits API
|
||||
request URL::
|
||||
|
||||
#define URL_FORMAT "http://github.com/api/v2/json/commits/list/%s/%s/master"
|
||||
#define URL_SIZE 256
|
||||
|
||||
The following function is used when formatting the result to find the
|
||||
first newline in the commit message::
|
||||
|
||||
/* Return the offset of the first newline in text or the length of
|
||||
text if there's no newline */
|
||||
static int newline_offset(const char *text)
|
||||
{
|
||||
const char *newline = strchr(text, '\n');
|
||||
if(!newline)
|
||||
return strlen(text);
|
||||
else
|
||||
return (int)(newline - text);
|
||||
}
|
||||
|
||||
The main function follows. In the beginning, we first declare a bunch
|
||||
of variables and check the command line parameters::
|
||||
|
||||
unsigned int i;
|
||||
char *text;
|
||||
char url[URL_SIZE];
|
||||
|
||||
json_t *root;
|
||||
json_error_t error;
|
||||
json_t *commits;
|
||||
|
||||
if(argc != 3)
|
||||
{
|
||||
fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]);
|
||||
fprintf(stderr, "List commits at USER's REPOSITORY.\n\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
Then we build the request URL using the user and repository names
|
||||
given as command line parameters::
|
||||
|
||||
snprintf(url, URL_SIZE, URL_FORMAT, argv[1], argv[2]);
|
||||
|
||||
This uses the ``URL_SIZE`` and ``URL_FORMAT`` constants defined above.
|
||||
Now we're ready to actually request the JSON data over the web::
|
||||
|
||||
text = request(url);
|
||||
if(!text)
|
||||
return 1;
|
||||
|
||||
If an error occurs, our function ``request`` prints the error and
|
||||
returns *NULL*, so it's enough to just return 1 from the main
|
||||
function.
|
||||
|
||||
Next we'll call :cfunc:`json_loads()` to decode the JSON text we got
|
||||
as a response::
|
||||
|
||||
root = json_loads(text, &error);
|
||||
free(text);
|
||||
|
||||
if(!root)
|
||||
{
|
||||
fprintf(stderr, "error: on line %d: %s\n", error.line, error.text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
We don't need the JSON text anymore, so we can free the ``text``
|
||||
variable right after decoding it. If :cfunc:`json_loads()` fails, it
|
||||
returns *NULL* and sets error information to the :ctype:`json_error_t`
|
||||
structure given as the second parameter. In this case, our program
|
||||
prints the error information out and returns 1 from the main function.
|
||||
|
||||
Now we're ready to extract the data out of the decoded JSON response.
|
||||
The structure of the response JSON was explained in section
|
||||
:ref:`tutorial-github-commits-api`.
|
||||
|
||||
First, we'll extract the ``commits`` array from the JSON response::
|
||||
|
||||
commits = json_object_get(root, "commits");
|
||||
if(!json_is_array(commits))
|
||||
{
|
||||
fprintf(stderr, "error: commits is not an array\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
This is the array that contains objects describing latest commits in
|
||||
the repository. We check that the returned value really is an array.
|
||||
If the key ``commits`` doesn't exist, :cfunc:`json_object_get()`
|
||||
returns *NULL*, but :cfunc:`json_is_array()` handles this case, too.
|
||||
|
||||
Then we proceed to loop over all the commits in the array::
|
||||
|
||||
for(i = 0; i < json_array_size(commits); i++)
|
||||
{
|
||||
json_t *commit, *id, *message;
|
||||
const char *message_text;
|
||||
|
||||
commit = json_array_get(commits, i);
|
||||
if(!json_is_object(commit))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d is not an object\n", i + 1);
|
||||
return 1;
|
||||
}
|
||||
...
|
||||
|
||||
The function :cfunc:`json_array_size()` returns the size of a JSON
|
||||
array. First, we again declare some variables and then extract the
|
||||
i'th element of the ``commits`` array using :cfunc:`json_array_get()`.
|
||||
We also check that the resulting value is a JSON object.
|
||||
|
||||
Next we'll extract the commit ID and commit message, and check that
|
||||
they both are JSON strings::
|
||||
|
||||
id = json_object_get(commit, "id");
|
||||
if(!json_is_string(id))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: id is not a string\n", i + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
message = json_object_get(commit, "message");
|
||||
if(!json_is_string(message))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: message is not a string\n", i + 1);
|
||||
return 1;
|
||||
}
|
||||
...
|
||||
|
||||
And finally, we'll print the first 8 characters of the commit ID and
|
||||
the first line of the commit message. A C-style string is extracted
|
||||
from a JSON string using :cfunc:`json_string_value()`::
|
||||
|
||||
message_text = json_string_value(message);
|
||||
printf("%.8s %.*s\n",
|
||||
json_string_value(id),
|
||||
newline_offset(message_text),
|
||||
message_text);
|
||||
}
|
||||
|
||||
After sending the HTTP request, we decoded the JSON text using
|
||||
:cfunc:`json_loads()`, remember? It returns a *new reference* to the
|
||||
JSON value it decodes. When we're finished with the value, we'll need
|
||||
to decrease the reference count using :cfunc:`json_decref()`. This way
|
||||
Jansson can release the resources::
|
||||
|
||||
json_decref(root);
|
||||
return 0;
|
||||
|
||||
For a detailed explanation of reference counting in Jansson, see
|
||||
:ref:`apiref-reference-count` in :ref:`apiref`.
|
||||
|
||||
The program's ready, let's test it and view the latest commits in
|
||||
Jansson's repository::
|
||||
|
||||
$ ./github_commits akheron jansson
|
||||
86dc1d62 Fix indentation
|
||||
b67e130f json_dumpf: Document the output shortage on error
|
||||
4cd77771 Enhance handling of circular references
|
||||
79009e62 json_dumps: Close the strbuffer if dumping fails
|
||||
76999799 doc: Fix a small typo in apiref
|
||||
22af193a doc/Makefile.am: Remove *.pyc in clean
|
||||
951d091f Make integer, real and string mutable
|
||||
185e107d Don't use non-portable asprintf()
|
||||
ca7703fb Merge branch '1.0'
|
||||
12cd4e8c jansson 1.0.4
|
||||
<etc...>
|
||||
|
||||
|
||||
Conclusion
|
||||
==========
|
||||
|
||||
In this tutorial, we implemented a program that fetches the latest
|
||||
commits of a GitHub repository using the GitHub commits API. Jansson
|
||||
was used to decode the JSON response and to extract the commit data.
|
||||
|
||||
This tutorial only covered a small part of Jansson. For example, we
|
||||
did not create or manipulate JSON values at all. Proceed to
|
||||
:ref:`apiref` to explore all features of Jansson.
|
||||
10
jansson.pc.in
Normal file
10
jansson.pc.in
Normal file
@@ -0,0 +1,10 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=${prefix}/include
|
||||
|
||||
Name: Jansson
|
||||
Description: Library for encoding, decoding and manipulating JSON data
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -ljansson
|
||||
Cflags: -I${includedir}
|
||||
@@ -13,6 +13,8 @@ libjansson_la_SOURCES = \
|
||||
utf.h \
|
||||
util.h \
|
||||
value.c
|
||||
libjansson_la_LDFLAGS = -version-info 0:4:0
|
||||
libjansson_la_LDFLAGS = \
|
||||
-export-symbols-regex '^json_' \
|
||||
-version-info 2:0:2
|
||||
|
||||
AM_CFLAGS = -Wall -Wextra -Werror -std=c99
|
||||
AM_CFLAGS = -Wall -Wextra -Werror
|
||||
|
||||
296
src/dump.c
296
src/dump.c
@@ -9,10 +9,16 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <jansson.h>
|
||||
#include "jansson_private.h"
|
||||
#include "strbuffer.h"
|
||||
#include "utf.h"
|
||||
|
||||
#define MAX_INTEGER_STR_LENGTH 100
|
||||
#define MAX_REAL_STR_LENGTH 100
|
||||
|
||||
typedef int (*dump_func)(const char *buffer, int size, void *data);
|
||||
|
||||
@@ -36,54 +42,74 @@ static int dump_to_file(const char *buffer, int size, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_indent(uint32_t flags, int depth, dump_func dump, void *data)
|
||||
/* 256 spaces (the maximum indentation size) */
|
||||
static char whitespace[] = " ";
|
||||
|
||||
static int dump_indent(unsigned long flags, int depth, int space, dump_func dump, void *data)
|
||||
{
|
||||
if(JSON_INDENT(flags) > 0)
|
||||
{
|
||||
char *ws_buffer;
|
||||
int ws_count = JSON_INDENT(flags) * depth;
|
||||
int i, ws_count = JSON_INDENT(flags);
|
||||
|
||||
if(dump("\n", 1, data))
|
||||
return -1;
|
||||
|
||||
if(ws_count == 0)
|
||||
return 0;
|
||||
|
||||
ws_buffer = alloca(ws_count);
|
||||
memset(ws_buffer, ' ', ws_count);
|
||||
return dump(ws_buffer, ws_count, data);
|
||||
for(i = 0; i < depth; i++)
|
||||
{
|
||||
if(dump(whitespace, ws_count, data))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if(space && !(flags & JSON_COMPACT))
|
||||
{
|
||||
return dump(" ", 1, data);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_string(const char *str, dump_func dump, void *data)
|
||||
static int dump_string(const char *str, int ascii, dump_func dump, void *data)
|
||||
{
|
||||
const char *end;
|
||||
const char *pos, *end;
|
||||
int32_t codepoint;
|
||||
|
||||
if(dump("\"", 1, data))
|
||||
return -1;
|
||||
|
||||
end = str;
|
||||
end = pos = str;
|
||||
while(1)
|
||||
{
|
||||
const char *text;
|
||||
char seq[7];
|
||||
char seq[13];
|
||||
int length;
|
||||
|
||||
while(*end && *end != '\\' && *end != '"' && (unsigned char)*end > 0x1F)
|
||||
end++;
|
||||
while(*end)
|
||||
{
|
||||
end = utf8_iterate(pos, &codepoint);
|
||||
if(!end)
|
||||
return -1;
|
||||
|
||||
if(end != str) {
|
||||
if(dump(str, end - str, data))
|
||||
/* mandatory escape or control char */
|
||||
if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
|
||||
break;
|
||||
|
||||
/* non-ASCII */
|
||||
if(ascii && codepoint > 0x7F)
|
||||
break;
|
||||
|
||||
pos = end;
|
||||
}
|
||||
|
||||
if(pos != str) {
|
||||
if(dump(str, pos - str, data))
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!*end)
|
||||
if(end == pos)
|
||||
break;
|
||||
|
||||
/* handle \, ", and control codes */
|
||||
length = 2;
|
||||
switch(*end)
|
||||
switch(codepoint)
|
||||
{
|
||||
case '\\': text = "\\\\"; break;
|
||||
case '\"': text = "\\\""; break;
|
||||
@@ -94,9 +120,27 @@ static int dump_string(const char *str, dump_func dump, void *data)
|
||||
case '\t': text = "\\t"; break;
|
||||
default:
|
||||
{
|
||||
sprintf(seq, "\\u00%02x", *end);
|
||||
/* codepoint is in BMP */
|
||||
if(codepoint < 0x10000)
|
||||
{
|
||||
sprintf(seq, "\\u%04x", codepoint);
|
||||
length = 6;
|
||||
}
|
||||
|
||||
/* not in BMP -> construct a UTF-16 surrogate pair */
|
||||
else
|
||||
{
|
||||
int32_t first, last;
|
||||
|
||||
codepoint -= 0x10000;
|
||||
first = 0xD800 | ((codepoint & 0xffc00) >> 10);
|
||||
last = 0xDC00 | (codepoint & 0x003ff);
|
||||
|
||||
sprintf(seq, "\\u%04x\\u%04x", first, last);
|
||||
length = 12;
|
||||
}
|
||||
|
||||
text = seq;
|
||||
length = 6;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -104,16 +148,22 @@ static int dump_string(const char *str, dump_func dump, void *data)
|
||||
if(dump(text, length, data))
|
||||
return -1;
|
||||
|
||||
end++;
|
||||
str = end;
|
||||
str = pos = end;
|
||||
}
|
||||
|
||||
return dump("\"", 1, data);
|
||||
}
|
||||
|
||||
static int do_dump(const json_t *json, uint32_t flags, int depth,
|
||||
static int object_key_cmp(const void *key1, const void *key2)
|
||||
{
|
||||
return strcmp(*(const char **)key1, *(const char **)key2);
|
||||
}
|
||||
|
||||
static int do_dump(const json_t *json, unsigned long flags, int depth,
|
||||
dump_func dump, void *data)
|
||||
{
|
||||
int ascii = flags & JSON_ENSURE_ASCII ? 1 : 0;
|
||||
|
||||
switch(json_typeof(json)) {
|
||||
case JSON_NULL:
|
||||
return dump("null", 4, data);
|
||||
@@ -126,45 +176,65 @@ static int do_dump(const json_t *json, uint32_t flags, int depth,
|
||||
|
||||
case JSON_INTEGER:
|
||||
{
|
||||
char *buffer;
|
||||
int size, ret;
|
||||
char buffer[MAX_INTEGER_STR_LENGTH];
|
||||
int size;
|
||||
|
||||
size = asprintf(&buffer, "%d", json_integer_value(json));
|
||||
if(size == -1)
|
||||
size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, "%d", json_integer_value(json));
|
||||
if(size >= MAX_INTEGER_STR_LENGTH)
|
||||
return -1;
|
||||
|
||||
ret = dump(buffer, size, data);
|
||||
free(buffer);
|
||||
return ret;
|
||||
return dump(buffer, size, data);
|
||||
}
|
||||
|
||||
case JSON_REAL:
|
||||
{
|
||||
char *buffer;
|
||||
int size, ret;
|
||||
char buffer[MAX_REAL_STR_LENGTH];
|
||||
int size;
|
||||
|
||||
size = asprintf(&buffer, "%.17f", json_real_value(json));
|
||||
if(size == -1)
|
||||
size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%.17g",
|
||||
json_real_value(json));
|
||||
if(size >= MAX_REAL_STR_LENGTH)
|
||||
return -1;
|
||||
|
||||
ret = dump(buffer, size, data);
|
||||
free(buffer);
|
||||
return ret;
|
||||
/* Make sure there's a dot or 'e' in the output. Otherwise
|
||||
a real is converted to an integer when decoding */
|
||||
if(strchr(buffer, '.') == NULL &&
|
||||
strchr(buffer, 'e') == NULL)
|
||||
{
|
||||
if(size + 2 >= MAX_REAL_STR_LENGTH) {
|
||||
/* No space to append ".0" */
|
||||
return -1;
|
||||
}
|
||||
buffer[size] = '.';
|
||||
buffer[size + 1] = '0';
|
||||
size += 2;
|
||||
}
|
||||
|
||||
return dump(buffer, size, data);
|
||||
}
|
||||
|
||||
case JSON_STRING:
|
||||
return dump_string(json_string_value(json), dump, data);
|
||||
return dump_string(json_string_value(json), ascii, dump, data);
|
||||
|
||||
case JSON_ARRAY:
|
||||
{
|
||||
int i;
|
||||
int n = json_array_size(json);
|
||||
int n;
|
||||
json_array_t *array;
|
||||
|
||||
/* detect circular references */
|
||||
array = json_to_array(json);
|
||||
if(array->visited)
|
||||
return -1;
|
||||
array->visited = 1;
|
||||
|
||||
n = json_array_size(json);
|
||||
|
||||
if(dump("[", 1, data))
|
||||
return -1;
|
||||
if(n == 0)
|
||||
return dump("]", 1, data);
|
||||
if(dump_indent(flags, depth + 1, dump, data))
|
||||
if(dump_indent(flags, depth + 1, 0, dump, data))
|
||||
return -1;
|
||||
|
||||
for(i = 0; i < n; ++i) {
|
||||
@@ -175,53 +245,144 @@ static int do_dump(const json_t *json, uint32_t flags, int depth,
|
||||
if(i < n - 1)
|
||||
{
|
||||
if(dump(",", 1, data) ||
|
||||
dump_indent(flags, depth + 1, dump, data))
|
||||
dump_indent(flags, depth + 1, 1, dump, data))
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(dump_indent(flags, depth, dump, data))
|
||||
if(dump_indent(flags, depth, 0, dump, data))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
array->visited = 0;
|
||||
return dump("]", 1, data);
|
||||
}
|
||||
|
||||
case JSON_OBJECT:
|
||||
{
|
||||
void *iter = json_object_iter((json_t *)json);
|
||||
json_object_t *object;
|
||||
void *iter;
|
||||
const char *separator;
|
||||
int separator_length;
|
||||
|
||||
if(flags & JSON_COMPACT) {
|
||||
separator = ":";
|
||||
separator_length = 1;
|
||||
}
|
||||
else {
|
||||
separator = ": ";
|
||||
separator_length = 2;
|
||||
}
|
||||
|
||||
/* detect circular references */
|
||||
object = json_to_object(json);
|
||||
if(object->visited)
|
||||
return -1;
|
||||
object->visited = 1;
|
||||
|
||||
iter = json_object_iter((json_t *)json);
|
||||
|
||||
if(dump("{", 1, data))
|
||||
return -1;
|
||||
if(!iter)
|
||||
return dump("}", 1, data);
|
||||
if(dump_indent(flags, depth + 1, dump, data))
|
||||
if(dump_indent(flags, depth + 1, 0, dump, data))
|
||||
return -1;
|
||||
|
||||
while(iter)
|
||||
if(flags & JSON_SORT_KEYS)
|
||||
{
|
||||
void *next = json_object_iter_next((json_t *)json, iter);
|
||||
/* Sort keys */
|
||||
|
||||
dump_string(json_object_iter_key(iter), dump, data);
|
||||
if(dump(": ", 2, data) ||
|
||||
do_dump(json_object_iter_value(iter), flags, depth + 1,
|
||||
dump, data))
|
||||
const char **keys;
|
||||
unsigned int size;
|
||||
unsigned int i;
|
||||
|
||||
size = json_object_size(json);
|
||||
keys = malloc(size * sizeof(const char *));
|
||||
if(!keys)
|
||||
return -1;
|
||||
|
||||
if(next)
|
||||
i = 0;
|
||||
while(iter)
|
||||
{
|
||||
if(dump(",", 1, data) ||
|
||||
dump_indent(flags, depth + 1, dump, data))
|
||||
return -1;
|
||||
keys[i] = json_object_iter_key(iter);
|
||||
iter = json_object_iter_next((json_t *)json, iter);
|
||||
i++;
|
||||
}
|
||||
else
|
||||
assert(i == size);
|
||||
|
||||
qsort(keys, size, sizeof(const char *), object_key_cmp);
|
||||
|
||||
for(i = 0; i < size; i++)
|
||||
{
|
||||
if(dump_indent(flags, depth, dump, data))
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
key = keys[i];
|
||||
value = json_object_get(json, key);
|
||||
assert(value);
|
||||
|
||||
dump_string(key, ascii, dump, data);
|
||||
if(dump(separator, separator_length, data) ||
|
||||
do_dump(value, flags, depth + 1, dump, data))
|
||||
{
|
||||
free(keys);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(i < size - 1)
|
||||
{
|
||||
if(dump(",", 1, data) ||
|
||||
dump_indent(flags, depth + 1, 1, dump, data))
|
||||
{
|
||||
free(keys);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(dump_indent(flags, depth, 0, dump, data))
|
||||
{
|
||||
free(keys);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iter = next;
|
||||
free(keys);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Don't sort keys */
|
||||
|
||||
while(iter)
|
||||
{
|
||||
void *next = json_object_iter_next((json_t *)json, iter);
|
||||
|
||||
dump_string(json_object_iter_key(iter), ascii, dump, data);
|
||||
if(dump(separator, separator_length, data) ||
|
||||
do_dump(json_object_iter_value(iter), flags, depth + 1,
|
||||
dump, data))
|
||||
return -1;
|
||||
|
||||
if(next)
|
||||
{
|
||||
if(dump(",", 1, data) ||
|
||||
dump_indent(flags, depth + 1, 1, dump, data))
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(dump_indent(flags, depth, 0, dump, data))
|
||||
return -1;
|
||||
}
|
||||
|
||||
iter = next;
|
||||
}
|
||||
}
|
||||
|
||||
object->visited = 0;
|
||||
return dump("}", 1, data);
|
||||
}
|
||||
|
||||
@@ -232,7 +393,7 @@ static int do_dump(const json_t *json, uint32_t flags, int depth,
|
||||
}
|
||||
|
||||
|
||||
char *json_dumps(const json_t *json, uint32_t flags)
|
||||
char *json_dumps(const json_t *json, unsigned long flags)
|
||||
{
|
||||
strbuffer_t strbuff;
|
||||
char *result;
|
||||
@@ -241,13 +402,12 @@ char *json_dumps(const json_t *json, uint32_t flags)
|
||||
return NULL;
|
||||
|
||||
if(strbuffer_init(&strbuff))
|
||||
return NULL;
|
||||
|
||||
if(do_dump(json, flags, 0, dump_to_strbuffer, (void *)&strbuff))
|
||||
return NULL;
|
||||
|
||||
if(dump_to_strbuffer("\n", 1, (void *)&strbuff))
|
||||
if(do_dump(json, flags, 0, dump_to_strbuffer, (void *)&strbuff)) {
|
||||
strbuffer_close(&strbuff);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = strdup(strbuffer_value(&strbuff));
|
||||
strbuffer_close(&strbuff);
|
||||
@@ -255,17 +415,15 @@ char *json_dumps(const json_t *json, uint32_t flags)
|
||||
return result;
|
||||
}
|
||||
|
||||
int json_dumpf(const json_t *json, FILE *output, uint32_t flags)
|
||||
int json_dumpf(const json_t *json, FILE *output, unsigned long flags)
|
||||
{
|
||||
if(!json_is_array(json) && !json_is_object(json))
|
||||
return -1;
|
||||
|
||||
if(do_dump(json, flags, 0, dump_to_file, (void *)output))
|
||||
return -1;
|
||||
return dump_to_file("\n", 1, (void *)output);
|
||||
return do_dump(json, flags, 0, dump_to_file, (void *)output);
|
||||
}
|
||||
|
||||
int json_dump_file(const json_t *json, const char *path, uint32_t flags)
|
||||
int json_dump_file(const json_t *json, const char *path, unsigned long flags)
|
||||
{
|
||||
int result;
|
||||
|
||||
|
||||
@@ -133,6 +133,23 @@ static int hashtable_do_del(hashtable_t *hashtable,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hashtable_do_clear(hashtable_t *hashtable)
|
||||
{
|
||||
list_t *list, *next;
|
||||
pair_t *pair;
|
||||
|
||||
for(list = hashtable->list.next; list != &hashtable->list; list = next)
|
||||
{
|
||||
next = list->next;
|
||||
pair = list_to_pair(list);
|
||||
if(hashtable->free_key)
|
||||
hashtable->free_key(pair->key);
|
||||
if(hashtable->free_value)
|
||||
hashtable->free_value(pair->value);
|
||||
free(pair);
|
||||
}
|
||||
}
|
||||
|
||||
static int hashtable_do_rehash(hashtable_t *hashtable)
|
||||
{
|
||||
list_t *list, *next;
|
||||
@@ -220,19 +237,7 @@ int hashtable_init(hashtable_t *hashtable,
|
||||
|
||||
void hashtable_close(hashtable_t *hashtable)
|
||||
{
|
||||
list_t *list, *next;
|
||||
pair_t *pair;
|
||||
for(list = hashtable->list.next; list != &hashtable->list; list = next)
|
||||
{
|
||||
next = list->next;
|
||||
pair = list_to_pair(list);
|
||||
if(hashtable->free_key)
|
||||
hashtable->free_key(pair->key);
|
||||
if(hashtable->free_value)
|
||||
hashtable->free_value(pair->value);
|
||||
free(pair);
|
||||
}
|
||||
|
||||
hashtable_do_clear(hashtable);
|
||||
free(hashtable->buckets);
|
||||
}
|
||||
|
||||
@@ -292,6 +297,22 @@ int hashtable_del(hashtable_t *hashtable, const void *key)
|
||||
return hashtable_do_del(hashtable, key, hash);
|
||||
}
|
||||
|
||||
void hashtable_clear(hashtable_t *hashtable)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
hashtable_do_clear(hashtable);
|
||||
|
||||
for(i = 0; i < num_buckets(hashtable); i++)
|
||||
{
|
||||
hashtable->buckets[i].first = hashtable->buckets[i].last =
|
||||
&hashtable->list;
|
||||
}
|
||||
|
||||
list_init(&hashtable->list);
|
||||
hashtable->size = 0;
|
||||
}
|
||||
|
||||
void *hashtable_iter(hashtable_t *hashtable)
|
||||
{
|
||||
return hashtable_iter_next(hashtable, &hashtable->list);
|
||||
|
||||
@@ -13,8 +13,8 @@ typedef int (*key_cmp_fn)(const void *key1, const void *key2);
|
||||
typedef void (*free_fn)(void *key);
|
||||
|
||||
struct hashtable_list {
|
||||
struct hashtable_list *prev;
|
||||
struct hashtable_list *next;
|
||||
struct hashtable_list *prev;
|
||||
struct hashtable_list *next;
|
||||
};
|
||||
|
||||
struct hashtable_pair {
|
||||
@@ -134,6 +134,15 @@ void *hashtable_get(hashtable_t *hashtable, const void *key);
|
||||
*/
|
||||
int hashtable_del(hashtable_t *hashtable, const void *key);
|
||||
|
||||
/**
|
||||
* hashtable_clear - Clear hashtable
|
||||
*
|
||||
* @hashtable: The hashtable object
|
||||
*
|
||||
* Removes all items from the hashtable.
|
||||
*/
|
||||
void hashtable_clear(hashtable_t *hashtable);
|
||||
|
||||
/**
|
||||
* hashtable_iter - Iterate over hashtable
|
||||
*
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
#define JANSSON_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* types */
|
||||
|
||||
@@ -46,6 +49,7 @@ typedef struct {
|
||||
json_t *json_object(void);
|
||||
json_t *json_array(void);
|
||||
json_t *json_string(const char *value);
|
||||
json_t *json_string_nocheck(const char *value);
|
||||
json_t *json_integer(int value);
|
||||
json_t *json_real(double value);
|
||||
json_t *json_true(void);
|
||||
@@ -54,7 +58,7 @@ json_t *json_null(void);
|
||||
|
||||
static inline json_t *json_incref(json_t *json)
|
||||
{
|
||||
if(json)
|
||||
if(json && json->refcount != (unsigned int)-1)
|
||||
++json->refcount;
|
||||
return json;
|
||||
}
|
||||
@@ -64,31 +68,85 @@ void json_delete(json_t *json);
|
||||
|
||||
static inline void json_decref(json_t *json)
|
||||
{
|
||||
if(json && --json->refcount == 0)
|
||||
if(json && json->refcount != (unsigned int)-1 && --json->refcount == 0)
|
||||
json_delete(json);
|
||||
}
|
||||
|
||||
|
||||
/* getters, setters, manipulation */
|
||||
|
||||
unsigned int json_object_size(const json_t *object);
|
||||
json_t *json_object_get(const json_t *object, const char *key);
|
||||
int json_object_set(json_t *object, const char *key, json_t *value);
|
||||
int json_object_set_new(json_t *object, const char *key, json_t *value);
|
||||
int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value);
|
||||
int json_object_del(json_t *object, const char *key);
|
||||
int json_object_clear(json_t *object);
|
||||
int json_object_update(json_t *object, json_t *other);
|
||||
void *json_object_iter(json_t *object);
|
||||
void *json_object_iter_next(json_t *object, void *iter);
|
||||
const char *json_object_iter_key(void *iter);
|
||||
json_t *json_object_iter_value(void *iter);
|
||||
|
||||
static inline
|
||||
int json_object_set(json_t *object, const char *key, json_t *value)
|
||||
{
|
||||
return json_object_set_new(object, key, json_incref(value));
|
||||
}
|
||||
|
||||
static inline
|
||||
int json_object_set_nocheck(json_t *object, const char *key, json_t *value)
|
||||
{
|
||||
return json_object_set_new_nocheck(object, key, json_incref(value));
|
||||
}
|
||||
|
||||
unsigned int json_array_size(const json_t *array);
|
||||
json_t *json_array_get(const json_t *array, unsigned int index);
|
||||
int json_array_set(json_t *array, unsigned int index, json_t *value);
|
||||
int json_array_append(json_t *array, json_t *value);
|
||||
int json_array_set_new(json_t *array, unsigned int index, json_t *value);
|
||||
int json_array_append_new(json_t *array, json_t *value);
|
||||
int json_array_insert_new(json_t *array, unsigned int index, json_t *value);
|
||||
int json_array_remove(json_t *array, unsigned int index);
|
||||
int json_array_clear(json_t *array);
|
||||
int json_array_extend(json_t *array, json_t *other);
|
||||
|
||||
const char *json_string_value(const json_t *json);
|
||||
int json_integer_value(const json_t *json);
|
||||
double json_real_value(const json_t *json);
|
||||
static inline
|
||||
int json_array_set(json_t *array, unsigned int index, json_t *value)
|
||||
{
|
||||
return json_array_set_new(array, index, json_incref(value));
|
||||
}
|
||||
|
||||
static inline
|
||||
int json_array_append(json_t *array, json_t *value)
|
||||
{
|
||||
return json_array_append_new(array, json_incref(value));
|
||||
}
|
||||
|
||||
static inline
|
||||
int json_array_insert(json_t *array, unsigned int index, json_t *value)
|
||||
{
|
||||
return json_array_insert_new(array, index, json_incref(value));
|
||||
}
|
||||
|
||||
const char *json_string_value(const json_t *string);
|
||||
int json_integer_value(const json_t *integer);
|
||||
double json_real_value(const json_t *real);
|
||||
double json_number_value(const json_t *json);
|
||||
|
||||
int json_string_set(json_t *string, const char *value);
|
||||
int json_string_set_nocheck(json_t *string, const char *value);
|
||||
int json_integer_set(json_t *integer, int value);
|
||||
int json_real_set(json_t *real, double value);
|
||||
|
||||
|
||||
/* equality */
|
||||
|
||||
int json_equal(json_t *value1, json_t *value2);
|
||||
|
||||
|
||||
/* copying */
|
||||
|
||||
json_t *json_copy(json_t *value);
|
||||
json_t *json_deep_copy(json_t *value);
|
||||
|
||||
|
||||
/* loading, printing */
|
||||
|
||||
@@ -103,10 +161,17 @@ json_t *json_loads(const char *input, json_error_t *error);
|
||||
json_t *json_loadf(FILE *input, json_error_t *error);
|
||||
json_t *json_load_file(const char *path, json_error_t *error);
|
||||
|
||||
#define JSON_INDENT(n) (n & 0xFF)
|
||||
#define JSON_INDENT(n) (n & 0xFF)
|
||||
#define JSON_COMPACT 0x100
|
||||
#define JSON_ENSURE_ASCII 0x200
|
||||
#define JSON_SORT_KEYS 0x400
|
||||
|
||||
char *json_dumps(const json_t *json, uint32_t flags);
|
||||
int json_dumpf(const json_t *json, FILE *output, uint32_t flags);
|
||||
int json_dump_file(const json_t *json, const char *path, uint32_t flags);
|
||||
char *json_dumps(const json_t *json, unsigned long flags);
|
||||
int json_dumpf(const json_t *json, FILE *output, unsigned long flags);
|
||||
int json_dump_file(const json_t *json, const char *path, unsigned long flags);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8,8 +8,45 @@
|
||||
#ifndef JANSSON_PRIVATE_H
|
||||
#define JANSSON_PRIVATE_H
|
||||
|
||||
int json_object_set_nocheck(json_t *json, const char *key, json_t *value);
|
||||
json_t *json_string_nocheck(const char *value);
|
||||
#include "jansson.h"
|
||||
#include "hashtable.h"
|
||||
|
||||
#define container_of(ptr_, type_, member_) \
|
||||
((type_ *)((char *)ptr_ - (size_t)&((type_ *)0)->member_))
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
hashtable_t hashtable;
|
||||
int visited;
|
||||
} json_object_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
unsigned int size;
|
||||
unsigned int entries;
|
||||
json_t **table;
|
||||
int visited;
|
||||
} json_array_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
char *value;
|
||||
} json_string_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
double value;
|
||||
} json_real_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
int value;
|
||||
} json_integer_t;
|
||||
|
||||
#define json_to_object(json_) container_of(json_, json_object_t, json)
|
||||
#define json_to_array(json_) container_of(json_, json_array_t, json)
|
||||
#define json_to_string(json_) container_of(json_, json_string_t, json)
|
||||
#define json_to_real(json_) container_of(json_, json_real_t, json)
|
||||
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
|
||||
|
||||
#endif
|
||||
|
||||
48
src/load.c
48
src/load.c
@@ -13,8 +13,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <jansson.h>
|
||||
#include "jansson_private.h"
|
||||
@@ -149,7 +149,7 @@ static char stream_get(stream_t *stream, json_error_t *error)
|
||||
for(i = 1; i < count; i++)
|
||||
stream->buffer[i] = stream->get(stream->data);
|
||||
|
||||
if(!utf8_check_full(stream->buffer, count))
|
||||
if(!utf8_check_full(stream->buffer, count, NULL))
|
||||
goto out;
|
||||
|
||||
stream->stream_pos += count;
|
||||
@@ -222,10 +222,10 @@ static void lex_save_cached(lex_t *lex)
|
||||
}
|
||||
|
||||
/* assumes that str points to 'u' plus at least 4 valid hex digits */
|
||||
static int decode_unicode_escape(const char *str)
|
||||
static int32_t decode_unicode_escape(const char *str)
|
||||
{
|
||||
int i;
|
||||
int value = 0;
|
||||
int32_t value = 0;
|
||||
|
||||
assert(str[0] == 'u');
|
||||
|
||||
@@ -326,7 +326,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
if(*p == 'u') {
|
||||
char buffer[4];
|
||||
int length;
|
||||
int value;
|
||||
int32_t value;
|
||||
|
||||
value = decode_unicode_escape(p);
|
||||
p += 5;
|
||||
@@ -334,14 +334,15 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
if(0xD800 <= value && value <= 0xDBFF) {
|
||||
/* surrogate pair */
|
||||
if(*p == '\\' && *(p + 1) == 'u') {
|
||||
int value2 = decode_unicode_escape(++p);
|
||||
int32_t value2 = decode_unicode_escape(++p);
|
||||
p += 5;
|
||||
|
||||
if(0xDC00 <= value2 && value2 <= 0xDFFF) {
|
||||
/* valid second surrogate */
|
||||
value = ((value - 0xD800) << 10) +
|
||||
(value2 - 0xDC00) +
|
||||
0x10000;
|
||||
value =
|
||||
((value - 0xD800) << 10) +
|
||||
(value2 - 0xDC00) +
|
||||
0x10000;
|
||||
}
|
||||
else {
|
||||
/* invalid second surrogate */
|
||||
@@ -418,11 +419,15 @@ static int lex_scan_number(lex_t *lex, char c, json_error_t *error)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else /* c != '0' */ {
|
||||
else if(isdigit(c)) {
|
||||
c = lex_get_save(lex, error);
|
||||
while(isdigit(c))
|
||||
c = lex_get_save(lex, error);
|
||||
}
|
||||
else {
|
||||
lex_unget_unsave(lex, c);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(c != '.' && c != 'E' && c != 'e') {
|
||||
long value;
|
||||
@@ -506,8 +511,8 @@ static int lex_scan(lex_t *lex, json_error_t *error)
|
||||
strbuffer_clear(&lex->saved_text);
|
||||
|
||||
if(lex->token == TOKEN_STRING) {
|
||||
free(lex->value.string);
|
||||
lex->value.string = NULL;
|
||||
free(lex->value.string);
|
||||
lex->value.string = NULL;
|
||||
}
|
||||
|
||||
c = lex_get(lex, error);
|
||||
@@ -572,6 +577,17 @@ out:
|
||||
return lex->token;
|
||||
}
|
||||
|
||||
static char *lex_steal_string(lex_t *lex)
|
||||
{
|
||||
char *result = NULL;
|
||||
if(lex->token == TOKEN_STRING)
|
||||
{
|
||||
result = lex->value.string;
|
||||
lex->value.string = NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int lex_init(lex_t *lex, get_func get, eof_func eof, void *data)
|
||||
{
|
||||
stream_init(&lex->stream, get, eof, data);
|
||||
@@ -615,7 +631,7 @@ static json_t *parse_object(lex_t *lex, json_error_t *error)
|
||||
goto error;
|
||||
}
|
||||
|
||||
key = strdup(lex->value.string);
|
||||
key = lex_steal_string(lex);
|
||||
if(!key)
|
||||
return NULL;
|
||||
|
||||
@@ -734,7 +750,7 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
|
||||
break;
|
||||
|
||||
case '{':
|
||||
json = parse_object(lex, error);
|
||||
json = parse_object(lex, error);
|
||||
break;
|
||||
|
||||
case '[':
|
||||
@@ -756,7 +772,7 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
|
||||
return json;
|
||||
}
|
||||
|
||||
json_t *parse_json(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_json(lex_t *lex, json_error_t *error)
|
||||
{
|
||||
error_init(error);
|
||||
|
||||
@@ -853,6 +869,8 @@ json_t *json_load_file(const char *path, json_error_t *error)
|
||||
json_t *result;
|
||||
FILE *fp;
|
||||
|
||||
error_init(error);
|
||||
|
||||
fp = fopen(path, "r");
|
||||
if(!fp)
|
||||
{
|
||||
|
||||
39
src/utf.c
39
src/utf.c
@@ -6,8 +6,9 @@
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int utf8_encode(int codepoint, char *buffer, int *size)
|
||||
int utf8_encode(int32_t codepoint, char *buffer, int *size)
|
||||
{
|
||||
if(codepoint < 0)
|
||||
return -1;
|
||||
@@ -79,9 +80,10 @@ int utf8_check_first(char byte)
|
||||
}
|
||||
}
|
||||
|
||||
int utf8_check_full(const char *buffer, int size)
|
||||
int utf8_check_full(const char *buffer, int size, int32_t *codepoint)
|
||||
{
|
||||
int i, value = 0;
|
||||
int i;
|
||||
int32_t value = 0;
|
||||
unsigned char u = (unsigned char)buffer[0];
|
||||
|
||||
if(size == 2)
|
||||
@@ -128,9 +130,38 @@ int utf8_check_full(const char *buffer, int size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(codepoint)
|
||||
*codepoint = value;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *utf8_iterate(const char *buffer, int32_t *codepoint)
|
||||
{
|
||||
int count;
|
||||
int32_t value;
|
||||
|
||||
if(!*buffer)
|
||||
return buffer;
|
||||
|
||||
count = utf8_check_first(buffer[0]);
|
||||
if(count <= 0)
|
||||
return NULL;
|
||||
|
||||
if(count == 1)
|
||||
value = (unsigned char)buffer[0];
|
||||
else
|
||||
{
|
||||
if(!utf8_check_full(buffer, count, &value))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(codepoint)
|
||||
*codepoint = value;
|
||||
|
||||
return buffer + count;
|
||||
}
|
||||
|
||||
int utf8_check_string(const char *string, int length)
|
||||
{
|
||||
int i;
|
||||
@@ -148,7 +179,7 @@ int utf8_check_string(const char *string, int length)
|
||||
if(i + count > length)
|
||||
return 0;
|
||||
|
||||
if(!utf8_check_full(&string[i], count))
|
||||
if(!utf8_check_full(&string[i], count, NULL))
|
||||
return 0;
|
||||
|
||||
i += count - 1;
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
int utf8_encode(int codepoint, char *buffer, int *size);
|
||||
|
||||
int utf8_check_first(char byte);
|
||||
int utf8_check_full(const char *buffer, int size);
|
||||
int utf8_check_full(const char *buffer, int size, int32_t *codepoint);
|
||||
const char *utf8_iterate(const char *buffer, int32_t *codepoint);
|
||||
|
||||
int utf8_check_string(const char *string, int length);
|
||||
|
||||
|
||||
630
src/value.c
630
src/value.c
@@ -15,41 +15,6 @@
|
||||
#include "utf.h"
|
||||
#include "util.h"
|
||||
|
||||
#define container_of(ptr_, type_, member_) \
|
||||
((type_ *)((char *)ptr_ - (size_t)&((type_ *)0)->member_))
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
hashtable_t hashtable;
|
||||
} json_object_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
unsigned int size;
|
||||
unsigned int entries;
|
||||
json_t **table;
|
||||
} json_array_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
char *value;
|
||||
} json_string_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
double value;
|
||||
} json_real_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
int value;
|
||||
} json_integer_t;
|
||||
|
||||
#define json_to_object(json_) container_of(json_, json_object_t, json)
|
||||
#define json_to_array(json_) container_of(json_, json_array_t, json)
|
||||
#define json_to_string(json_) container_of(json_, json_string_t, json)
|
||||
#define json_to_real(json_) container_of(json_, json_real_t, json)
|
||||
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
|
||||
|
||||
static inline void json_init(json_t *json, json_type type)
|
||||
{
|
||||
@@ -98,6 +63,9 @@ json_t *json_object(void)
|
||||
free(object);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
object->visited = 0;
|
||||
|
||||
return &object->json;
|
||||
}
|
||||
|
||||
@@ -107,6 +75,17 @@ static void json_delete_object(json_object_t *object)
|
||||
free(object);
|
||||
}
|
||||
|
||||
unsigned int json_object_size(const json_t *json)
|
||||
{
|
||||
json_object_t *object;
|
||||
|
||||
if(!json_is_object(json))
|
||||
return -1;
|
||||
|
||||
object = json_to_object(json);
|
||||
return object->hashtable.size;
|
||||
}
|
||||
|
||||
json_t *json_object_get(const json_t *json, const char *key)
|
||||
{
|
||||
json_object_t *object;
|
||||
@@ -118,23 +97,38 @@ json_t *json_object_get(const json_t *json, const char *key)
|
||||
return hashtable_get(&object->hashtable, key);
|
||||
}
|
||||
|
||||
int json_object_set_nocheck(json_t *json, const char *key, json_t *value)
|
||||
int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
|
||||
{
|
||||
json_object_t *object;
|
||||
|
||||
if(!json_is_object(json))
|
||||
if(!key || !value)
|
||||
return -1;
|
||||
|
||||
if(!json_is_object(json) || json == value)
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
object = json_to_object(json);
|
||||
return hashtable_set(&object->hashtable, strdup(key), json_incref(value));
|
||||
|
||||
if(hashtable_set(&object->hashtable, strdup(key), value))
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_object_set(json_t *json, const char *key, json_t *value)
|
||||
int json_object_set_new(json_t *json, const char *key, json_t *value)
|
||||
{
|
||||
if(!utf8_check_string(key, -1))
|
||||
if(!key || !utf8_check_string(key, -1))
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return json_object_set_nocheck(json, key, value);
|
||||
return json_object_set_new_nocheck(json, key, value);
|
||||
}
|
||||
|
||||
int json_object_del(json_t *json, const char *key)
|
||||
@@ -148,6 +142,43 @@ int json_object_del(json_t *json, const char *key)
|
||||
return hashtable_del(&object->hashtable, key);
|
||||
}
|
||||
|
||||
int json_object_clear(json_t *json)
|
||||
{
|
||||
json_object_t *object;
|
||||
|
||||
if(!json_is_object(json))
|
||||
return -1;
|
||||
|
||||
object = json_to_object(json);
|
||||
hashtable_clear(&object->hashtable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_object_update(json_t *object, json_t *other)
|
||||
{
|
||||
void *iter;
|
||||
|
||||
if(!json_is_object(object) || !json_is_object(other))
|
||||
return -1;
|
||||
|
||||
iter = json_object_iter(other);
|
||||
while(iter) {
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
key = json_object_iter_key(iter);
|
||||
value = json_object_iter_value(iter);
|
||||
|
||||
if(json_object_set_nocheck(object, key, value))
|
||||
return -1;
|
||||
|
||||
iter = json_object_iter_next(other, iter);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *json_object_iter(json_t *json)
|
||||
{
|
||||
json_object_t *object;
|
||||
@@ -186,6 +217,82 @@ json_t *json_object_iter_value(void *iter)
|
||||
return (json_t *)hashtable_iter_value(iter);
|
||||
}
|
||||
|
||||
static int json_object_equal(json_t *object1, json_t *object2)
|
||||
{
|
||||
void *iter;
|
||||
|
||||
if(json_object_size(object1) != json_object_size(object2))
|
||||
return 0;
|
||||
|
||||
iter = json_object_iter(object1);
|
||||
while(iter)
|
||||
{
|
||||
const char *key;
|
||||
json_t *value1, *value2;
|
||||
|
||||
key = json_object_iter_key(iter);
|
||||
value1 = json_object_iter_value(iter);
|
||||
value2 = json_object_get(object2, key);
|
||||
|
||||
if(!json_equal(value1, value2))
|
||||
return 0;
|
||||
|
||||
iter = json_object_iter_next(object1, iter);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static json_t *json_object_copy(json_t *object)
|
||||
{
|
||||
json_t *result;
|
||||
void *iter;
|
||||
|
||||
result = json_object();
|
||||
if(!result)
|
||||
return NULL;
|
||||
|
||||
iter = json_object_iter(object);
|
||||
while(iter)
|
||||
{
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
key = json_object_iter_key(iter);
|
||||
value = json_object_iter_value(iter);
|
||||
json_object_set_nocheck(result, key, value);
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static json_t *json_object_deep_copy(json_t *object)
|
||||
{
|
||||
json_t *result;
|
||||
void *iter;
|
||||
|
||||
result = json_object();
|
||||
if(!result)
|
||||
return NULL;
|
||||
|
||||
iter = json_object_iter(object);
|
||||
while(iter)
|
||||
{
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
key = json_object_iter_key(iter);
|
||||
value = json_object_iter_value(iter);
|
||||
json_object_set_new_nocheck(result, key, json_deep_copy(value));
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*** array ***/
|
||||
|
||||
@@ -193,12 +300,19 @@ json_t *json_array(void)
|
||||
{
|
||||
json_array_t *array = malloc(sizeof(json_array_t));
|
||||
if(!array)
|
||||
return NULL;
|
||||
return NULL;
|
||||
json_init(&array->json, JSON_ARRAY);
|
||||
|
||||
array->entries = 0;
|
||||
array->size = 0;
|
||||
array->table = NULL;
|
||||
array->size = 8;
|
||||
|
||||
array->table = malloc(array->size * sizeof(json_t *));
|
||||
if(!array->table) {
|
||||
free(array);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
array->visited = 0;
|
||||
|
||||
return &array->json;
|
||||
}
|
||||
@@ -235,9 +349,143 @@ json_t *json_array_get(const json_t *json, unsigned int index)
|
||||
return array->table[index];
|
||||
}
|
||||
|
||||
int json_array_set(json_t *json, unsigned int index, json_t *value)
|
||||
int json_array_set_new(json_t *json, unsigned int index, json_t *value)
|
||||
{
|
||||
json_array_t *array;
|
||||
|
||||
if(!value)
|
||||
return -1;
|
||||
|
||||
if(!json_is_array(json) || json == value)
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
array = json_to_array(json);
|
||||
|
||||
if(index >= array->entries)
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
json_decref(array->table[index]);
|
||||
array->table[index] = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void array_move(json_array_t *array, unsigned int dest,
|
||||
unsigned int src, unsigned int count)
|
||||
{
|
||||
memmove(&array->table[dest], &array->table[src], count * sizeof(json_t *));
|
||||
}
|
||||
|
||||
static void array_copy(json_t **dest, unsigned int dpos,
|
||||
json_t **src, unsigned int spos,
|
||||
unsigned int count)
|
||||
{
|
||||
memcpy(&dest[dpos], &src[spos], count * sizeof(json_t *));
|
||||
}
|
||||
|
||||
static json_t **json_array_grow(json_array_t *array,
|
||||
unsigned int amount,
|
||||
int copy)
|
||||
{
|
||||
unsigned int new_size;
|
||||
json_t **old_table, **new_table;
|
||||
|
||||
if(array->entries + amount <= array->size)
|
||||
return array->table;
|
||||
|
||||
old_table = array->table;
|
||||
|
||||
new_size = max(array->size + amount, array->size * 2);
|
||||
new_table = malloc(new_size * sizeof(json_t *));
|
||||
if(!new_table)
|
||||
return NULL;
|
||||
|
||||
array->size = new_size;
|
||||
array->table = new_table;
|
||||
|
||||
if(copy) {
|
||||
array_copy(array->table, 0, old_table, 0, array->entries);
|
||||
free(old_table);
|
||||
return array->table;
|
||||
}
|
||||
|
||||
return old_table;
|
||||
}
|
||||
|
||||
int json_array_append_new(json_t *json, json_t *value)
|
||||
{
|
||||
json_array_t *array;
|
||||
|
||||
if(!value)
|
||||
return -1;
|
||||
|
||||
if(!json_is_array(json) || json == value)
|
||||
{
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
array = json_to_array(json);
|
||||
|
||||
if(!json_array_grow(array, 1, 1)) {
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
array->table[array->entries] = value;
|
||||
array->entries++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_array_insert_new(json_t *json, unsigned int index, json_t *value)
|
||||
{
|
||||
json_array_t *array;
|
||||
json_t **old_table;
|
||||
|
||||
if(!value)
|
||||
return -1;
|
||||
|
||||
if(!json_is_array(json) || json == value) {
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
array = json_to_array(json);
|
||||
|
||||
if(index > array->entries) {
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
old_table = json_array_grow(array, 1, 0);
|
||||
if(!old_table) {
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(old_table != array->table) {
|
||||
array_copy(array->table, 0, old_table, 0, index);
|
||||
array_copy(array->table, index + 1, old_table, index,
|
||||
array->entries - index);
|
||||
free(old_table);
|
||||
}
|
||||
else
|
||||
array_move(array, index + 1, index, array->entries - index);
|
||||
|
||||
array->table[index] = value;
|
||||
array->entries++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_array_remove(json_t *json, unsigned int index)
|
||||
{
|
||||
json_array_t *array;
|
||||
|
||||
if(!json_is_array(json))
|
||||
return -1;
|
||||
array = json_to_array(json);
|
||||
@@ -246,48 +494,129 @@ int json_array_set(json_t *json, unsigned int index, json_t *value)
|
||||
return -1;
|
||||
|
||||
json_decref(array->table[index]);
|
||||
array->table[index] = json_incref(value);
|
||||
|
||||
array_move(array, index, index + 1, array->entries - index);
|
||||
array->entries--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_array_append(json_t *json, json_t *value)
|
||||
int json_array_clear(json_t *json)
|
||||
{
|
||||
json_array_t *array;
|
||||
unsigned int i;
|
||||
|
||||
if(!json_is_array(json))
|
||||
return -1;
|
||||
array = json_to_array(json);
|
||||
|
||||
if(array->entries == array->size) {
|
||||
array->size = max(8, array->size * 2);
|
||||
array->table = realloc(array->table, array->size * sizeof(json_t *));
|
||||
if(!array->table)
|
||||
return -1;
|
||||
}
|
||||
|
||||
array->table[array->entries] = json_incref(value);
|
||||
array->entries++;
|
||||
for(i = 0; i < array->entries; i++)
|
||||
json_decref(array->table[i]);
|
||||
|
||||
array->entries = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_array_extend(json_t *json, json_t *other_json)
|
||||
{
|
||||
json_array_t *array, *other;
|
||||
unsigned int i;
|
||||
|
||||
if(!json_is_array(json) || !json_is_array(other_json))
|
||||
return -1;
|
||||
array = json_to_array(json);
|
||||
other = json_to_array(other_json);
|
||||
|
||||
if(!json_array_grow(array, other->entries, 1))
|
||||
return -1;
|
||||
|
||||
for(i = 0; i < other->entries; i++)
|
||||
json_incref(other->table[i]);
|
||||
|
||||
array_copy(array->table, array->entries, other->table, 0, other->entries);
|
||||
|
||||
array->entries += other->entries;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_array_equal(json_t *array1, json_t *array2)
|
||||
{
|
||||
unsigned int i, size;
|
||||
|
||||
size = json_array_size(array1);
|
||||
if(size != json_array_size(array2))
|
||||
return 0;
|
||||
|
||||
for(i = 0; i < size; i++)
|
||||
{
|
||||
json_t *value1, *value2;
|
||||
|
||||
value1 = json_array_get(array1, i);
|
||||
value2 = json_array_get(array2, i);
|
||||
|
||||
if(!json_equal(value1, value2))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static json_t *json_array_copy(json_t *array)
|
||||
{
|
||||
json_t *result;
|
||||
unsigned int i;
|
||||
|
||||
result = json_array();
|
||||
if(!result)
|
||||
return NULL;
|
||||
|
||||
for(i = 0; i < json_array_size(array); i++)
|
||||
json_array_append(result, json_array_get(array, i));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static json_t *json_array_deep_copy(json_t *array)
|
||||
{
|
||||
json_t *result;
|
||||
unsigned int i;
|
||||
|
||||
result = json_array();
|
||||
if(!result)
|
||||
return NULL;
|
||||
|
||||
for(i = 0; i < json_array_size(array); i++)
|
||||
json_array_append_new(result, json_deep_copy(json_array_get(array, i)));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*** string ***/
|
||||
|
||||
json_t *json_string_nocheck(const char *value)
|
||||
{
|
||||
json_string_t *string = malloc(sizeof(json_string_t));
|
||||
json_string_t *string;
|
||||
|
||||
if(!value)
|
||||
return NULL;
|
||||
|
||||
string = malloc(sizeof(json_string_t));
|
||||
if(!string)
|
||||
return NULL;
|
||||
return NULL;
|
||||
json_init(&string->json, JSON_STRING);
|
||||
|
||||
string->value = strdup(value);
|
||||
if(!string->value) {
|
||||
free(string);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &string->json;
|
||||
}
|
||||
|
||||
json_t *json_string(const char *value)
|
||||
{
|
||||
if(!utf8_check_string(value, -1))
|
||||
if(!value || !utf8_check_string(value, -1))
|
||||
return NULL;
|
||||
|
||||
return json_string_nocheck(value);
|
||||
@@ -301,12 +630,46 @@ const char *json_string_value(const json_t *json)
|
||||
return json_to_string(json)->value;
|
||||
}
|
||||
|
||||
int json_string_set_nocheck(json_t *json, const char *value)
|
||||
{
|
||||
char *dup;
|
||||
json_string_t *string;
|
||||
|
||||
dup = strdup(value);
|
||||
if(!dup)
|
||||
return -1;
|
||||
|
||||
string = json_to_string(json);
|
||||
free(string->value);
|
||||
string->value = dup;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_string_set(json_t *json, const char *value)
|
||||
{
|
||||
if(!value || !utf8_check_string(value, -1))
|
||||
return -1;
|
||||
|
||||
return json_string_set_nocheck(json, value);
|
||||
}
|
||||
|
||||
static void json_delete_string(json_string_t *string)
|
||||
{
|
||||
free(string->value);
|
||||
free(string);
|
||||
}
|
||||
|
||||
static int json_string_equal(json_t *string1, json_t *string2)
|
||||
{
|
||||
return strcmp(json_string_value(string1), json_string_value(string2)) == 0;
|
||||
}
|
||||
|
||||
static json_t *json_string_copy(json_t *string)
|
||||
{
|
||||
return json_string_nocheck(json_string_value(string));
|
||||
}
|
||||
|
||||
|
||||
/*** integer ***/
|
||||
|
||||
@@ -314,7 +677,7 @@ json_t *json_integer(int value)
|
||||
{
|
||||
json_integer_t *integer = malloc(sizeof(json_integer_t));
|
||||
if(!integer)
|
||||
return NULL;
|
||||
return NULL;
|
||||
json_init(&integer->json, JSON_INTEGER);
|
||||
|
||||
integer->value = value;
|
||||
@@ -329,11 +692,31 @@ int json_integer_value(const json_t *json)
|
||||
return json_to_integer(json)->value;
|
||||
}
|
||||
|
||||
int json_integer_set(json_t *json, int value)
|
||||
{
|
||||
if(!json_is_integer(json))
|
||||
return -1;
|
||||
|
||||
json_to_integer(json)->value = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void json_delete_integer(json_integer_t *integer)
|
||||
{
|
||||
free(integer);
|
||||
}
|
||||
|
||||
static int json_integer_equal(json_t *integer1, json_t *integer2)
|
||||
{
|
||||
return json_integer_value(integer1) == json_integer_value(integer2);
|
||||
}
|
||||
|
||||
static json_t *json_integer_copy(json_t *integer)
|
||||
{
|
||||
return json_integer(json_integer_value(integer));
|
||||
}
|
||||
|
||||
|
||||
/*** real ***/
|
||||
|
||||
@@ -341,7 +724,7 @@ json_t *json_real(double value)
|
||||
{
|
||||
json_real_t *real = malloc(sizeof(json_real_t));
|
||||
if(!real)
|
||||
return NULL;
|
||||
return NULL;
|
||||
json_init(&real->json, JSON_REAL);
|
||||
|
||||
real->value = value;
|
||||
@@ -356,11 +739,31 @@ double json_real_value(const json_t *json)
|
||||
return json_to_real(json)->value;
|
||||
}
|
||||
|
||||
static void json_delete_real (json_real_t *real)
|
||||
int json_real_set(json_t *json, double value)
|
||||
{
|
||||
if(!json_is_real(json))
|
||||
return 0;
|
||||
|
||||
json_to_real(json)->value = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void json_delete_real(json_real_t *real)
|
||||
{
|
||||
free(real);
|
||||
}
|
||||
|
||||
static int json_real_equal(json_t *real1, json_t *real2)
|
||||
{
|
||||
return json_real_value(real1) == json_real_value(real2);
|
||||
}
|
||||
|
||||
static json_t *json_real_copy(json_t *real)
|
||||
{
|
||||
return json_real(json_real_value(real));
|
||||
}
|
||||
|
||||
|
||||
/*** number ***/
|
||||
|
||||
@@ -381,9 +784,9 @@ json_t *json_true(void)
|
||||
{
|
||||
static json_t the_true = {
|
||||
.type = JSON_TRUE,
|
||||
.refcount = 1
|
||||
.refcount = (unsigned int)1
|
||||
};
|
||||
return json_incref(&the_true);
|
||||
return &the_true;
|
||||
}
|
||||
|
||||
|
||||
@@ -391,9 +794,9 @@ json_t *json_false(void)
|
||||
{
|
||||
static json_t the_false = {
|
||||
.type = JSON_FALSE,
|
||||
.refcount = 1
|
||||
.refcount = (unsigned int)1
|
||||
};
|
||||
return json_incref(&the_false);
|
||||
return &the_false;
|
||||
}
|
||||
|
||||
|
||||
@@ -401,9 +804,9 @@ json_t *json_null(void)
|
||||
{
|
||||
static json_t the_null = {
|
||||
.type = JSON_NULL,
|
||||
.refcount = 1
|
||||
.refcount = (unsigned int)1
|
||||
};
|
||||
return json_incref(&the_null);
|
||||
return &the_null;
|
||||
}
|
||||
|
||||
|
||||
@@ -428,3 +831,94 @@ void json_delete(json_t *json)
|
||||
|
||||
/* json_delete is not called for true, false or null */
|
||||
}
|
||||
|
||||
|
||||
/*** equality ***/
|
||||
|
||||
int json_equal(json_t *json1, json_t *json2)
|
||||
{
|
||||
if(!json1 || !json2)
|
||||
return 0;
|
||||
|
||||
if(json_typeof(json1) != json_typeof(json2))
|
||||
return 0;
|
||||
|
||||
/* this covers true, false and null as they are singletons */
|
||||
if(json1 == json2)
|
||||
return 1;
|
||||
|
||||
if(json_is_object(json1))
|
||||
return json_object_equal(json1, json2);
|
||||
|
||||
if(json_is_array(json1))
|
||||
return json_array_equal(json1, json2);
|
||||
|
||||
if(json_is_string(json1))
|
||||
return json_string_equal(json1, json2);
|
||||
|
||||
if(json_is_integer(json1))
|
||||
return json_integer_equal(json1, json2);
|
||||
|
||||
if(json_is_real(json1))
|
||||
return json_real_equal(json1, json2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*** copying ***/
|
||||
|
||||
json_t *json_copy(json_t *json)
|
||||
{
|
||||
if(!json)
|
||||
return NULL;
|
||||
|
||||
if(json_is_object(json))
|
||||
return json_object_copy(json);
|
||||
|
||||
if(json_is_array(json))
|
||||
return json_array_copy(json);
|
||||
|
||||
if(json_is_string(json))
|
||||
return json_string_copy(json);
|
||||
|
||||
if(json_is_integer(json))
|
||||
return json_integer_copy(json);
|
||||
|
||||
if(json_is_real(json))
|
||||
return json_real_copy(json);
|
||||
|
||||
if(json_is_true(json) || json_is_false(json) || json_is_null(json))
|
||||
return json;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
json_t *json_deep_copy(json_t *json)
|
||||
{
|
||||
if(!json)
|
||||
return NULL;
|
||||
|
||||
if(json_is_object(json))
|
||||
return json_object_deep_copy(json);
|
||||
|
||||
if(json_is_array(json))
|
||||
return json_array_deep_copy(json);
|
||||
|
||||
/* for the rest of the types, deep copying doesn't differ from
|
||||
shallow copying */
|
||||
|
||||
if(json_is_string(json))
|
||||
return json_string_copy(json);
|
||||
|
||||
if(json_is_integer(json))
|
||||
return json_integer_copy(json);
|
||||
|
||||
if(json_is_real(json))
|
||||
return json_real_copy(json);
|
||||
|
||||
if(json_is_true(json) || json_is_false(json) || json_is_null(json))
|
||||
return json;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
16
test/.gitignore
vendored
16
test/.gitignore
vendored
@@ -1,7 +1,9 @@
|
||||
loadf_dumpf
|
||||
loads_dumps
|
||||
load_file_dump_file
|
||||
testlogs
|
||||
testprogs/test_array
|
||||
testprogs/test_number
|
||||
testprogs/test_object
|
||||
logs
|
||||
bin/json_process
|
||||
suites/api/test_array
|
||||
suites/api/test_equal
|
||||
suites/api/test_copy
|
||||
suites/api/test_load
|
||||
suites/api/test_number
|
||||
suites/api/test_object
|
||||
suites/api/test_simple
|
||||
|
||||
@@ -1,22 +1,10 @@
|
||||
DIST_SUBDIRS = testprogs testdata
|
||||
SUBDIRS = testprogs
|
||||
SUBDIRS = bin suites
|
||||
EXTRA_DIST = scripts run-suites
|
||||
|
||||
check_PROGRAMS = loadf_dumpf loads_dumps load_file_dump_file
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src
|
||||
AM_CFLAGS = -Wall -Werror
|
||||
LDFLAGS = -static # for speed and Valgrind
|
||||
LDADD = ../src/libjansson.la
|
||||
|
||||
TESTS = test-api test-invalid test-valid
|
||||
|
||||
EXTRA_DIST = \
|
||||
test-api \
|
||||
test-invalid \
|
||||
test-valid \
|
||||
run-test \
|
||||
json-compare.py \
|
||||
split-testfile.py
|
||||
TESTS = run-suites
|
||||
TESTS_ENVIRONMENT = \
|
||||
top_srcdir=$(top_srcdir) \
|
||||
top_builddir=$(top_builddir)
|
||||
|
||||
clean-local:
|
||||
rm -rf testlogs
|
||||
rm -rf logs
|
||||
|
||||
6
test/bin/Makefile.am
Normal file
6
test/bin/Makefile.am
Normal file
@@ -0,0 +1,6 @@
|
||||
check_PROGRAMS = json_process
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src
|
||||
AM_CFLAGS = -Wall -Werror
|
||||
LDFLAGS = -static # for speed and Valgrind
|
||||
LDADD = $(top_builddir)/src/libjansson.la
|
||||
69
test/bin/json_process.c
Normal file
69
test/bin/json_process.c
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2009 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <jansson.h>
|
||||
|
||||
static int getenv_int(const char *name)
|
||||
{
|
||||
char *value, *end;
|
||||
long result;
|
||||
|
||||
value = getenv(name);
|
||||
if(!value)
|
||||
return 0;
|
||||
|
||||
result = strtol(value, &end, 10);
|
||||
if(*end != '\0')
|
||||
return 0;
|
||||
|
||||
return (int)result;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int indent = 0;
|
||||
unsigned int flags = 0;
|
||||
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
|
||||
if(argc != 1) {
|
||||
fprintf(stderr, "usage: %s\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
indent = getenv_int("JSON_INDENT");
|
||||
if(indent < 0 || indent > 255) {
|
||||
fprintf(stderr, "invalid value for JSON_INDENT: %d\n", indent);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if(indent > 0)
|
||||
flags |= JSON_INDENT(indent);
|
||||
|
||||
if(getenv_int("JSON_COMPACT") > 0)
|
||||
flags |= JSON_COMPACT;
|
||||
|
||||
if(getenv_int("JSON_ENSURE_ASCII"))
|
||||
flags |= JSON_ENSURE_ASCII;
|
||||
|
||||
if(getenv_int("JSON_SORT_KEYS"))
|
||||
flags |= JSON_SORT_KEYS;
|
||||
|
||||
json = json_loadf(stdin, &error);
|
||||
if(!json) {
|
||||
fprintf(stderr, "%d\n%s\n", error.line, error.text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
json_dumpf(json, stdout, flags);
|
||||
json_decref(json);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2009 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.
|
||||
|
||||
import sys
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
import simplejson as json
|
||||
|
||||
def load(filename):
|
||||
try:
|
||||
jsonfile = open(filename)
|
||||
except IOError, err:
|
||||
print >>sys.stderr, "unable to load %s: %s" % \
|
||||
(filename, err.strerror)
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
jsondata = json.load(jsonfile)
|
||||
except ValueError, err:
|
||||
print "%s is malformed: %s" % (filename, err)
|
||||
sys.exit(1)
|
||||
finally:
|
||||
jsonfile.close()
|
||||
|
||||
return jsondata
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
print >>sys.stderr, "usage: %s json1 json2" % sys.argv[0]
|
||||
return 2
|
||||
|
||||
json1 = load(sys.argv[1])
|
||||
json2 = load(sys.argv[2])
|
||||
if json1 == json2:
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main() or 0)
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009 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 <stdio.h>
|
||||
#include <jansson.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
|
||||
if(argc != 3) {
|
||||
fprintf(stderr, "usage: %s infile outfile\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
json = json_load_file(argv[1], &error);
|
||||
if(!json) {
|
||||
fprintf(stderr, "%d\n%s\n", error.line, error.text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
json_dump_file(json, argv[2], 0);
|
||||
json_decref(json);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009 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 <stdio.h>
|
||||
#include <jansson.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
|
||||
if(argc != 1) {
|
||||
fprintf(stderr, "usage: %s\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
json = json_loadf(stdin, &error);
|
||||
if(!json) {
|
||||
fprintf(stderr, "%d\n%s\n", error.line, error.text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* loadf_dumpf indents, others don't, so dumping with and without
|
||||
indenting is tested */
|
||||
json_dumpf(json, stdout, JSON_INDENT(4));
|
||||
json_decref(json);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <jansson.h>
|
||||
|
||||
#define BUFFER_SIZE (256 * 1024)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
int count;
|
||||
char buffer[BUFFER_SIZE];
|
||||
char *result;
|
||||
|
||||
if(argc != 1) {
|
||||
fprintf(stderr, "usage: %s\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
count = fread(buffer, 1, BUFFER_SIZE, stdin);
|
||||
if(count < 0 || count >= BUFFER_SIZE) {
|
||||
fprintf(stderr, "unable to read input\n");
|
||||
return 1;
|
||||
}
|
||||
buffer[count] = '\0';
|
||||
|
||||
json = json_loads(buffer, &error);
|
||||
if(!json) {
|
||||
fprintf(stderr, "%d\n%s\n", error.line, error.text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
result = json_dumps(json, 0);
|
||||
json_decref(json);
|
||||
|
||||
puts(result);
|
||||
free(result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
46
test/run-suites
Executable file
46
test/run-suites
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/bin/sh
|
||||
|
||||
while [ -n "$1" ]; do
|
||||
suite=$1
|
||||
if [ -x $top_srcdir/test/suites/$suite/run ]; then
|
||||
SUITES="$SUITES $suite"
|
||||
else
|
||||
echo "No such suite: $suite"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
done
|
||||
|
||||
if [ -z "$SUITES" ]; then
|
||||
suitedirs=$top_srcdir/test/suites/*
|
||||
for suitedir in $suitedirs; do
|
||||
if [ -d $suitedir ]; then
|
||||
SUITES="$SUITES `basename $suitedir`"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
export suites_srcdir=$top_srcdir/test/suites
|
||||
export suites_builddir=suites
|
||||
export scriptdir=$top_srcdir/test/scripts
|
||||
export logdir=logs
|
||||
export bindir=bin
|
||||
|
||||
passed=0
|
||||
failed=0
|
||||
for suite in $SUITES; do
|
||||
echo "Suite: $suite"
|
||||
if $suites_srcdir/$suite/run $suite; then
|
||||
passed=$(($passed+1))
|
||||
else
|
||||
failed=$(($failed+1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $failed -gt 0 ]; then
|
||||
echo "$failed of $((passed+failed)) test suites failed"
|
||||
exit 1
|
||||
else
|
||||
echo "$passed test suites passed"
|
||||
rm -rf $logdir
|
||||
fi
|
||||
@@ -1,54 +0,0 @@
|
||||
# Copyright (c) 2009 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.
|
||||
|
||||
VALGRIND_CMDLINE="valgrind --leak-check=full --show-reachable=yes --track-origins=yes -q"
|
||||
|
||||
run_testprog() {
|
||||
local prog=$1
|
||||
local prefix=$2
|
||||
if [ -n "$VALGRIND" ]; then
|
||||
local runner="$VALGRIND_CMDLINE "
|
||||
fi
|
||||
|
||||
case "$prog" in
|
||||
load_file_dump_file)
|
||||
$runner./$prog \
|
||||
$prefix.in \
|
||||
$prefix.$prog.stdout \
|
||||
2>$prefix.$prog.stderr
|
||||
;;
|
||||
*)
|
||||
$runner./$prog \
|
||||
<$prefix.in \
|
||||
>$prefix.$prog.stdout \
|
||||
2>$prefix.$prog.stderr
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -n "$VALGRIND" ]; then
|
||||
# Check for Valgrind error output. The valgrind option
|
||||
# --error-exitcode is not enough because Valgrind doesn't
|
||||
# think unfreed allocs are errors.
|
||||
if grep -E -q '^==[0-9]+== ' $prefix.$prog.stderr; then
|
||||
echo "### $prefix ($prog) failed:" >&2
|
||||
echo "valgrind detected an error" >&2
|
||||
echo "for details, see test/$prefix.$prog.stderr" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
for testfile in $TESTFILES; do
|
||||
tmpdir="testlogs/`basename $testfile`"
|
||||
rm -rf $tmpdir
|
||||
mkdir -p $tmpdir
|
||||
${srcdir}/split-testfile.py $testfile $tmpdir | while read name; do
|
||||
run_test loadf_dumpf $tmpdir/$name
|
||||
run_test loads_dumps $tmpdir/$name
|
||||
run_test load_file_dump_file $tmpdir/$name
|
||||
echo -n '.'
|
||||
done || exit 1
|
||||
echo
|
||||
done
|
||||
71
test/scripts/run-tests.sh
Normal file
71
test/scripts/run-tests.sh
Normal file
@@ -0,0 +1,71 @@
|
||||
# Copyright (c) 2009 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.
|
||||
|
||||
json_process=$bindir/json_process
|
||||
|
||||
suite_name=$1
|
||||
suite_srcdir=$suites_srcdir/$suite_name
|
||||
suite_builddir=$suites_builddir/$suite_name
|
||||
suite_log=$logdir/$suite_name
|
||||
|
||||
|
||||
[ -z "$VERBOSE" ] && VERBOSE=0
|
||||
|
||||
. $scriptdir/valgrind.sh
|
||||
|
||||
rm -rf $suite_log
|
||||
mkdir -p $suite_log
|
||||
|
||||
for test_path in $suite_srcdir/*; do
|
||||
test_name=$(basename $test_path)
|
||||
test_builddir=$suite_builddir/$test_name
|
||||
test_log=$suite_log/$test_name
|
||||
|
||||
[ "$test_name" = "run" ] && continue
|
||||
is_test || continue
|
||||
|
||||
rm -rf $test_log
|
||||
mkdir -p $test_log
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
echo -n "$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
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $VERBOSE -eq 0 ]; then
|
||||
echo
|
||||
fi
|
||||
|
||||
if [ -n "$(ls -A $suite_log)" ]; then
|
||||
for test_log in $suite_log/*; do
|
||||
test_name=$(basename $test_log)
|
||||
test_path=$suite_srcdir/$test_name
|
||||
echo "================================================================="
|
||||
echo "$suite_name/$test_name"
|
||||
echo "================================================================="
|
||||
show_error
|
||||
echo
|
||||
done
|
||||
echo "================================================================="
|
||||
exit 1
|
||||
else
|
||||
rm -rf $suite_log
|
||||
fi
|
||||
35
test/scripts/valgrind.sh
Normal file
35
test/scripts/valgrind.sh
Normal file
@@ -0,0 +1,35 @@
|
||||
# Copyright (c) 2009 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.
|
||||
|
||||
[ -z "$VALGRIND" ] && VALGRIND=0
|
||||
|
||||
VALGRIND_CMDLINE="valgrind --leak-check=full --show-reachable=yes --track-origins=yes -q"
|
||||
|
||||
if [ $VALGRIND -eq 1 ]; then
|
||||
test_runner="$VALGRIND_CMDLINE"
|
||||
json_process="$VALGRIND_CMDLINE $json_process"
|
||||
else
|
||||
test_runner=""
|
||||
fi
|
||||
|
||||
valgrind_check() {
|
||||
if [ $VALGRIND -eq 1 ]; then
|
||||
# Check for Valgrind error output. The valgrind option
|
||||
# --error-exitcode is not enough because Valgrind doesn't
|
||||
# think unfreed allocs are errors.
|
||||
if grep -E -q '^==[0-9]+== ' $1; then
|
||||
touch $test_log/valgrind_error
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
valgrind_show_error() {
|
||||
if [ $VALGRIND -eq 1 -a -f $test_log/valgrind_error ]; then
|
||||
echo "valgrind detected an error"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2009 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.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
def open_files(outdir, i, name):
|
||||
basename = '%02d_%s' % (i, name)
|
||||
print basename
|
||||
input_path = os.path.join(outdir, basename + '.in')
|
||||
output_path = os.path.join(outdir, basename + '.out')
|
||||
return open(input_path, 'w'), open(output_path, 'w')
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
print 'usage: %s input-file output-directory' % sys.argv[0]
|
||||
return 2
|
||||
|
||||
infile = os.path.normpath(sys.argv[1])
|
||||
outdir = os.path.normpath(sys.argv[2])
|
||||
|
||||
if not os.path.exists(outdir):
|
||||
print >>sys.stderr, 'output directory %r does not exist!' % outdir
|
||||
return 1
|
||||
|
||||
n = 0
|
||||
current = None
|
||||
input, output = None, None
|
||||
|
||||
for line in open(infile):
|
||||
if line.startswith('==== '):
|
||||
n += 1
|
||||
if input is not None and output is not None:
|
||||
input.close()
|
||||
output.close()
|
||||
input, output = open_files(outdir, n, line[5:line.find(' ====\n')])
|
||||
current = input
|
||||
elif line == '====\n':
|
||||
current = output
|
||||
else:
|
||||
current.write(line)
|
||||
|
||||
if input is not None and output is not None:
|
||||
input.close()
|
||||
output.close()
|
||||
|
||||
print >>sys.stderr, "%s: %d test cases" % (infile, n)
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main() or 0)
|
||||
2
test/suites/Makefile.am
Normal file
2
test/suites/Makefile.am
Normal file
@@ -0,0 +1,2 @@
|
||||
SUBDIRS = api
|
||||
EXTRA_DIST = invalid invalid-strip invalid-unicode valid valid-strip
|
||||
22
test/suites/api/Makefile.am
Normal file
22
test/suites/api/Makefile.am
Normal file
@@ -0,0 +1,22 @@
|
||||
EXTRA_DIST = run
|
||||
|
||||
check_PROGRAMS = \
|
||||
test_array \
|
||||
test_equal \
|
||||
test_copy \
|
||||
test_load \
|
||||
test_simple \
|
||||
test_number \
|
||||
test_object
|
||||
|
||||
test_array_SOURCES = test_array.c util.h
|
||||
test_copy_SOURCES = test_copy.c util.h
|
||||
test_load_SOURCES = test_load.c util.h
|
||||
test_simple_SOURCES = test_simple.c util.h
|
||||
test_number_SOURCES = test_number.c util.h
|
||||
test_object_SOURCES = test_object.c util.h
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src
|
||||
AM_CFLAGS = -Wall -Werror
|
||||
LDFLAGS = -static # for speed and Valgrind
|
||||
LDADD = $(top_builddir)/src/libjansson.la
|
||||
88
test/suites/api/check-exports
Executable file
88
test/suites/api/check-exports
Executable file
@@ -0,0 +1,88 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This tests checks that the libjansson.so exports the correct
|
||||
# symbols.
|
||||
|
||||
# The list of symbols that the shared object should export
|
||||
sort >$test_log/exports <<EOF
|
||||
json_delete
|
||||
json_true
|
||||
json_false
|
||||
json_null
|
||||
json_string
|
||||
json_string_nocheck
|
||||
json_string_value
|
||||
json_string_set
|
||||
json_string_set_nocheck
|
||||
json_integer
|
||||
json_integer_value
|
||||
json_integer_set
|
||||
json_real
|
||||
json_real_value
|
||||
json_real_set
|
||||
json_number_value
|
||||
json_array
|
||||
json_array_size
|
||||
json_array_get
|
||||
json_array_set_new
|
||||
json_array_append_new
|
||||
json_array_insert_new
|
||||
json_array_remove
|
||||
json_array_clear
|
||||
json_array_extend
|
||||
json_object
|
||||
json_object_size
|
||||
json_object_get
|
||||
json_object_set_new
|
||||
json_object_set_new_nocheck
|
||||
json_object_del
|
||||
json_object_clear
|
||||
json_object_update
|
||||
json_object_iter
|
||||
json_object_iter_next
|
||||
json_object_iter_key
|
||||
json_object_iter_value
|
||||
json_dumps
|
||||
json_dumpf
|
||||
json_dump_file
|
||||
json_loads
|
||||
json_loadf
|
||||
json_load_file
|
||||
json_equal
|
||||
json_copy
|
||||
json_deep_copy
|
||||
EOF
|
||||
|
||||
# The list of functions are not exported in the library because they
|
||||
# are macros or static inline functions. This is only the make the
|
||||
# list complete, there are not used by the test.
|
||||
sort >$test_log/macros_or_inline <<EOF
|
||||
json_typeof
|
||||
json_incref
|
||||
json_decref
|
||||
json_is_object
|
||||
json_is_object
|
||||
json_is_array
|
||||
json_is_string
|
||||
json_is_integer
|
||||
json_is_real
|
||||
json_is_true
|
||||
json_is_false
|
||||
json_is_null
|
||||
json_is_number
|
||||
json_is_boolean
|
||||
json_array_set
|
||||
json_array_append
|
||||
json_array_insert
|
||||
json_object_set
|
||||
json_object_set_nocheck
|
||||
EOF
|
||||
|
||||
SOFILE="../src/.libs/libjansson.so"
|
||||
|
||||
nm -D $SOFILE | grep ' T ' | 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
|
||||
exit 1
|
||||
fi
|
||||
30
test/suites/api/run
Executable file
30
test/suites/api/run
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009 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.
|
||||
|
||||
is_test() {
|
||||
[ "${test_name%.c}" != "$test_name" ] && return 0
|
||||
[ -x $test_path -a ! -f $test_path.c ] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
run_test() {
|
||||
if [ -x $test_path ]; then
|
||||
test_log=$test_log $test_path >$test_log/stdout 2>$test_log/stderr
|
||||
else
|
||||
$test_runner $suite_builddir/${test_name%.c} \
|
||||
>$test_log/stdout \
|
||||
2>$test_log/stderr
|
||||
valgrind_check $test_log/stderr || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
show_error() {
|
||||
valgrind_show_error && return
|
||||
cat $test_log/stderr
|
||||
}
|
||||
|
||||
. $top_srcdir/test/scripts/run-tests.sh
|
||||
400
test/suites/api/test_array.c
Normal file
400
test/suites/api/test_array.c
Normal file
@@ -0,0 +1,400 @@
|
||||
/*
|
||||
* Copyright (c) 2009 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 "util.h"
|
||||
|
||||
static void test_misc(void)
|
||||
{
|
||||
json_t *array, *five, *seven, *value;
|
||||
int i;
|
||||
|
||||
array = json_array();
|
||||
five = json_integer(5);
|
||||
seven = json_integer(7);
|
||||
|
||||
if(!array)
|
||||
fail("unable to create array");
|
||||
if(!five || !seven)
|
||||
fail("unable to create integer");
|
||||
|
||||
if(json_array_size(array) != 0)
|
||||
fail("empty array has nonzero size");
|
||||
|
||||
if(!json_array_append(array, NULL))
|
||||
fail("able to append NULL");
|
||||
|
||||
if(json_array_append(array, five))
|
||||
fail("unable to append");
|
||||
|
||||
if(json_array_size(array) != 1)
|
||||
fail("wrong array size");
|
||||
|
||||
value = json_array_get(array, 0);
|
||||
if(!value)
|
||||
fail("unable to get item");
|
||||
if(value != five)
|
||||
fail("got wrong value");
|
||||
|
||||
if(json_array_append(array, seven))
|
||||
fail("unable to append value");
|
||||
|
||||
if(json_array_size(array) != 2)
|
||||
fail("wrong array size");
|
||||
|
||||
value = json_array_get(array, 1);
|
||||
if(!value)
|
||||
fail("unable to get item");
|
||||
if(value != seven)
|
||||
fail("got wrong value");
|
||||
|
||||
if(json_array_set(array, 0, seven))
|
||||
fail("unable to set value");
|
||||
|
||||
if(!json_array_set(array, 0, NULL))
|
||||
fail("able to set NULL");
|
||||
|
||||
if(json_array_size(array) != 2)
|
||||
fail("wrong array size");
|
||||
|
||||
value = json_array_get(array, 0);
|
||||
if(!value)
|
||||
fail("unable to get item");
|
||||
if(value != seven)
|
||||
fail("got wrong value");
|
||||
|
||||
if(json_array_get(array, 2) != NULL)
|
||||
fail("able to get value out of bounds");
|
||||
|
||||
if(!json_array_set(array, 2, seven))
|
||||
fail("able to set value out of bounds");
|
||||
|
||||
for(i = 2; i < 30; i++) {
|
||||
if(json_array_append(array, seven))
|
||||
fail("unable to append value");
|
||||
|
||||
if(json_array_size(array) != i + 1)
|
||||
fail("wrong array size");
|
||||
}
|
||||
|
||||
for(i = 0; i < 30; i++) {
|
||||
value = json_array_get(array, i);
|
||||
if(!value)
|
||||
fail("unable to get item");
|
||||
if(value != seven)
|
||||
fail("got wrong value");
|
||||
}
|
||||
|
||||
if(json_array_set_new(array, 15, json_integer(123)))
|
||||
fail("unable to set new value");
|
||||
|
||||
value = json_array_get(array, 15);
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 123)
|
||||
fail("json_array_set_new works incorrectly");
|
||||
|
||||
if(!json_array_set_new(array, 15, NULL))
|
||||
fail("able to set_new NULL value");
|
||||
|
||||
if(json_array_append_new(array, json_integer(321)))
|
||||
fail("unable to append new value");
|
||||
|
||||
value = json_array_get(array, json_array_size(array) - 1);
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 321)
|
||||
fail("json_array_append_new works incorrectly");
|
||||
|
||||
if(!json_array_append_new(array, NULL))
|
||||
fail("able to append_new NULL value");
|
||||
|
||||
json_decref(five);
|
||||
json_decref(seven);
|
||||
json_decref(array);
|
||||
}
|
||||
|
||||
static void test_insert(void)
|
||||
{
|
||||
json_t *array, *five, *seven, *eleven, *value;
|
||||
int i;
|
||||
|
||||
array = json_array();
|
||||
five = json_integer(5);
|
||||
seven = json_integer(7);
|
||||
eleven = json_integer(11);
|
||||
|
||||
if(!array)
|
||||
fail("unable to create array");
|
||||
if(!five || !seven || !eleven)
|
||||
fail("unable to create integer");
|
||||
|
||||
|
||||
if(!json_array_insert(array, 1, five))
|
||||
fail("able to insert value out of bounds");
|
||||
|
||||
|
||||
if(json_array_insert(array, 0, five))
|
||||
fail("unable to insert value in an empty array");
|
||||
|
||||
if(json_array_get(array, 0) != five)
|
||||
fail("json_array_insert works incorrectly");
|
||||
|
||||
if(json_array_size(array) != 1)
|
||||
fail("array size is invalid after insertion");
|
||||
|
||||
|
||||
if(json_array_insert(array, 1, seven))
|
||||
fail("unable to insert value at the end of an array");
|
||||
|
||||
if(json_array_get(array, 0) != five)
|
||||
fail("json_array_insert works incorrectly");
|
||||
|
||||
if(json_array_get(array, 1) != seven)
|
||||
fail("json_array_insert works incorrectly");
|
||||
|
||||
if(json_array_size(array) != 2)
|
||||
fail("array size is invalid after insertion");
|
||||
|
||||
|
||||
if(json_array_insert(array, 1, eleven))
|
||||
fail("unable to insert value in the middle of an array");
|
||||
|
||||
if(json_array_get(array, 0) != five)
|
||||
fail("json_array_insert works incorrectly");
|
||||
|
||||
if(json_array_get(array, 1) != eleven)
|
||||
fail("json_array_insert works incorrectly");
|
||||
|
||||
if(json_array_get(array, 2) != seven)
|
||||
fail("json_array_insert works incorrectly");
|
||||
|
||||
if(json_array_size(array) != 3)
|
||||
fail("array size is invalid after insertion");
|
||||
|
||||
|
||||
if(json_array_insert_new(array, 2, json_integer(123)))
|
||||
fail("unable to insert value in the middle of an array");
|
||||
|
||||
value = json_array_get(array, 2);
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 123)
|
||||
fail("json_array_insert_new works incorrectly");
|
||||
|
||||
if(json_array_size(array) != 4)
|
||||
fail("array size is invalid after insertion");
|
||||
|
||||
|
||||
for(i = 0; i < 20; i++) {
|
||||
if(json_array_insert(array, 0, seven))
|
||||
fail("unable to insert value at the begining of an array");
|
||||
}
|
||||
|
||||
for(i = 0; i < 20; i++) {
|
||||
if(json_array_get(array, i) != seven)
|
||||
fail("json_aray_insert works incorrectly");
|
||||
}
|
||||
|
||||
if(json_array_size(array) != 24)
|
||||
fail("array size is invalid after loop insertion");
|
||||
|
||||
json_decref(five);
|
||||
json_decref(seven);
|
||||
json_decref(eleven);
|
||||
json_decref(array);
|
||||
}
|
||||
|
||||
static void test_remove(void)
|
||||
{
|
||||
json_t *array, *five, *seven;
|
||||
|
||||
array = json_array();
|
||||
five = json_integer(5);
|
||||
seven = json_integer(7);
|
||||
|
||||
if(!array)
|
||||
fail("unable to create array");
|
||||
if(!five)
|
||||
fail("unable to create integer");
|
||||
if(!seven)
|
||||
fail("unable to create integer");
|
||||
|
||||
|
||||
if(!json_array_remove(array, 0))
|
||||
fail("able to remove an unexisting index");
|
||||
|
||||
|
||||
if(json_array_append(array, five))
|
||||
fail("unable to append");
|
||||
|
||||
if(!json_array_remove(array, 1))
|
||||
fail("able to remove an unexisting index");
|
||||
|
||||
if(json_array_remove(array, 0))
|
||||
fail("unable to remove");
|
||||
|
||||
if(json_array_size(array) != 0)
|
||||
fail("array size is invalid after removing");
|
||||
|
||||
|
||||
if(json_array_append(array, five) ||
|
||||
json_array_append(array, seven) ||
|
||||
json_array_append(array, five) ||
|
||||
json_array_append(array, seven))
|
||||
fail("unable to append");
|
||||
|
||||
if(json_array_remove(array, 2))
|
||||
fail("unable to remove");
|
||||
|
||||
if(json_array_size(array) != 3)
|
||||
fail("array size is invalid after removing");
|
||||
|
||||
if(json_array_get(array, 0) != five ||
|
||||
json_array_get(array, 1) != seven ||
|
||||
json_array_get(array, 2) != seven)
|
||||
fail("remove works incorrectly");
|
||||
|
||||
json_decref(five);
|
||||
json_decref(seven);
|
||||
json_decref(array);
|
||||
}
|
||||
|
||||
static void test_clear(void)
|
||||
{
|
||||
json_t *array, *five, *seven;
|
||||
int i;
|
||||
|
||||
array = json_array();
|
||||
five = json_integer(5);
|
||||
seven = json_integer(7);
|
||||
|
||||
if(!array)
|
||||
fail("unable to create array");
|
||||
if(!five || !seven)
|
||||
fail("unable to create integer");
|
||||
|
||||
for(i = 0; i < 10; i++) {
|
||||
if(json_array_append(array, five))
|
||||
fail("unable to append");
|
||||
}
|
||||
for(i = 0; i < 10; i++) {
|
||||
if(json_array_append(array, seven))
|
||||
fail("unable to append");
|
||||
}
|
||||
|
||||
if(json_array_size(array) != 20)
|
||||
fail("array size is invalid after appending");
|
||||
|
||||
if(json_array_clear(array))
|
||||
fail("unable to clear");
|
||||
|
||||
if(json_array_size(array) != 0)
|
||||
fail("array size is invalid after clearing");
|
||||
|
||||
json_decref(five);
|
||||
json_decref(seven);
|
||||
json_decref(array);
|
||||
}
|
||||
|
||||
static void test_extend(void)
|
||||
{
|
||||
json_t *array1, *array2, *five, *seven;
|
||||
int i;
|
||||
|
||||
array1 = json_array();
|
||||
array2 = json_array();
|
||||
five = json_integer(5);
|
||||
seven = json_integer(7);
|
||||
|
||||
if(!array1 || !array2)
|
||||
fail("unable to create array");
|
||||
if(!five || !seven)
|
||||
fail("unable to create integer");
|
||||
|
||||
for(i = 0; i < 10; i++) {
|
||||
if(json_array_append(array1, five))
|
||||
fail("unable to append");
|
||||
}
|
||||
for(i = 0; i < 10; i++) {
|
||||
if(json_array_append(array2, seven))
|
||||
fail("unable to append");
|
||||
}
|
||||
|
||||
if(json_array_size(array1) != 10 || json_array_size(array2) != 10)
|
||||
fail("array size is invalid after appending");
|
||||
|
||||
if(json_array_extend(array1, array2))
|
||||
fail("unable to extend");
|
||||
|
||||
for(i = 0; i < 10; i++) {
|
||||
if(json_array_get(array1, i) != five)
|
||||
fail("invalid array contents after extending");
|
||||
}
|
||||
for(i = 10; i < 20; i++) {
|
||||
if(json_array_get(array1, i) != seven)
|
||||
fail("invalid array contents after extending");
|
||||
}
|
||||
|
||||
json_decref(five);
|
||||
json_decref(seven);
|
||||
json_decref(array1);
|
||||
json_decref(array2);
|
||||
}
|
||||
|
||||
static void test_circular()
|
||||
{
|
||||
json_t *array1, *array2;
|
||||
|
||||
/* the simple cases are checked */
|
||||
|
||||
array1 = json_array();
|
||||
if(!array1)
|
||||
fail("unable to create array");
|
||||
|
||||
if(json_array_append(array1, array1) == 0)
|
||||
fail("able to append self");
|
||||
|
||||
if(json_array_insert(array1, 0, array1) == 0)
|
||||
fail("able to insert self");
|
||||
|
||||
if(json_array_append_new(array1, json_true()))
|
||||
fail("failed to append true");
|
||||
|
||||
if(json_array_set(array1, 0, array1) == 0)
|
||||
fail("able to set self");
|
||||
|
||||
json_decref(array1);
|
||||
|
||||
|
||||
/* create circular references */
|
||||
|
||||
array1 = json_array();
|
||||
array2 = json_array();
|
||||
if(!array1 || !array2)
|
||||
fail("unable to create array");
|
||||
|
||||
if(json_array_append(array1, array2) ||
|
||||
json_array_append(array2, array1))
|
||||
fail("unable to append");
|
||||
|
||||
/* circularity is detected when dumping */
|
||||
if(json_dumps(array1, 0) != NULL)
|
||||
fail("able to dump circulars");
|
||||
|
||||
/* decref twice to deal with the circular references */
|
||||
json_decref(array1);
|
||||
json_decref(array2);
|
||||
json_decref(array1);
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
test_misc();
|
||||
test_insert();
|
||||
test_remove();
|
||||
test_clear();
|
||||
test_extend();
|
||||
test_circular();
|
||||
|
||||
return 0;
|
||||
}
|
||||
319
test/suites/api/test_copy.c
Normal file
319
test/suites/api/test_copy.c
Normal file
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* Copyright (c) 2009 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 <string.h>
|
||||
#include <jansson.h>
|
||||
#include "util.h"
|
||||
|
||||
static void test_copy_simple(void)
|
||||
{
|
||||
json_t *value, *copy;
|
||||
|
||||
if(json_copy(NULL))
|
||||
fail("copying NULL doesn't return NULL");
|
||||
|
||||
/* true */
|
||||
value = json_true();
|
||||
copy = json_copy(value);
|
||||
if(value != copy)
|
||||
fail("copying true failed");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
|
||||
/* false */
|
||||
value = json_false();
|
||||
copy = json_copy(value);
|
||||
if(value != copy)
|
||||
fail("copying false failed");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
|
||||
/* null */
|
||||
value = json_null();
|
||||
copy = json_copy(value);
|
||||
if(value != copy)
|
||||
fail("copying null failed");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
|
||||
/* string */
|
||||
value = json_string("foo");
|
||||
if(!value)
|
||||
fail("unable to create a string");
|
||||
copy = json_copy(value);
|
||||
if(!copy)
|
||||
fail("unable to copy a string");
|
||||
if(copy == value)
|
||||
fail("copying a string doesn't copy");
|
||||
if(!json_equal(copy, value))
|
||||
fail("copying a string produces an inequal copy");
|
||||
if(value->refcount != 1 || copy->refcount != 1)
|
||||
fail("invalid refcounts");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
|
||||
/* integer */
|
||||
value = json_integer(543);
|
||||
if(!value)
|
||||
fail("unable to create an integer");
|
||||
copy = json_copy(value);
|
||||
if(!copy)
|
||||
fail("unable to copy an integer");
|
||||
if(copy == value)
|
||||
fail("copying an integer doesn't copy");
|
||||
if(!json_equal(copy, value))
|
||||
fail("copying an integer produces an inequal copy");
|
||||
if(value->refcount != 1 || copy->refcount != 1)
|
||||
fail("invalid refcounts");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
|
||||
/* real */
|
||||
value = json_real(123e9);
|
||||
if(!value)
|
||||
fail("unable to create a real");
|
||||
copy = json_copy(value);
|
||||
if(!copy)
|
||||
fail("unable to copy a real");
|
||||
if(copy == value)
|
||||
fail("copying a real doesn't copy");
|
||||
if(!json_equal(copy, value))
|
||||
fail("copying a real produces an inequal copy");
|
||||
if(value->refcount != 1 || copy->refcount != 1)
|
||||
fail("invalid refcounts");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
}
|
||||
|
||||
static void test_deep_copy_simple(void)
|
||||
{
|
||||
json_t *value, *copy;
|
||||
|
||||
if(json_deep_copy(NULL))
|
||||
fail("deep copying NULL doesn't return NULL");
|
||||
|
||||
/* true */
|
||||
value = json_true();
|
||||
copy = json_deep_copy(value);
|
||||
if(value != copy)
|
||||
fail("deep copying true failed");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
|
||||
/* false */
|
||||
value = json_false();
|
||||
copy = json_deep_copy(value);
|
||||
if(value != copy)
|
||||
fail("deep copying false failed");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
|
||||
/* null */
|
||||
value = json_null();
|
||||
copy = json_deep_copy(value);
|
||||
if(value != copy)
|
||||
fail("deep copying null failed");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
|
||||
/* string */
|
||||
value = json_string("foo");
|
||||
if(!value)
|
||||
fail("unable to create a string");
|
||||
copy = json_deep_copy(value);
|
||||
if(!copy)
|
||||
fail("unable to deep copy a string");
|
||||
if(copy == value)
|
||||
fail("deep copying a string doesn't copy");
|
||||
if(!json_equal(copy, value))
|
||||
fail("deep copying a string produces an inequal copy");
|
||||
if(value->refcount != 1 || copy->refcount != 1)
|
||||
fail("invalid refcounts");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
|
||||
/* integer */
|
||||
value = json_integer(543);
|
||||
if(!value)
|
||||
fail("unable to create an integer");
|
||||
copy = json_deep_copy(value);
|
||||
if(!copy)
|
||||
fail("unable to deep copy an integer");
|
||||
if(copy == value)
|
||||
fail("deep copying an integer doesn't copy");
|
||||
if(!json_equal(copy, value))
|
||||
fail("deep copying an integer produces an inequal copy");
|
||||
if(value->refcount != 1 || copy->refcount != 1)
|
||||
fail("invalid refcounts");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
|
||||
/* real */
|
||||
value = json_real(123e9);
|
||||
if(!value)
|
||||
fail("unable to create a real");
|
||||
copy = json_deep_copy(value);
|
||||
if(!copy)
|
||||
fail("unable to deep copy a real");
|
||||
if(copy == value)
|
||||
fail("deep copying a real doesn't copy");
|
||||
if(!json_equal(copy, value))
|
||||
fail("deep copying a real produces an inequal copy");
|
||||
if(value->refcount != 1 || copy->refcount != 1)
|
||||
fail("invalid refcounts");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
}
|
||||
|
||||
static void test_copy_array(void)
|
||||
{
|
||||
const char *json_array_text = "[1, \"foo\", 3.141592, {\"foo\": \"bar\"}]";
|
||||
|
||||
json_t *array, *copy;
|
||||
unsigned int i;
|
||||
|
||||
array = json_loads(json_array_text, NULL);
|
||||
if(!array)
|
||||
fail("unable to parse an array");
|
||||
|
||||
copy = json_copy(array);
|
||||
if(!copy)
|
||||
fail("unable to copy an array");
|
||||
if(copy == array)
|
||||
fail("copying an array doesn't copy");
|
||||
if(!json_equal(copy, array))
|
||||
fail("copying an array produces an inequal copy");
|
||||
|
||||
for(i = 0; i < json_array_size(copy); i++)
|
||||
{
|
||||
if(json_array_get(array, i) != json_array_get(copy, i))
|
||||
fail("copying an array modifies its elements");
|
||||
}
|
||||
|
||||
json_decref(array);
|
||||
json_decref(copy);
|
||||
}
|
||||
|
||||
static void test_deep_copy_array(void)
|
||||
{
|
||||
const char *json_array_text = "[1, \"foo\", 3.141592, {\"foo\": \"bar\"}]";
|
||||
|
||||
json_t *array, *copy;
|
||||
unsigned int i;
|
||||
|
||||
array = json_loads(json_array_text, NULL);
|
||||
if(!array)
|
||||
fail("unable to parse an array");
|
||||
|
||||
copy = json_deep_copy(array);
|
||||
if(!copy)
|
||||
fail("unable to deep copy an array");
|
||||
if(copy == array)
|
||||
fail("deep copying an array doesn't copy");
|
||||
if(!json_equal(copy, array))
|
||||
fail("deep copying an array produces an inequal copy");
|
||||
|
||||
for(i = 0; i < json_array_size(copy); i++)
|
||||
{
|
||||
if(json_array_get(array, i) == json_array_get(copy, i))
|
||||
fail("deep copying an array doesn't copy its elements");
|
||||
}
|
||||
|
||||
json_decref(array);
|
||||
json_decref(copy);
|
||||
}
|
||||
|
||||
static void test_copy_object(void)
|
||||
{
|
||||
const char *json_object_text =
|
||||
"{\"foo\": \"bar\", \"a\": 1, \"b\": 3.141592, \"c\": [1,2,3,4]}";
|
||||
|
||||
json_t *object, *copy;
|
||||
void *iter;
|
||||
|
||||
object = json_loads(json_object_text, NULL);
|
||||
if(!object)
|
||||
fail("unable to parse an object");
|
||||
|
||||
copy = json_copy(object);
|
||||
if(!copy)
|
||||
fail("unable to copy an object");
|
||||
if(copy == object)
|
||||
fail("copying an object doesn't copy");
|
||||
if(!json_equal(copy, object))
|
||||
fail("copying an object produces an inequal copy");
|
||||
|
||||
iter = json_object_iter(object);
|
||||
while(iter)
|
||||
{
|
||||
const char *key;
|
||||
json_t *value1, *value2;
|
||||
|
||||
key = json_object_iter_key(iter);
|
||||
value1 = json_object_iter_value(iter);
|
||||
value2 = json_object_get(copy, key);
|
||||
|
||||
if(value1 != value2)
|
||||
fail("deep copying an object modifies its items");
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
}
|
||||
|
||||
json_decref(object);
|
||||
json_decref(copy);
|
||||
}
|
||||
|
||||
static void test_deep_copy_object(void)
|
||||
{
|
||||
const char *json_object_text =
|
||||
"{\"foo\": \"bar\", \"a\": 1, \"b\": 3.141592, \"c\": [1,2,3,4]}";
|
||||
|
||||
json_t *object, *copy;
|
||||
void *iter;
|
||||
|
||||
object = json_loads(json_object_text, NULL);
|
||||
if(!object)
|
||||
fail("unable to parse an object");
|
||||
|
||||
copy = json_deep_copy(object);
|
||||
if(!copy)
|
||||
fail("unable to deep copy an object");
|
||||
if(copy == object)
|
||||
fail("deep copying an object doesn't copy");
|
||||
if(!json_equal(copy, object))
|
||||
fail("deep copying an object produces an inequal copy");
|
||||
|
||||
iter = json_object_iter(object);
|
||||
while(iter)
|
||||
{
|
||||
const char *key;
|
||||
json_t *value1, *value2;
|
||||
|
||||
key = json_object_iter_key(iter);
|
||||
value1 = json_object_iter_value(iter);
|
||||
value2 = json_object_get(copy, key);
|
||||
|
||||
if(value1 == value2)
|
||||
fail("deep copying an object doesn't copy its items");
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
}
|
||||
|
||||
json_decref(object);
|
||||
json_decref(copy);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_copy_simple();
|
||||
test_deep_copy_simple();
|
||||
test_copy_array();
|
||||
test_deep_copy_array();
|
||||
test_copy_object();
|
||||
test_deep_copy_object();
|
||||
return 0;
|
||||
}
|
||||
190
test/suites/api/test_equal.c
Normal file
190
test/suites/api/test_equal.c
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright (c) 2009 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 "util.h"
|
||||
|
||||
static void test_equal_simple()
|
||||
{
|
||||
json_t *value1, *value2;
|
||||
|
||||
if(json_equal(NULL, NULL))
|
||||
fail("json_equal fails for two NULLs");
|
||||
|
||||
value1 = json_true();
|
||||
if(json_equal(value1, NULL) || json_equal(NULL, value1))
|
||||
fail("json_equal fails for NULL");
|
||||
|
||||
/* this covers true, false and null as they are singletons */
|
||||
if(!json_equal(value1, value1))
|
||||
fail("identical objects are not equal");
|
||||
json_decref(value1);
|
||||
|
||||
/* integer */
|
||||
value1 = json_integer(1);
|
||||
value2 = json_integer(1);
|
||||
if(!value1 || !value2)
|
||||
fail("unable to create integers");
|
||||
if(!json_equal(value1, value2))
|
||||
fail("json_equal fails for two equal integers");
|
||||
json_decref(value2);
|
||||
|
||||
value2 = json_integer(2);
|
||||
if(!value2)
|
||||
fail("unable to create an integer");
|
||||
if(json_equal(value1, value2))
|
||||
fail("json_equal fails for two inequal integers");
|
||||
|
||||
json_decref(value1);
|
||||
json_decref(value2);
|
||||
|
||||
/* real */
|
||||
value1 = json_real(1.2);
|
||||
value2 = json_real(1.2);
|
||||
if(!value1 || !value2)
|
||||
fail("unable to create reals");
|
||||
if(!json_equal(value1, value2))
|
||||
fail("json_equal fails for two equal reals");
|
||||
json_decref(value2);
|
||||
|
||||
value2 = json_real(3.141592);
|
||||
if(!value2)
|
||||
fail("unable to create an real");
|
||||
if(json_equal(value1, value2))
|
||||
fail("json_equal fails for two inequal reals");
|
||||
|
||||
json_decref(value1);
|
||||
json_decref(value2);
|
||||
|
||||
/* string */
|
||||
value1 = json_string("foo");
|
||||
value2 = json_string("foo");
|
||||
if(!value1 || !value2)
|
||||
fail("unable to create strings");
|
||||
if(!json_equal(value1, value2))
|
||||
fail("json_equal fails for two equal strings");
|
||||
json_decref(value2);
|
||||
|
||||
value2 = json_string("bar");
|
||||
if(!value2)
|
||||
fail("unable to create an string");
|
||||
if(json_equal(value1, value2))
|
||||
fail("json_equal fails for two inequal strings");
|
||||
|
||||
json_decref(value1);
|
||||
json_decref(value2);
|
||||
}
|
||||
|
||||
static void test_equal_array()
|
||||
{
|
||||
json_t *array1, *array2;
|
||||
|
||||
array1 = json_array();
|
||||
array2 = json_array();
|
||||
if(!array1 || !array2)
|
||||
fail("unable to create arrays");
|
||||
|
||||
if(!json_equal(array1, array2))
|
||||
fail("json_equal fails for two empty arrays");
|
||||
|
||||
json_array_append_new(array1, json_integer(1));
|
||||
json_array_append_new(array2, json_integer(1));
|
||||
json_array_append_new(array1, json_string("foo"));
|
||||
json_array_append_new(array2, json_string("foo"));
|
||||
json_array_append_new(array1, json_integer(2));
|
||||
json_array_append_new(array2, json_integer(2));
|
||||
if(!json_equal(array1, array2))
|
||||
fail("json_equal fails for two equal arrays");
|
||||
|
||||
json_array_remove(array2, 2);
|
||||
if(json_equal(array1, array2))
|
||||
fail("json_equal fails for two inequal arrays");
|
||||
|
||||
json_array_append_new(array2, json_integer(3));
|
||||
if(json_equal(array1, array2))
|
||||
fail("json_equal fails for two inequal arrays");
|
||||
|
||||
json_decref(array1);
|
||||
json_decref(array2);
|
||||
}
|
||||
|
||||
static void test_equal_object()
|
||||
{
|
||||
json_t *object1, *object2;
|
||||
|
||||
object1 = json_object();
|
||||
object2 = json_object();
|
||||
if(!object1 || !object2)
|
||||
fail("unable to create objects");
|
||||
|
||||
if(!json_equal(object1, object2))
|
||||
fail("json_equal fails for two empty objects");
|
||||
|
||||
json_object_set_new(object1, "a", json_integer(1));
|
||||
json_object_set_new(object2, "a", json_integer(1));
|
||||
json_object_set_new(object1, "b", json_string("foo"));
|
||||
json_object_set_new(object2, "b", json_string("foo"));
|
||||
json_object_set_new(object1, "c", json_integer(2));
|
||||
json_object_set_new(object2, "c", json_integer(2));
|
||||
if(!json_equal(object1, object2))
|
||||
fail("json_equal fails for two equal objects");
|
||||
|
||||
json_object_del(object2, "c");
|
||||
if(json_equal(object1, object2))
|
||||
fail("json_equal fails for two inequal objects");
|
||||
|
||||
json_object_set_new(object2, "c", json_integer(3));
|
||||
if(json_equal(object1, object2))
|
||||
fail("json_equal fails for two inequal objects");
|
||||
|
||||
json_object_del(object2, "c");
|
||||
json_object_set_new(object2, "d", json_integer(2));
|
||||
if(json_equal(object1, object2))
|
||||
fail("json_equal fails for two inequal objects");
|
||||
|
||||
json_decref(object1);
|
||||
json_decref(object2);
|
||||
}
|
||||
|
||||
static void test_equal_complex()
|
||||
{
|
||||
json_t *value1, *value2;
|
||||
|
||||
const char *complex_json =
|
||||
"{"
|
||||
" \"integer\": 1, "
|
||||
" \"real\": 3.141592, "
|
||||
" \"string\": \"foobar\", "
|
||||
" \"true\": true, "
|
||||
" \"object\": {"
|
||||
" \"array-in-object\": [1,true,\"foo\",{}],"
|
||||
" \"object-in-object\": {\"foo\": \"bar\"}"
|
||||
" },"
|
||||
" \"array\": [\"foo\", false, null, 1.234]"
|
||||
"}";
|
||||
|
||||
value1 = json_loads(complex_json, NULL);
|
||||
value2 = json_loads(complex_json, NULL);
|
||||
if(!value1 || !value2)
|
||||
fail("unable to parse JSON");
|
||||
if(!json_equal(value1, value2))
|
||||
fail("json_equal fails for two inequal strings");
|
||||
|
||||
json_decref(value1);
|
||||
json_decref(value2);
|
||||
|
||||
/* TODO: There's no negative test case here */
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_equal_simple();
|
||||
test_equal_array();
|
||||
test_equal_object();
|
||||
test_equal_complex();
|
||||
return 0;
|
||||
}
|
||||
24
test/suites/api/test_load.c
Normal file
24
test/suites/api/test_load.c
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2009 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;
|
||||
|
||||
json = json_load_file("/path/to/nonexistent/file.json", &error);
|
||||
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");
|
||||
|
||||
return 0;
|
||||
}
|
||||
337
test/suites/api/test_object.c
Normal file
337
test/suites/api/test_object.c
Normal file
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* Copyright (c) 2009 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"
|
||||
|
||||
static void test_clear()
|
||||
{
|
||||
json_t *object, *ten;
|
||||
|
||||
object = json_object();
|
||||
ten = json_integer(10);
|
||||
|
||||
if(!object)
|
||||
fail("unable to create object");
|
||||
if(!ten)
|
||||
fail("unable to create integer");
|
||||
|
||||
if(json_object_set(object, "a", ten) ||
|
||||
json_object_set(object, "b", ten) ||
|
||||
json_object_set(object, "c", ten) ||
|
||||
json_object_set(object, "d", ten) ||
|
||||
json_object_set(object, "e", ten))
|
||||
fail("unable to set value");
|
||||
|
||||
if(json_object_size(object) != 5)
|
||||
fail("invalid size");
|
||||
|
||||
json_object_clear(object);
|
||||
|
||||
if(json_object_size(object) != 0)
|
||||
fail("invalid size after clear");
|
||||
|
||||
json_decref(ten);
|
||||
json_decref(object);
|
||||
}
|
||||
|
||||
static void test_update()
|
||||
{
|
||||
json_t *object, *other, *nine, *ten;
|
||||
|
||||
object = json_object();
|
||||
other = json_object();
|
||||
|
||||
nine = json_integer(9);
|
||||
ten = json_integer(10);
|
||||
|
||||
if(!object || !other)
|
||||
fail("unable to create object");
|
||||
if(!nine || !ten)
|
||||
fail("unable to create integer");
|
||||
|
||||
|
||||
/* update an empty object with an empty object */
|
||||
|
||||
if(json_object_update(object, other))
|
||||
fail("unable to update an emtpy object with an empty object");
|
||||
|
||||
if(json_object_size(object) != 0)
|
||||
fail("invalid size after update");
|
||||
|
||||
if(json_object_size(other) != 0)
|
||||
fail("invalid size for updater after update");
|
||||
|
||||
|
||||
/* update an empty object with a nonempty object */
|
||||
|
||||
if(json_object_set(other, "a", ten) ||
|
||||
json_object_set(other, "b", ten) ||
|
||||
json_object_set(other, "c", ten) ||
|
||||
json_object_set(other, "d", ten) ||
|
||||
json_object_set(other, "e", ten))
|
||||
fail("unable to set value");
|
||||
|
||||
if(json_object_update(object, other))
|
||||
fail("unable to update an empty object");
|
||||
|
||||
if(json_object_size(object) != 5)
|
||||
fail("invalid size after update");
|
||||
|
||||
if(json_object_get(object, "a") != ten ||
|
||||
json_object_get(object, "b") != ten ||
|
||||
json_object_get(object, "c") != ten ||
|
||||
json_object_get(object, "d") != ten ||
|
||||
json_object_get(object, "e") != ten)
|
||||
fail("update works incorrectly");
|
||||
|
||||
|
||||
/* perform the same update again */
|
||||
|
||||
if(json_object_update(object, other))
|
||||
fail("unable to update an empty object");
|
||||
|
||||
if(json_object_size(object) != 5)
|
||||
fail("invalid size after update");
|
||||
|
||||
if(json_object_get(object, "a") != ten ||
|
||||
json_object_get(object, "b") != ten ||
|
||||
json_object_get(object, "c") != ten ||
|
||||
json_object_get(object, "d") != ten ||
|
||||
json_object_get(object, "e") != ten)
|
||||
fail("update works incorrectly");
|
||||
|
||||
|
||||
/* update a nonempty object with a nonempty object with both old
|
||||
and new keys */
|
||||
|
||||
if(json_object_clear(other))
|
||||
fail("clear failed");
|
||||
|
||||
if(json_object_set(other, "a", nine) ||
|
||||
json_object_set(other, "b", nine) ||
|
||||
json_object_set(other, "f", nine) ||
|
||||
json_object_set(other, "g", nine) ||
|
||||
json_object_set(other, "h", nine))
|
||||
fail("unable to set value");
|
||||
|
||||
if(json_object_update(object, other))
|
||||
fail("unable to update a nonempty object");
|
||||
|
||||
if(json_object_size(object) != 8)
|
||||
fail("invalid size after update");
|
||||
|
||||
if(json_object_get(object, "a") != nine ||
|
||||
json_object_get(object, "b") != nine ||
|
||||
json_object_get(object, "f") != nine ||
|
||||
json_object_get(object, "g") != nine ||
|
||||
json_object_get(object, "h") != nine)
|
||||
fail("update works incorrectly");
|
||||
|
||||
json_decref(nine);
|
||||
json_decref(ten);
|
||||
json_decref(other);
|
||||
json_decref(object);
|
||||
}
|
||||
|
||||
static void test_circular()
|
||||
{
|
||||
json_t *object1, *object2;
|
||||
|
||||
object1 = json_object();
|
||||
object2 = json_object();
|
||||
if(!object1 || !object2)
|
||||
fail("unable to create object");
|
||||
|
||||
/* the simple case is checked */
|
||||
if(json_object_set(object1, "a", object1) == 0)
|
||||
fail("able to set self");
|
||||
|
||||
/* create circular references */
|
||||
if(json_object_set(object1, "a", object2) ||
|
||||
json_object_set(object2, "a", object1))
|
||||
fail("unable to set value");
|
||||
|
||||
/* circularity is detected when dumping */
|
||||
if(json_dumps(object1, 0) != NULL)
|
||||
fail("able to dump circulars");
|
||||
|
||||
/* decref twice to deal with the circular references */
|
||||
json_decref(object1);
|
||||
json_decref(object2);
|
||||
json_decref(object1);
|
||||
}
|
||||
|
||||
static void test_set_nocheck()
|
||||
{
|
||||
json_t *object, *string;
|
||||
|
||||
object = json_object();
|
||||
string = json_string("bar");
|
||||
|
||||
if(!object)
|
||||
fail("unable to create object");
|
||||
if(!string)
|
||||
fail("unable to create string");
|
||||
|
||||
if(json_object_set_nocheck(object, "foo", string))
|
||||
fail("json_object_set_nocheck failed");
|
||||
if(json_object_get(object, "foo") != string)
|
||||
fail("json_object_get after json_object_set_nocheck failed");
|
||||
|
||||
/* invalid UTF-8 in key */
|
||||
if(json_object_set_nocheck(object, "a\xefz", string))
|
||||
fail("json_object_set_nocheck failed for invalid UTF-8");
|
||||
if(json_object_get(object, "a\xefz") != string)
|
||||
fail("json_object_get after json_object_set_nocheck failed");
|
||||
|
||||
if(json_object_set_new_nocheck(object, "bax", json_integer(123)))
|
||||
fail("json_object_set_new_nocheck failed");
|
||||
if(json_integer_value(json_object_get(object, "bax")) != 123)
|
||||
fail("json_object_get after json_object_set_new_nocheck failed");
|
||||
|
||||
/* invalid UTF-8 in key */
|
||||
if(json_object_set_new_nocheck(object, "asdf\xfe", json_integer(321)))
|
||||
fail("json_object_set_new_nocheck failed for invalid UTF-8");
|
||||
if(json_integer_value(json_object_get(object, "asdf\xfe")) != 321)
|
||||
fail("json_object_get after json_object_set_new_nocheck failed");
|
||||
|
||||
json_decref(string);
|
||||
json_decref(object);
|
||||
}
|
||||
|
||||
static void test_misc()
|
||||
{
|
||||
json_t *object, *string, *other_string, *value;
|
||||
void *iter;
|
||||
|
||||
object = json_object();
|
||||
string = json_string("test");
|
||||
other_string = json_string("other");
|
||||
|
||||
if(!object)
|
||||
fail("unable to create object");
|
||||
if(!string || !other_string)
|
||||
fail("unable to create string");
|
||||
|
||||
if(json_object_get(object, "a"))
|
||||
fail("value for nonexisting key");
|
||||
|
||||
if(json_object_set(object, "a", string))
|
||||
fail("unable to set value");
|
||||
|
||||
if(!json_object_set(object, NULL, string))
|
||||
fail("able to set NULL key");
|
||||
|
||||
if(!json_object_set(object, "a", NULL))
|
||||
fail("able to set NULL value");
|
||||
|
||||
iter = json_object_iter(object);
|
||||
if(!iter)
|
||||
fail("unable to get iterator");
|
||||
|
||||
if(strcmp(json_object_iter_key(iter), "a"))
|
||||
fail("iterating failed: wrong key");
|
||||
if(json_object_iter_value(iter) != string)
|
||||
fail("iterating failed: wrong value");
|
||||
if(json_object_iter_next(object, iter) != NULL)
|
||||
fail("able to iterate over the end");
|
||||
|
||||
/* invalid UTF-8 in key */
|
||||
if(!json_object_set(object, "a\xefz", string))
|
||||
fail("able to set invalid unicode key");
|
||||
|
||||
value = json_object_get(object, "a");
|
||||
if(!value)
|
||||
fail("no value for existing key");
|
||||
if(value != string)
|
||||
fail("got different value than what was added");
|
||||
|
||||
/* "a", "lp" and "px" collide in a five-bucket hashtable */
|
||||
if(json_object_set(object, "b", string) ||
|
||||
json_object_set(object, "lp", string) ||
|
||||
json_object_set(object, "px", string))
|
||||
fail("unable to set value");
|
||||
|
||||
value = json_object_get(object, "a");
|
||||
if(!value)
|
||||
fail("no value for existing key");
|
||||
if(value != string)
|
||||
fail("got different value than what was added");
|
||||
|
||||
if(json_object_set(object, "a", other_string))
|
||||
fail("unable to replace an existing key");
|
||||
|
||||
value = json_object_get(object, "a");
|
||||
if(!value)
|
||||
fail("no value for existing key");
|
||||
if(value != other_string)
|
||||
fail("got different value than what was set");
|
||||
|
||||
if(!json_object_del(object, "nonexisting"))
|
||||
fail("able to delete a nonexisting key");
|
||||
|
||||
if(json_object_del(object, "px"))
|
||||
fail("unable to delete an existing key");
|
||||
|
||||
if(json_object_del(object, "a"))
|
||||
fail("unable to delete an existing key");
|
||||
|
||||
if(json_object_del(object, "lp"))
|
||||
fail("unable to delete an existing key");
|
||||
|
||||
|
||||
/* add many keys to initiate rehashing */
|
||||
|
||||
if(json_object_set(object, "a", string))
|
||||
fail("unable to set value");
|
||||
|
||||
if(json_object_set(object, "lp", string))
|
||||
fail("unable to set value");
|
||||
|
||||
if(json_object_set(object, "px", string))
|
||||
fail("unable to set value");
|
||||
|
||||
if(json_object_set(object, "c", string))
|
||||
fail("unable to set value");
|
||||
|
||||
if(json_object_set(object, "d", string))
|
||||
fail("unable to set value");
|
||||
|
||||
if(json_object_set(object, "e", string))
|
||||
fail("unable to set value");
|
||||
|
||||
|
||||
if(json_object_set_new(object, "foo", json_integer(123)))
|
||||
fail("unable to set new value");
|
||||
|
||||
value = json_object_get(object, "foo");
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 123)
|
||||
fail("json_object_set_new works incorrectly");
|
||||
|
||||
if(!json_object_set_new(object, NULL, json_integer(432)))
|
||||
fail("able to set_new NULL key");
|
||||
|
||||
if(!json_object_set_new(object, "foo", NULL))
|
||||
fail("able to set_new NULL value");
|
||||
|
||||
json_decref(string);
|
||||
json_decref(other_string);
|
||||
json_decref(object);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_misc();
|
||||
test_clear();
|
||||
test_update();
|
||||
test_circular();
|
||||
test_set_nocheck();
|
||||
|
||||
return 0;
|
||||
}
|
||||
154
test/suites/api/test_simple.c
Normal file
154
test/suites/api/test_simple.c
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (c) 2009 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 <string.h>
|
||||
#include <jansson.h>
|
||||
#include "util.h"
|
||||
|
||||
/* Call the simple functions not covered by other tests of the public API */
|
||||
int main()
|
||||
{
|
||||
json_t *value;
|
||||
|
||||
value = json_integer(1);
|
||||
if(json_typeof(value) != JSON_INTEGER)
|
||||
fail("json_typeof failed");
|
||||
|
||||
if(json_is_object(value))
|
||||
fail("json_is_object failed");
|
||||
|
||||
if(json_is_array(value))
|
||||
fail("json_is_array failed");
|
||||
|
||||
if(json_is_string(value))
|
||||
fail("json_is_string failed");
|
||||
|
||||
if(!json_is_integer(value))
|
||||
fail("json_is_integer failed");
|
||||
|
||||
if(json_is_real(value))
|
||||
fail("json_is_real failed");
|
||||
|
||||
if(!json_is_number(value))
|
||||
fail("json_is_number failed");
|
||||
|
||||
if(json_is_true(value))
|
||||
fail("json_is_true failed");
|
||||
|
||||
if(json_is_false(value))
|
||||
fail("json_is_false failed");
|
||||
|
||||
if(json_is_boolean(value))
|
||||
fail("json_is_boolean failed");
|
||||
|
||||
if(json_is_null(value))
|
||||
fail("json_is_null failed");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
|
||||
value = json_string("foo");
|
||||
if(!value)
|
||||
fail("json_string failed");
|
||||
if(strcmp(json_string_value(value), "foo"))
|
||||
fail("invalid string value");
|
||||
|
||||
if(json_string_set(value, "bar"))
|
||||
fail("json_string_set failed");
|
||||
if(strcmp(json_string_value(value), "bar"))
|
||||
fail("invalid string value");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
value = json_string(NULL);
|
||||
if(value)
|
||||
fail("json_string(NULL) failed");
|
||||
|
||||
/* invalid UTF-8 */
|
||||
value = json_string("a\xefz");
|
||||
if(value)
|
||||
fail("json_string(<invalid utf-8>) failed");
|
||||
|
||||
value = json_string_nocheck("foo");
|
||||
if(!value)
|
||||
fail("json_string_nocheck failed");
|
||||
if(strcmp(json_string_value(value), "foo"))
|
||||
fail("invalid string value");
|
||||
|
||||
if(json_string_set_nocheck(value, "bar"))
|
||||
fail("json_string_set_nocheck failed");
|
||||
if(strcmp(json_string_value(value), "bar"))
|
||||
fail("invalid string value");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
/* invalid UTF-8 */
|
||||
value = json_string_nocheck("qu\xff");
|
||||
if(!value)
|
||||
fail("json_string_nocheck failed");
|
||||
if(strcmp(json_string_value(value), "qu\xff"))
|
||||
fail("invalid string value");
|
||||
|
||||
if(json_string_set_nocheck(value, "\xfd\xfe\xff"))
|
||||
fail("json_string_set_nocheck failed");
|
||||
if(strcmp(json_string_value(value), "\xfd\xfe\xff"))
|
||||
fail("invalid string value");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
|
||||
value = json_integer(123);
|
||||
if(!value)
|
||||
fail("json_integer failed");
|
||||
if(json_integer_value(value) != 123)
|
||||
fail("invalid integer value");
|
||||
if(json_number_value(value) != 123.0)
|
||||
fail("invalid number value");
|
||||
|
||||
if(json_integer_set(value, 321))
|
||||
fail("json_integer_set failed");
|
||||
if(json_integer_value(value) != 321)
|
||||
fail("invalid integer value");
|
||||
if(json_number_value(value) != 321.0)
|
||||
fail("invalid number value");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
value = json_real(123.123);
|
||||
if(!value)
|
||||
fail("json_real failed");
|
||||
if(json_real_value(value) != 123.123)
|
||||
fail("invalid integer value");
|
||||
if(json_number_value(value) != 123.123)
|
||||
fail("invalid number value");
|
||||
|
||||
if(json_real_set(value, 321.321))
|
||||
fail("json_real_set failed");
|
||||
if(json_real_value(value) != 321.321)
|
||||
fail("invalid real value");
|
||||
if(json_number_value(value) != 321.321)
|
||||
fail("invalid number value");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
value = json_true();
|
||||
if(!value)
|
||||
fail("json_true failed");
|
||||
json_decref(value);
|
||||
|
||||
value = json_false();
|
||||
if(!value)
|
||||
fail("json_false failed");
|
||||
json_decref(value);
|
||||
|
||||
value = json_null();
|
||||
if(!value)
|
||||
fail("json_null failed");
|
||||
json_decref(value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -8,10 +8,13 @@
|
||||
#ifndef TESTPROGS_UTIL_H
|
||||
#define TESTPROGS_UTIL_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define fail(msg) \
|
||||
do { \
|
||||
fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, msg); \
|
||||
return 1; \
|
||||
fprintf(stderr, "%s:%s:%d: %s\n", \
|
||||
__FILE__, __FUNCTION__, __LINE__, msg); \
|
||||
exit(1); \
|
||||
} while(0)
|
||||
|
||||
#endif
|
||||
1
test/suites/encoding-flags/array/input
Normal file
1
test/suites/encoding-flags/array/input
Normal file
@@ -0,0 +1 @@
|
||||
[1, 2]
|
||||
1
test/suites/encoding-flags/array/output
Normal file
1
test/suites/encoding-flags/array/output
Normal file
@@ -0,0 +1 @@
|
||||
[1, 2]
|
||||
1
test/suites/encoding-flags/compact-array/env
Normal file
1
test/suites/encoding-flags/compact-array/env
Normal file
@@ -0,0 +1 @@
|
||||
export JSON_COMPACT=1
|
||||
1
test/suites/encoding-flags/compact-array/input
Normal file
1
test/suites/encoding-flags/compact-array/input
Normal file
@@ -0,0 +1 @@
|
||||
[1, 2]
|
||||
1
test/suites/encoding-flags/compact-array/output
Normal file
1
test/suites/encoding-flags/compact-array/output
Normal file
@@ -0,0 +1 @@
|
||||
[1,2]
|
||||
1
test/suites/encoding-flags/compact-object/env
Normal file
1
test/suites/encoding-flags/compact-object/env
Normal file
@@ -0,0 +1 @@
|
||||
export JSON_COMPACT=1
|
||||
1
test/suites/encoding-flags/compact-object/input
Normal file
1
test/suites/encoding-flags/compact-object/input
Normal file
@@ -0,0 +1 @@
|
||||
{"a": 1, "b": 2}
|
||||
1
test/suites/encoding-flags/compact-object/output
Normal file
1
test/suites/encoding-flags/compact-object/output
Normal file
@@ -0,0 +1 @@
|
||||
{"a":1,"b":2}
|
||||
1
test/suites/encoding-flags/ensure-ascii/env
Normal file
1
test/suites/encoding-flags/ensure-ascii/env
Normal file
@@ -0,0 +1 @@
|
||||
export JSON_ENSURE_ASCII=1
|
||||
8
test/suites/encoding-flags/ensure-ascii/input
Normal file
8
test/suites/encoding-flags/ensure-ascii/input
Normal file
@@ -0,0 +1,8 @@
|
||||
[
|
||||
"foo",
|
||||
"å ä ö",
|
||||
"foo åä",
|
||||
"åä foo",
|
||||
"å foo ä",
|
||||
"clef g: 𝄞"
|
||||
]
|
||||
1
test/suites/encoding-flags/ensure-ascii/output
Normal file
1
test/suites/encoding-flags/ensure-ascii/output
Normal file
@@ -0,0 +1 @@
|
||||
["foo", "\u00e5 \u00e4 \u00f6", "foo \u00e5\u00e4", "\u00e5\u00e4 foo", "\u00e5 foo \u00e4", "clef g: \ud834\udd1e"]
|
||||
1
test/suites/encoding-flags/indent-array/env
Normal file
1
test/suites/encoding-flags/indent-array/env
Normal file
@@ -0,0 +1 @@
|
||||
export JSON_INDENT=4
|
||||
1
test/suites/encoding-flags/indent-array/input
Normal file
1
test/suites/encoding-flags/indent-array/input
Normal file
@@ -0,0 +1 @@
|
||||
[1, 2]
|
||||
4
test/suites/encoding-flags/indent-array/output
Normal file
4
test/suites/encoding-flags/indent-array/output
Normal file
@@ -0,0 +1,4 @@
|
||||
[
|
||||
1,
|
||||
2
|
||||
]
|
||||
2
test/suites/encoding-flags/indent-compact-array/env
Normal file
2
test/suites/encoding-flags/indent-compact-array/env
Normal file
@@ -0,0 +1,2 @@
|
||||
export JSON_INDENT=4
|
||||
export JSON_COMPACT=1
|
||||
1
test/suites/encoding-flags/indent-compact-array/input
Normal file
1
test/suites/encoding-flags/indent-compact-array/input
Normal file
@@ -0,0 +1 @@
|
||||
[1, 2]
|
||||
4
test/suites/encoding-flags/indent-compact-array/output
Normal file
4
test/suites/encoding-flags/indent-compact-array/output
Normal file
@@ -0,0 +1,4 @@
|
||||
[
|
||||
1,
|
||||
2
|
||||
]
|
||||
2
test/suites/encoding-flags/indent-compact-object/env
Normal file
2
test/suites/encoding-flags/indent-compact-object/env
Normal file
@@ -0,0 +1,2 @@
|
||||
export JSON_INDENT=4
|
||||
export JSON_COMPACT=1
|
||||
1
test/suites/encoding-flags/indent-compact-object/input
Normal file
1
test/suites/encoding-flags/indent-compact-object/input
Normal file
@@ -0,0 +1 @@
|
||||
{"a": 1, "b": 2}
|
||||
4
test/suites/encoding-flags/indent-compact-object/output
Normal file
4
test/suites/encoding-flags/indent-compact-object/output
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"a":1,
|
||||
"b":2
|
||||
}
|
||||
1
test/suites/encoding-flags/indent-object/env
Normal file
1
test/suites/encoding-flags/indent-object/env
Normal file
@@ -0,0 +1 @@
|
||||
export JSON_INDENT=4
|
||||
1
test/suites/encoding-flags/indent-object/input
Normal file
1
test/suites/encoding-flags/indent-object/input
Normal file
@@ -0,0 +1 @@
|
||||
{"a": 1, "b": 2}
|
||||
4
test/suites/encoding-flags/indent-object/output
Normal file
4
test/suites/encoding-flags/indent-object/output
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"a": 1,
|
||||
"b": 2
|
||||
}
|
||||
1
test/suites/encoding-flags/object/input
Normal file
1
test/suites/encoding-flags/object/input
Normal file
@@ -0,0 +1 @@
|
||||
{"a": 1, "b": 2}
|
||||
1
test/suites/encoding-flags/object/output
Normal file
1
test/suites/encoding-flags/object/output
Normal file
@@ -0,0 +1 @@
|
||||
{"a": 1, "b": 2}
|
||||
32
test/suites/encoding-flags/run
Executable file
32
test/suites/encoding-flags/run
Executable file
@@ -0,0 +1,32 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009 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.
|
||||
|
||||
is_test() {
|
||||
test -d $test_path
|
||||
}
|
||||
|
||||
run_test() {
|
||||
(
|
||||
if [ -f $test_path/env ]; then
|
||||
. $test_path/env
|
||||
fi
|
||||
$json_process <$test_path/input >$test_log/stdout 2>$test_log/stderr
|
||||
)
|
||||
valgrind_check $test_log/stderr || return 1
|
||||
cmp -s $test_path/output $test_log/stdout
|
||||
}
|
||||
|
||||
show_error() {
|
||||
valgrind_show_error && return
|
||||
|
||||
echo "EXPECTED OUTPUT:"
|
||||
nl -bn $test_path/output
|
||||
echo "ACTUAL OUTPUT:"
|
||||
nl -bn $test_log/stdout
|
||||
}
|
||||
|
||||
. $top_srcdir/test/scripts/run-tests.sh
|
||||
1
test/suites/encoding-flags/sort-keys/env
Normal file
1
test/suites/encoding-flags/sort-keys/env
Normal file
@@ -0,0 +1 @@
|
||||
export JSON_SORT_KEYS=1
|
||||
1
test/suites/encoding-flags/sort-keys/input
Normal file
1
test/suites/encoding-flags/sort-keys/input
Normal file
@@ -0,0 +1 @@
|
||||
{"foo": 1, "bar": 2, "baz": 3, "quux": 4}
|
||||
1
test/suites/encoding-flags/sort-keys/output
Normal file
1
test/suites/encoding-flags/sort-keys/output
Normal file
@@ -0,0 +1 @@
|
||||
{"bar": 2, "baz": 3, "foo": 1, "quux": 4}
|
||||
2
test/suites/invalid-strip/apostrophe/error
Normal file
2
test/suites/invalid-strip/apostrophe/error
Normal file
@@ -0,0 +1,2 @@
|
||||
1
|
||||
invalid token near '''
|
||||
1
test/suites/invalid-strip/apostrophe/input
Normal file
1
test/suites/invalid-strip/apostrophe/input
Normal file
@@ -0,0 +1 @@
|
||||
['
|
||||
2
test/suites/invalid-strip/ascii-unicode-identifier/error
Normal file
2
test/suites/invalid-strip/ascii-unicode-identifier/error
Normal file
@@ -0,0 +1,2 @@
|
||||
1
|
||||
'[' or '{' expected near 'a'
|
||||
1
test/suites/invalid-strip/ascii-unicode-identifier/input
Normal file
1
test/suites/invalid-strip/ascii-unicode-identifier/input
Normal file
@@ -0,0 +1 @@
|
||||
aå
|
||||
2
test/suites/invalid-strip/brace-comma/error
Normal file
2
test/suites/invalid-strip/brace-comma/error
Normal file
@@ -0,0 +1,2 @@
|
||||
1
|
||||
string or '}' expected near ','
|
||||
1
test/suites/invalid-strip/brace-comma/input
Normal file
1
test/suites/invalid-strip/brace-comma/input
Normal file
@@ -0,0 +1 @@
|
||||
{,
|
||||
2
test/suites/invalid-strip/bracket-comma/error
Normal file
2
test/suites/invalid-strip/bracket-comma/error
Normal file
@@ -0,0 +1,2 @@
|
||||
1
|
||||
unexpected token near ','
|
||||
1
test/suites/invalid-strip/bracket-comma/input
Normal file
1
test/suites/invalid-strip/bracket-comma/input
Normal file
@@ -0,0 +1 @@
|
||||
[,
|
||||
2
test/suites/invalid-strip/bracket-one-comma/error
Normal file
2
test/suites/invalid-strip/bracket-one-comma/error
Normal file
@@ -0,0 +1,2 @@
|
||||
1
|
||||
']' expected near end of file
|
||||
1
test/suites/invalid-strip/bracket-one-comma/input
Normal file
1
test/suites/invalid-strip/bracket-one-comma/input
Normal file
@@ -0,0 +1 @@
|
||||
[1,
|
||||
2
test/suites/invalid-strip/empty/error
Normal file
2
test/suites/invalid-strip/empty/error
Normal file
@@ -0,0 +1,2 @@
|
||||
1
|
||||
'[' or '{' expected near end of file
|
||||
0
test/suites/invalid-strip/empty/input
Normal file
0
test/suites/invalid-strip/empty/input
Normal file
2
test/suites/invalid-strip/extra-comma-in-array/error
Normal file
2
test/suites/invalid-strip/extra-comma-in-array/error
Normal file
@@ -0,0 +1,2 @@
|
||||
1
|
||||
unexpected token near ']'
|
||||
1
test/suites/invalid-strip/extra-comma-in-array/input
Normal file
1
test/suites/invalid-strip/extra-comma-in-array/input
Normal file
@@ -0,0 +1 @@
|
||||
[1,]
|
||||
@@ -0,0 +1,2 @@
|
||||
6
|
||||
unexpected token near ']'
|
||||
@@ -0,0 +1,6 @@
|
||||
[1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
]
|
||||
2
test/suites/invalid-strip/garbage-after-newline/error
Normal file
2
test/suites/invalid-strip/garbage-after-newline/error
Normal file
@@ -0,0 +1,2 @@
|
||||
2
|
||||
end of file expected near 'foo'
|
||||
2
test/suites/invalid-strip/garbage-after-newline/input
Normal file
2
test/suites/invalid-strip/garbage-after-newline/input
Normal file
@@ -0,0 +1,2 @@
|
||||
[1,2,3]
|
||||
foo
|
||||
2
test/suites/invalid-strip/garbage-at-the-end/error
Normal file
2
test/suites/invalid-strip/garbage-at-the-end/error
Normal file
@@ -0,0 +1,2 @@
|
||||
1
|
||||
end of file expected near 'foo'
|
||||
1
test/suites/invalid-strip/garbage-at-the-end/input
Normal file
1
test/suites/invalid-strip/garbage-at-the-end/input
Normal file
@@ -0,0 +1 @@
|
||||
[1,2,3]foo
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user