93 Commits
v1.0.3 ... v1.2

Author SHA1 Message Date
Petri Lehtinen
8c2ca3fae6 jansson 1.2 2010-01-21 21:49:10 +02:00
Petri Lehtinen
2ae279e0d4 test/run-suites: Be less picky when searching for tests
This is to better catch distribution errors. It's easier to notice
that run-tests fails than to notice that one of many test suites is
silently skipped.
2010-01-21 21:49:04 +02:00
Petri Lehtinen
4c6cb6afd1 Distribute some missing files 2010-01-21 21:48:31 +02:00
Petri Lehtinen
78594e9bd3 Remove CHANGES preprocessing, as it didn't work with VPATH builds
The problem is that Sphinx can only read input files from a single
directory. In VPATH builds, the source and build trees are separate,
and the changes.rst went into the build tree.

This patch solves the issue by using cfunc as the Sphinx default role.
2010-01-21 20:53:05 +02:00
Petri Lehtinen
e921e63b54 CHANGES: Update for v1.2 2010-01-19 21:19:37 +02:00
Petri Lehtinen
f9febb64c5 Merge branch '1.1'
Conflicts:
	Makefile.am
2010-01-17 13:57:20 +02:00
Petri Lehtinen
b76ee75aad doc: Convert CHANGES to reStructuredText and add it to HTML docs
CHANGES is preprocessed to convert json_*() function names to Sphinx
:cfunc: cross references. This is to keep CHANGES more readable in
both plain text and HTML.
2010-01-14 22:03:48 +02:00
Petri Lehtinen
b077d7988e Update documentation
* Python is no longer required to run the tests
* Mention pkg-config support
* Fix some errors
2010-01-12 21:29:16 +02:00
Petri Lehtinen
7f8684828d Fix memory leaks in json_equal() tests 2010-01-10 21:02:08 +02:00
Petri Lehtinen
93ac06c902 Fix memory leaks in json_*_deep_copy() 2010-01-10 21:01:07 +02:00
Petri Lehtinen
b21f07b35c Enable Valgrind support in the API suite
It was accidentally left out when the test system was refactored.
2010-01-10 16:18:46 +02:00
Petri Lehtinen
508873de9b Use _nocheck functions internally for speed
There are some places where we copy a string from an existing JSON
value. In these cases the string has already been checked for valid
UTF-8.
2010-01-10 14:39:11 +02:00
Petri Lehtinen
aeb5b481c9 Add pkg-config support
Thanks to Sean Middleditch for contributing.
2010-01-07 19:30:20 +02:00
Petri Lehtinen
9db34dc31a Add functions for shallow and deep copying JSON values 2009-12-31 18:50:39 +02:00
Petri Lehtinen
95a468cebb Add equality test for JSON values 2009-12-31 18:50:36 +02:00
Petri Lehtinen
22173c1d8b Add check-exports test 2009-12-23 22:29:59 +02:00
Petri Lehtinen
dd2fe1ebe8 Add _nocheck functions
Added functions are:

* json_string_nocheck()
* json_string_set_nocheck()
* json_object_set_nocheck()
* json_object_set_new_nocheck()

These functions don't check that their string argument is valid UTF-8,
but assume that the user has already performed the check.
2009-12-23 22:28:24 +02:00
Petri Lehtinen
6637b976ed Merge branch '1.1' 2009-12-21 14:13:05 +02:00
Petri Lehtinen
f5202bedef Remove const qualifier from the json_t parameter in json_*_set() functions.
It's incorrect as these functions modify the value.
2009-12-21 14:12:06 +02:00
Petri Lehtinen
2db2f2cfb6 Fix tests for real
The tests were broken by the "%.17g" fix that was merged from the 1.1
branch.
2009-12-21 12:52:29 +02:00
Petri Lehtinen
e7a5dc58e6 Merge branch '1.1'
Conflicts:
	configure.ac
	doc/conf.py
2009-12-21 12:52:25 +02:00
Petri Lehtinen
3889af476b Enhance tests
* Now that JSON_SORT_KEYS is implemented, take it into use with the
  valid and valid-strip suites. This is to ensure that the tests
  remain valid even if the string hash function is changed in the
  future.

* Remove test_dump API test. Instead, implement the same tests more
  elegantly in the encoding-flags suite.
2009-12-21 12:50:49 +02:00
Petri Lehtinen
34fb97998c jansson 1.1.3 2009-12-18 21:43:12 +02:00
Petri Lehtinen
ec96cbf016 Encode reals correctly
This patch changes the sprintf format from "%0.17f" to "%.17g", as the
f format specifier doesn't print the exponent at all. This caused
losing precision in all but the most simple cases.

Because the g specifier doesn't print the decimal fraction or exponent
if they're not needed, a ".0" has to be appended by hand in these
cases. Otherwise the value's type changes from real to integer when
decoding again.

Thanks to Philip Grandinetti for reporting this issue.
2009-12-18 00:05:06 +02:00
Petri Lehtinen
19a606d736 Implement JSON_SORT_KEYS encoding flag
With this flag, the objects are sorted by key when encoding.
2009-12-16 23:12:39 +02:00
Petri Lehtinen
3add1cf361 Refactor the test system 2009-12-16 22:45:29 +02:00
Petri Lehtinen
50031440a3 Implement JSON_ENSURE_ASCII encoding flag
With this flag, all Unicode characters outside the ASCII range are
escaped.
2009-12-05 22:55:30 +02:00
Petri Lehtinen
d67aeb9739 Use int32_t instead of plain int with Unicode code points
On some architectures, int just isn't big enough to hold all Unicode
code points.
2009-12-02 23:53:54 +02:00
Petri Lehtinen
7c707a73a2 Only export symbols starting with "json_" in libjansson.la
This way we don't pollute the symbol namespace with internal symbols.
2009-11-29 13:04:12 +02:00
Petri Lehtinen
330e892ff6 Make parse_json static 2009-11-29 13:00:47 +02:00
Petri Lehtinen
d857fd08a5 doc/github_commits.c Add copyright notice 2009-11-28 13:39:06 +02:00
Petri Lehtinen
e0a88d19d1 Merge branch '1.1'
Conflicts:
	configure.ac
	doc/conf.py
2009-11-08 22:01:02 +02:00
Petri Lehtinen
842bc2128b jansson 1.1.2 2009-11-08 14:13:32 +02:00
Petri Lehtinen
ca6d26a1c2 Don't include stdint.h in jansson.h
It's not needed
2009-11-07 14:45:46 +02:00
Petri Lehtinen
17d913307e Merge branch '1.1'
Conflicts:
	test/.gitignore
	test/testprogs/Makefile.am
2009-11-04 22:10:46 +02:00
Petri Lehtinen
f236c14dc5 dump: Revise whitespace usage
- Never append newline to output
- By default, add spaces between array and object items for more
  readable output
- Introduce the flag JSON_COMPACT to not add the aforementioned spaces
2009-11-04 22:09:40 +02:00
Petri Lehtinen
bf01067a8a Build documentation in make html target
To keep the distchecks for the documentation, the documentation has to
be built in the check target instead of distcheck-hook.

While at it, rename doc/.build to doc/_build. This naming is the
default with sphinx 0.6.2.
2009-11-03 23:09:04 +02:00
Petri Lehtinen
d3959a8ce7 load: Parse a badly put - sign correctly
Thanks to Manolis Delakis for reporting.
2009-10-29 15:45:16 +02:00
Petri Lehtinen
f243930b68 json_load_file: Initialize the error struct properly
Failing to do this has the effect that the error message is not
returned when the input file cannot be opened (e.g. if it doesn't
exist).

Thanks to Martin Vopatek for reporting.
2009-10-27 17:56:02 +02:00
Petri Lehtinen
d1b07171cc Merge branch '1.1'
Conflicts:
	CHANGES
	configure.ac
	doc/conf.py
2009-10-26 21:33:03 +02:00
Petri Lehtinen
15d992cb6a jansson 1.1.1 2009-10-26 21:27:10 +02:00
Petri Lehtinen
59c58ea26c Build documentation in distcheck-hook
This is to check that all the documentation files are distributed
before releasing.
2009-10-25 00:20:23 +03:00
Petri Lehtinen
f95bb423a3 Really distribute all the docs
The tutorial example github_commits.c was still left out.
2009-10-25 00:17:16 +03:00
Petri Lehtinen
9448ed3ef7 Distribute all the docs 2009-10-24 14:16:12 +03:00
Petri Lehtinen
7c7a1ed01e Fix version 1.1 release date in CHANGES 2009-10-24 14:16:11 +03:00
Petri Lehtinen
5ff8ae8052 Fix version 1.1 release date in CHANGES 2009-10-21 08:52:34 +03:00
Petri Lehtinen
8d53f447bf Set the version number to 1.1+ 2009-10-21 08:51:43 +03:00
Petri Lehtinen
325101e525 jansson 1.1 2009-10-20 21:29:16 +03:00
Petri Lehtinen
1b02f3546c Distribute stripped unit test data
Apparently, it was forgotten to add these files to
test/testdata/Makefile.am in commit
04d550b02e.
2009-10-20 21:23:49 +03:00
Petri Lehtinen
cd96049d10 Enhance documentation
- Add more information to the documentation front page
- Document how to build the documentation
- Clarify the tutorial a bit and remove some quirks
- Bring README.rst up-to-date
- Small enhancements here and there
2009-10-19 21:56:11 +03:00
raoulh
a83cd30c31 Add C++ guards to jansson.h
Signed-off-by: Petri Lehtinen <petri@digip.org>
2009-10-19 12:22:21 +03:00
Petri Lehtinen
a00988f663 doc: Add tutorial 2009-10-17 14:18:40 +03:00
Petri Lehtinen
86dc1d629b Fix indentation
No functional changes.
2009-10-16 21:20:38 +03:00
Petri Lehtinen
b67e130fda json_dumpf: Document the output shortage on error 2009-10-15 21:06:59 +03:00
Petri Lehtinen
4cd777712b Enhance handling of circular references
It's now an error to try to add an object or array to itself. The
encoder checks for circular references and fails with an error status
if one is detected.
2009-10-15 21:02:27 +03:00
Petri Lehtinen
79009e62c1 json_dumps: Close the strbuffer if dumping fails 2009-10-15 20:54:32 +03:00
Petri Lehtinen
76999799ed doc: Fix a small typo in apiref 2009-10-15 17:39:49 +03:00
Petri Lehtinen
22af193a51 doc/Makefile.am: Remove *.pyc in clean 2009-10-14 22:09:14 +03:00
Petri Lehtinen
951d091f07 Make integer, real and string mutable
Added functions:

  json_string_set
  json_integer_set
  json_real_set

While at it, clarify the documentation and parameter naming of
json_{string,integer,real}_value() a bit.
2009-10-14 08:23:02 +03:00
Petri Lehtinen
185e107d24 Don't use non-portable asprintf()
Thanks to Adam Strzelecki for reporting.
2009-10-13 15:40:26 +03:00
Petri Lehtinen
ca7703fbd1 Merge branch '1.0'
Conflicts:
	configure.ac
	doc/conf.py
2009-10-11 21:51:54 +03:00
Petri Lehtinen
12cd4e8c09 jansson 1.0.4 2009-10-11 21:38:42 +03:00
Petri Lehtinen
bad16ea52a Fix API tests for object
Because of a typo in test/testprogs/Makefile.am, the tests for object
were never compiled or run.
2009-10-11 21:38:42 +03:00
Petri Lehtinen
30015bde90 Remove config.h.in
It doesn't have to be in version control.
2009-10-11 21:38:41 +03:00
Petri Lehtinen
1e00cd58a5 Extend object API
Added functions:

  json_object_size
  json_object_clear
  json_object_update
2009-10-11 20:44:01 +03:00
Petri Lehtinen
40bb7bf437 Fix API tests for object
Because of a typo in test/testprogs/Makefile.am, the tests for object
were never compiled or run.
2009-10-10 22:55:15 +03:00
Petri Lehtinen
9d648a87cb Check json_object_set_new arguments
A segfault was caused by NULL key.
2009-10-10 22:53:36 +03:00
Petri Lehtinen
8de850be95 Remove config.h.in
It doesn't have to be in version control.
2009-10-10 21:21:22 +03:00
Petri Lehtinen
19588c2d69 Fix a few more compilation issues
These were left out from the previous commit.
2009-10-06 13:22:27 +03:00
Petri Lehtinen
9c5a8430db Make it compile on platforms where char is unsigned
Linux on powerpc seems to be one such platform.
2009-10-01 21:54:45 +03:00
Petri Lehtinen
afc9c1a23a Relax Autoconf version prereq
From 2.63 to 2.59, which is more widely supported.
2009-10-01 21:54:29 +03:00
Petri Lehtinen
cbacac5975 Extend array API
Added functions:

  json_array_insert
  json_array_insert_new
  json_array_remove
  json_array_clear
  json_array_extend
2009-09-29 22:17:58 +03:00
Petri Lehtinen
b3e1fe2ec5 doc: Add getting started guide 2009-09-20 21:56:12 +03:00
Petri Lehtinen
7728716759 Merge branch '1.0'
Conflicts:
	configure.ac
	doc/conf.py
2009-09-14 15:06:06 +03:00
Petri Lehtinen
5406c2b3d3 load: Check for integer and real overlfows and underflows 2009-09-13 13:18:01 +03:00
Petri Lehtinen
743af38e7f Use unsigned long instead of uint32_t
Some day we will have ANSI C compatibility... This change doesn't make
the API backwards incompatible because uint32_t was only used in flags
to json_dump*() and the flags are meant to be used only by ORing
constants and macro output, and actually currently only JSON_INDENT
can be used.
2009-09-13 13:16:34 +03:00
Petri Lehtinen
9d16ec755c test/json-compare.py: Use json module from Python >=2.6 or simplejson 2009-09-11 22:22:34 +03:00
Petri Lehtinen
04d550b02e Add stripped unit tests for decoder
That is, test cases where there's no newline or other whitespace at
the beginning or end of input. This was implemented by adding a
--strip option to split-testfile to strip the input file after writing
it.

The actual test JSON texts are the same as testdata/invalid and
testdata/valid. The expected output of the invalid cases had to be
adjusted a bit: because there's no newline at the end, some of the
line numbers needed to be changed.
2009-09-08 17:03:01 +03:00
Petri Lehtinen
55d2566539 Merge branch '1.0' into HEAD
Conflicts:
	configure.ac
	doc/conf.py
2009-09-08 17:02:39 +03:00
Petri Lehtinen
7ee974e91c Don't perform reference counting on true, false and null
This makes their constructors thread safe. A special reference count
-1 is used to test whether to perform reference counting on a value or
not.
2009-09-07 21:40:14 +03:00
Petri Lehtinen
c288188a0f test/.gitignore: Add testprogs/test_simple 2009-09-07 21:25:22 +03:00
Petri Lehtinen
057ba29a27 doc apiref: Enhancements
Constructors are described more clearly and return values on error
situations more compehensively. Also add an example of the object
iteration protocol.
2009-09-07 21:09:55 +03:00
Petri Lehtinen
234ee47281 Better argument validation
All pointer arguments are now tested for NULL. json_string() now also
tests that strdup() succeeds. This is to ensure that no NULL values
end up in data structures.

Also desribe the different sources of errors in documentation.
2009-09-06 22:24:55 +03:00
Petri Lehtinen
98a8c1aebf Don't include unistd.h
It's not needed.
2009-09-06 22:24:22 +03:00
Petri Lehtinen
42e546d065 doc apiref: json_incref() returns its argument 2009-09-06 22:16:38 +03:00
Petri Lehtinen
6ffb04f0d4 Expand test coverage
Now all public API functions are tested (at least on some level) in
the test-api suite.
2009-09-06 14:06:02 +03:00
Petri Lehtinen
fd259ff68c Merge branch '1.0'
Conflicts:
	configure.ac
	doc/conf.py
2009-09-06 12:53:38 +03:00
Petri Lehtinen
9611780907 dump: Optimize indenting
Don't alloca() a whitespace buffer and fill it with spaces in each
call to dump_indent. Instead, use a static whitespace buffer.

As a bonus, this saves the use of poorly portable alloca().
2009-09-04 09:52:40 +03:00
Petri Lehtinen
93c5892bc3 load: Factor out an unneeded strdup
By "stealing" the string parsed out in lexer, one strdup can be saved.
2009-09-04 09:52:40 +03:00
Petri Lehtinen
1095ca956b Don't define -std=c99 in AM_CFLAGS
It is not needed.
2009-09-04 09:52:40 +03:00
Petri Lehtinen
05654bfcba doc: Fix typo 2009-09-04 09:52:40 +03:00
Petri Lehtinen
89d09813c3 Add reference stealing functions for inserting values to objects and arrays
The non-stealing functions are now just simple wrappers around these.
2009-09-04 09:52:37 +03:00
Petri Lehtinen
5ac4914642 Change the version to 1.0+ 2009-09-02 23:10:46 +03:00
444 changed files with 4661 additions and 1315 deletions

2
.gitignore vendored
View File

@@ -8,6 +8,7 @@ aclocal.m4
autom4te.cache
config.guess
config.h
config.h.in
config.log
config.status
config.sub
@@ -21,3 +22,4 @@ missing
*.la
stamp-h1
*.pyc
*.pc

132
CHANGES
View File

@@ -1,4 +1,117 @@
Version 1.0.3, released 2009-09-14
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
* Fix API tests for object
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
@@ -6,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

View File

@@ -1,2 +1,7 @@
EXTRA_DIST = CHANGES LICENSE README.rst
SUBDIRS = doc src test
check-local: html
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = jansson.pc

View File

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

View File

@@ -1,59 +0,0 @@
/* config.h.in. Generated from configure.ac by autoheader. */
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#undef LT_OBJDIR
/* Name of package */
#undef PACKAGE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Version number of package */
#undef VERSION

View File

@@ -1,5 +1,5 @@
AC_PREREQ([2.63])
AC_INIT([jansson], [1.0.3], [petri@digip.org])
AC_PREREQ([2.59])
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
View File

@@ -1 +1 @@
.build/
_build/

View File

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

View File

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

View File

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

@@ -0,0 +1,5 @@
******************
Changes in Jansson
******************
.. include:: ../CHANGES

View File

@@ -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.3'
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.3'
# 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
View 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
View 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;
}

View File

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

View File

@@ -13,6 +13,8 @@ libjansson_la_SOURCES = \
utf.h \
util.h \
value.c
libjansson_la_LDFLAGS = -version-info 0:3: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

View File

@@ -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 != '"' && (*end < 0 || *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;

View File

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

View File

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

View File

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

View File

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

View File

@@ -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"
@@ -135,7 +135,7 @@ static char stream_get(stream_t *stream, json_error_t *error)
c = stream->buffer[0];
if(c < 0 && c != EOF)
if((unsigned char)c >= 0x80 && c != (char)EOF)
{
/* multi-byte UTF-8 sequence */
int i, count;
@@ -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');
@@ -258,14 +258,14 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
c = lex_get_save(lex, error);
while(c != '"') {
if(c == EOF) {
if(c == (char)EOF) {
lex_unget_unsave(lex, c);
if(lex_eof(lex))
error_set(error, lex, "premature end of input");
goto out;
}
else if(0 <= c && c <= 0x1F) {
else if((unsigned char)c <= 0x1F) {
/* control character */
lex_unget_unsave(lex, c);
if(c == '\n')
@@ -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);
@@ -519,7 +524,7 @@ static int lex_scan(lex_t *lex, json_error_t *error)
c = lex_get(lex, error);
}
if(c == EOF) {
if(c == (char)EOF) {
if(lex_eof(lex))
lex->token = TOKEN_EOF;
else
@@ -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)
{

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

@@ -0,0 +1,2 @@
SUBDIRS = api
EXTRA_DIST = invalid invalid-strip invalid-unicode valid valid-strip

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

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

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

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

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

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

View File

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

View File

@@ -0,0 +1 @@
[1, 2]

View File

@@ -0,0 +1 @@
[1, 2]

View File

@@ -0,0 +1 @@
export JSON_COMPACT=1

View File

@@ -0,0 +1 @@
[1, 2]

View File

@@ -0,0 +1 @@
[1,2]

View File

@@ -0,0 +1 @@
export JSON_COMPACT=1

View File

@@ -0,0 +1 @@
{"a": 1, "b": 2}

View File

@@ -0,0 +1 @@
{"a":1,"b":2}

View File

@@ -0,0 +1 @@
export JSON_ENSURE_ASCII=1

View File

@@ -0,0 +1,8 @@
[
"foo",
"å ä ö",
"foo åä",
"åä foo",
"å foo ä",
"clef g: 𝄞"
]

View File

@@ -0,0 +1 @@
["foo", "\u00e5 \u00e4 \u00f6", "foo \u00e5\u00e4", "\u00e5\u00e4 foo", "\u00e5 foo \u00e4", "clef g: \ud834\udd1e"]

View File

@@ -0,0 +1 @@
export JSON_INDENT=4

View File

@@ -0,0 +1 @@
[1, 2]

View File

@@ -0,0 +1,4 @@
[
1,
2
]

View File

@@ -0,0 +1,2 @@
export JSON_INDENT=4
export JSON_COMPACT=1

View File

@@ -0,0 +1 @@
[1, 2]

View File

@@ -0,0 +1,4 @@
[
1,
2
]

View File

@@ -0,0 +1,2 @@
export JSON_INDENT=4
export JSON_COMPACT=1

View File

@@ -0,0 +1 @@
{"a": 1, "b": 2}

View File

@@ -0,0 +1,4 @@
{
"a":1,
"b":2
}

View File

@@ -0,0 +1 @@
export JSON_INDENT=4

View File

@@ -0,0 +1 @@
{"a": 1, "b": 2}

View File

@@ -0,0 +1,4 @@
{
"a": 1,
"b": 2
}

View File

@@ -0,0 +1 @@
{"a": 1, "b": 2}

View File

@@ -0,0 +1 @@
{"a": 1, "b": 2}

32
test/suites/encoding-flags/run Executable file
View 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

View File

@@ -0,0 +1 @@
export JSON_SORT_KEYS=1

View File

@@ -0,0 +1 @@
{"foo": 1, "bar": 2, "baz": 3, "quux": 4}

View File

@@ -0,0 +1 @@
{"bar": 2, "baz": 3, "foo": 1, "quux": 4}

View File

@@ -0,0 +1,2 @@
1
invalid token near '''

View File

@@ -0,0 +1 @@
['

View File

@@ -0,0 +1,2 @@
1
'[' or '{' expected near 'a'

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,2 @@
1
string or '}' expected near ','

View File

@@ -0,0 +1 @@
{,

View File

@@ -0,0 +1,2 @@
1
unexpected token near ','

View File

@@ -0,0 +1 @@
[,

View File

@@ -0,0 +1,2 @@
1
']' expected near end of file

View File

@@ -0,0 +1 @@
[1,

View File

@@ -0,0 +1,2 @@
1
'[' or '{' expected near end of file

View File

View File

@@ -0,0 +1,2 @@
1
unexpected token near ']'

View File

@@ -0,0 +1 @@
[1,]

View File

@@ -0,0 +1,2 @@
6
unexpected token near ']'

View File

@@ -0,0 +1,6 @@
[1,
2,
3,
4,
5,
]

View File

@@ -0,0 +1,2 @@
2
end of file expected near 'foo'

View File

@@ -0,0 +1,2 @@
[1,2,3]
foo

View File

@@ -0,0 +1,2 @@
1
end of file expected near 'foo'

Some files were not shown because too many files have changed in this diff Show More