307 Commits
v1.0.1 ... v2.2

Author SHA1 Message Date
Petri Lehtinen
889f295958 jansson 2.2 2011-09-02 21:39:40 +03:00
Petri Lehtinen
68809cd913 doc: json_dump_callback was added in v2.2 2011-09-02 21:29:01 +03:00
Petri Lehtinen
910fb92267 Merge branch '2.1' 2011-08-08 21:00:47 +03:00
Petri Lehtinen
f241e14cab doc: Explain the non-constness of the first parameter of json_unpack() et al
Fixes GH-30.
2011-08-08 20:59:10 +03:00
Petri Lehtinen
7e9c293986 Merge branch '2.1' 2011-06-30 21:48:51 +03:00
Petri Lehtinen
d43464a1ec Merge branch 'issue-29'
Fixes GH-29.
2011-06-30 21:48:33 +03:00
Petri Lehtinen
c7079a25eb doc: Add documentation for json_dump_callback() 2011-06-30 21:47:37 +03:00
JKL
54f38d250c test file for new json_dump_callback function 2011-06-30 21:47:12 +03:00
JKL
c7d543d36c new typedef json_dump_callback_t, function json_dump_callback 2011-06-30 21:47:12 +03:00
Petri Lehtinen
7fab57dcef doc: Fix several typos and other little errors 2011-06-30 21:04:49 +03:00
JKL
cd9757512d use size_t for strbuffer writes, and avoid integer overflow 2011-06-30 20:45:18 +03:00
Petri Lehtinen
c0193bfb7f Check that target is string and value is not NULL in json_string_set() 2011-06-17 21:42:19 +03:00
Petri Lehtinen
6e1f4bb560 doc: Clarify that removing from containers decrements the refcount
While at it, reword the object iteration protocol description a bit.
2011-06-17 20:53:32 +03:00
Petri Lehtinen
1358d0bfac CHANGES: Fix v2.1 release date 2011-06-10 21:53:14 +03:00
Petri Lehtinen
86d17a8dc2 jansson 2.1 2011-06-10 21:30:11 +03:00
Petri Lehtinen
3988ab2d27 Use `literal` syntax with macro and flag names in CHANGES 2011-06-07 21:40:09 +03:00
Petri Lehtinen
f7f7bf5ab2 Prepare for relase: Add CHANGES entry for v2.1 2011-06-07 21:33:59 +03:00
Petri Lehtinen
a76ba52f34 Add JSON_DISABLE_EOF_CHECK decoding flag
With this flag enabled, the decoder stops after a valid JSON input and
thus allows extra data after it.

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

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

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

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

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

Closes GH-18.
2011-03-21 08:22:34 +02:00
Petri Lehtinen
b90ed1accb Enhance portability of va_copy()
va_copy() is a C99 feature. In C89 implementations, it's sometimes
available as __va_copy(). If not, memcpy() should do the trick.
2011-03-20 21:23:37 +02:00
Petri Lehtinen
1111960120 Fix a declaration after statement
While at it, add -Wdeclaration-after-statement to AM_CFLAGS to catch
these in the future.
2011-03-10 21:28:53 +02:00
Petri Lehtinen
7f09f48e7e Distribute doc/upgrading.rst
Closes GH-16.
2011-03-10 21:26:18 +02:00
Petri Lehtinen
b397711a66 Check documentation in make distcheck
It seems that the only way to do this is to use the dvi make target,
as it's built in make distcheck after running configure.
2011-03-10 21:24:45 +02:00
Petri Lehtinen
cf9b384bcb jansson 2.0 2011-02-28 20:46:14 +02:00
Petri Lehtinen
42b651ef56 Fix packing of invalid UTF-8 strings 2011-02-27 21:34:12 +02:00
Petri Lehtinen
387298d4a6 Update README 2011-02-27 21:09:55 +02:00
Petri Lehtinen
cd854b5bc2 tests: Add missing json_decref() calls to suites/api/test_unpack.c 2011-02-25 22:24:12 +02:00
Petri Lehtinen
dd9b4e530c Remove invalid subdirs from test/suites/Makefile.am 2011-02-25 21:09:04 +02:00
Petri Lehtinen
b5dd566c83 doc: Change the title of the upgrading section 2011-02-25 21:01:13 +02:00
Petri Lehtinen
ff26dc60d1 Small documentation tweaks
- Capitalize section titles correctly

- Rename "Building on Windows" to "Building on non-Unix systems" and
  tweak the text accordingly.

- Remove the "Installing Prebuilt Binaries" section. This info is
  better in the web instead, as it may change.

- Mention that Sphinx 1.0 or newer is required.
2011-02-24 22:35:02 +02:00
Petri Lehtinen
53bc9d8a39 Add "Upgrading from older releases" to docs, tweak CHANGES a bit 2011-02-24 22:15:13 +02:00
Petri Lehtinen
a3468c9bd8 Add a CHANGES entry for version 2.0
Wow, lots of stuff. And it's been a long while after the last release.
2011-02-22 21:13:09 +02:00
Petri Lehtinen
58f9d65535 Add lots of tests for pack/unpack code, fix bugs found
Closes GH-12.
2011-02-22 19:08:51 +02:00
Petri Lehtinen
a33c3628da Move json_error_t documentation to its own section
json_error_t is used in the decoder and in the pack and unpack API's,
so it's better to have a separate section for it.
2011-02-22 13:53:58 +02:00
Petri Lehtinen
50dc64a7af Truncate error source from start, not end, if it's too long to fit
It's more helpful to see "...bar/baz.json" instead of "/long/path/to".
2011-02-22 13:34:37 +02:00
Petri Lehtinen
5df7b79397 Refactor decoder input stream
- Add a new field position to the json_error_t structure. This is the
  position in bytes from the beginning of the input.

- Keep track of line, column and input position in the stream level.
  Previously, only line was tracked, and it was in the lexer level, so
  this info was not available for UTF-8 decoding errors.

- While at it, refactor tests so that no separate "stripped" tests are
  required. json_process is now able to strip whitespace from its
  input, and the "valid" and "invalid" test suites now use this to
  test both non-stripped and stripped input.

Closes GH-9.
2011-02-22 12:07:37 +02:00
Petri Lehtinen
e54ea1f7c9 tests: Use "make STOP=1 check" to stop on first failure 2011-02-19 12:15:21 +02:00
Petri Lehtinen
4be9e9e7fe Add custom memory allocation
Thanks to Basile Starynkevitch for the suggestion and initial patch.
Thanks to Jonathan Landis and Deron Meranda for showing how this can
be utilized for implementing secure memory operations.
2011-02-17 10:10:53 +02:00
Graeme Smecher
dd7dd414f0 Resolve __va_list_tag ** -> va_list * type errors with clang and future GCCs
Functions taking va_args are munged to receive arguments of type
'__va_list_tag *'. This patch uses va_copy to coerce them to the expected type
so we don't get compiler errors.

Tested on x86_64, both 32-bit and 64-bit compiles.

Reported-By: Basile Starynkevitch <basile@starynkevitch.net>
2011-02-03 20:41:54 +02:00
Petri Lehtinen
f25698d08f Fix an unpack example in the docs 2011-01-30 12:53:54 +02:00
Petri Lehtinen
ef13fb9189 Tweak the default validation behaviour of the unpack API
Now, by default, unpacking doesn't check that all array and object
items are accessed. The check can be enabled globally by using the
JSON_STRICT flag (formerly JSON_UNPACK_ONLY), or on a per-value basis
by using the new '!' format character. The '*' format character is
still available to disable the global check on a per-value basis.
2011-01-29 21:38:07 +02:00
Petri Lehtinen
7706abcbed Use the Sphinx default theme again for documentation
The "haiku" theme doesn't show chapter contents in sidebar.
2011-01-26 09:27:19 +02:00
Petri Lehtinen
7d49fc75d5 Documentation for pack and unpack functions 2011-01-26 09:27:19 +02:00
Petri Lehtinen
908c62f327 Add the 'I' format for both pack and unpack 2011-01-25 21:41:35 +02:00
Petri Lehtinen
a1c185a376 Use 'f' for real and 'F' for number (real or integer) in unpack 2011-01-25 20:42:41 +02:00
Petri Lehtinen
ac96ac13d4 unpack: Unify and enhance error messages
Give the type name instead of its enum index in the error message when
an unexpected value is encountered while unpacking.
2011-01-25 20:37:53 +02:00
Petri Lehtinen
579c291882 Expand the pack/unpack API, implement unpack flags
Expand the pack/unpack API: json_(un)pack is the simple version with
no error output, json_(un)pack_ex has error output and flags,
json_v(un)pack_ex is a va_list version of json_(un)pack_ex.

Implement unpacking flags:

- JSON_UNPACK_ONLY turns off extra validation, i.e. array and object
  unpacking doesn't check that all items are unpacked. This is really
  just convenience for not adding '*' after each object and array.

- JSON_VALIDATE_ONLY turns off unpacking, i.e. no varargs are expected
  and nothing is unpacked.
2011-01-25 10:02:27 +02:00
Petri Lehtinen
2770dca2c0 Add validation features to json_unpack()
* By default, json_unpack() now checks that all items of arrays and
  objects are unpacked. This is useful for validation.

* Add format specifier '*' to suppress this check for individual
  arrays and objects. '*' must appear as the last format specifier
  before the closing ']' or '}'.
2011-01-25 08:53:55 +02:00
Petri Lehtinen
6825c2c706 Rename variadic.c to pack_unpack.c 2011-01-25 08:53:55 +02:00
Petri Lehtinen
6d1f28f050 Refactor json_unpack()
* Use the format string scanner from the previous commit (with the
  simpler separator skipping semantics)

* Split json_vnunpack() to three separate functions for unpacking
  objects, arrays and "simple" values

* Always return 0 on success, 1 on error

This shaves around 100 more lines from the original implementation.
2011-01-25 08:53:54 +02:00
Petri Lehtinen
7f3018a4fb Refactor json_pack()
* Implement a "scanner" that reads the format string, maintaining state

* Split json_vnpack() to three separate functions for packing objects,
  arrays and simple values. This makes it more clear what is being
  packed, and the object and array structures become more evident.

* Make the skipping of ignored character simpler, i.e. skip ':' and
  ',' independent of their context

This patch shaves around 80 lines of code from the original
implementation.
2011-01-24 21:26:23 +02:00
Petri Lehtinen
53383860e8 Unify style 2011-01-24 21:14:55 +02:00
Petri Lehtinen
fa7c2ea070 Update copyright notices for 2011 2011-01-22 13:43:14 +02:00
Petri Lehtinen
46f91797ec Build cleanly with gcc's -Werror=declaration-after-statement flag 2011-01-17 15:51:40 +02:00
Graeme Smecher
a242381024 Remove debugging printf 2011-01-15 12:45:08 +02:00
Graeme Smecher
3a7512d2b0 Make json_pack/json_unpack() recursive
Note that we pass va_list pointers around instead of just va_lists, which
would seem more intuitive. This is necessary since the behaviour of va_lists
passed as function parameters is finicky. Quoth stdarg(3):

	If ap is passed to a function that uses va_arg(ap,type) then the value
	of ap is undefined after the return of that function.

The pointer-passing strategy is used by Python's Py_BuildValue() for the same
purpose.
2011-01-14 21:43:48 +02:00
Petri Lehtinen
269e86b725 Emphasize the constness of the return value of json_string_value()
Closes GH-7.
2010-12-20 21:08:11 +02:00
Petri Lehtinen
bf32f6cd75 Document the version info constants 2010-12-19 21:55:00 +02:00
Petri Lehtinen
c7611e7a0d Make int32_t available when the configure script is not used 2010-12-18 23:02:54 +02:00
Petri Lehtinen
23bc8e468d Add version info to jansson.h 2010-12-14 20:58:40 +02:00
Petri Lehtinen
5422a862de Enhance error reporting
This patch adds two new fields to the json_error_t struct: column and
source. It also adds functions to populate json_error_t internally.

The column field is not currently used, but it will be utilized in the
decoder and pack/unpack functions.
2010-10-27 21:58:20 +03:00
Petri Lehtinen
818baf5fdb Move and enhance documentation on json_error_t 2010-10-26 21:20:34 +03:00
Petri Lehtinen
bb5d4efb2e Make json_error_t transparent again
After looking at the new code for a few days, I didn't like it
anymore. To prepare for the future, a few fields will be added to the
json_error_t struct later.

This reverts commit 23dd078c8d. Some
adjustments were needed because of newer commits.
2010-10-26 21:20:34 +03:00
Graeme Smecher
198d537be7 Adds json_pack / json_unpack variadic functions. 2010-10-26 08:59:06 +03:00
Petri Lehtinen
1acd1a7b56 Remove all "Added in version 1.x" info from documentation
As 2.0 will be backwards incompatible anyway, there's no need to
record which features were new in which 1.x releases.
2010-10-14 21:05:22 +03:00
Petri Lehtinen
23dd078c8d Make json_error_t opaque
All decoding functions now accept a json_error_t** parameter and set
it to point to a heap-allocated json_error_t structure if an error
occurs. The contents of json_error_t are no longer exposed directly, a
few functions to do it have been added instead. If an error occurs,
the user must free the json_error_t value.

This makes it possible to enhance the error reporting facilities in
the future without breaking ABI compatibility with older versions.

This is a backwards incompatible change.
2010-10-14 21:02:37 +03:00
Petri Lehtinen
781bda1404 Merge branch '1.3' 2010-09-06 20:48:25 +03:00
Petri Lehtinen
3d5c0f46f1 Run Sphinx without the -W flag when building documentation
In "make html", don't use the -W flag with Sphinx. This makes it
possible to create the documentation with Sphinx 1.0 without errors,
as the warning about using old-style C markup isn't turned to an
error.

Don't build documentation in "make check". Instead, add a new make
target "check-doc" to build the documentation with the -W flag.
2010-09-06 20:46:15 +03:00
Petri Lehtinen
8567816542 Use Sphinx 1.0, change the HTML theme 2010-09-05 22:00:47 +03:00
Petri Lehtinen
664c88ca97 Add a section describing how to build on Windows 2010-09-05 21:38:06 +03:00
Petri Lehtinen
cbb3855d97 On Windows, typedef int to int32_t
There's no inttypes.h or stdint.h in the stdlib on Windows.
2010-09-05 21:30:02 +03:00
Petri Lehtinen
3c4cf31a01 Add jansson_config.h.win32
As the configure script cannot be run on Windows, give the users a
jansson_config.h that they can use directly.
2010-09-05 21:21:37 +03:00
Petri Lehtinen
06eb436008 Move max() to jansson_private.h, define only if not already defined
On some platforms (Visual C++ for one) the standard library already
defines max() as a macro.
2010-09-05 21:18:46 +03:00
Petri Lehtinen
e3654c2245 Don't use designated struct initializers
It's a C99 feature and not available on all compilers (Visual C++, for
example).
2010-09-05 21:18:46 +03:00
Petri Lehtinen
a112563214 Use config.h only if it exists
This makes it easier to compile without the configure script (e.g. on
Windows).
2010-09-05 21:18:46 +03:00
Petri Lehtinen
976fc2279f Replace all occurences of inline with JSON_INLINE
This makes it easier to compile without the configure script (e.g. on
Windows).
2010-09-05 21:18:46 +03:00
Petri Lehtinen
56643d4311 Merge branch '1.3'
Conflicts:
	doc/apiref.rst
	src/jansson_private.h
2010-08-14 21:02:08 +03:00
Petri Lehtinen
cb8fcc7808 Set the version number to 2.0pre 2010-08-14 20:47:08 +03:00
Petri Lehtinen
b76c69de1b Clarify and document the integer type configuration 2010-08-14 20:42:15 +03:00
Petri Lehtinen
bfac1972e2 Add a flags parameter to all decoding functions for future needs
As of now, the parameter is unused, but may be needed in the future.
I'm adding it now so that in the future both API and ABI remain
backwards compatible as long as possible.

This is a backwards incompatible change.
2010-08-14 17:28:09 +03:00
Petri Lehtinen
f8d0e01e46 Change the maximum indentation size to 32 spaces in encoder
This is to free up bits from the flags parameter of json_dump
functions. I'm pretty sure no-one needs 256 spaces of indentation when
pretty-printing JSON values...

This is a backwards incompatible change.
2010-08-13 22:12:36 +03:00
Petri Lehtinen
ffbab6fedd Change the underlying type of JSON integer from long to json_int_t
json_int_t is typedef'd to long long if it's supported, or long
otherwise. There's also some supporting things, like the
JSON_INTEGER_FORMAT macro that expands to the printf() conversion
specifier that corresponds to json_int_t's actual type.

This is a backwards incompatible change.
2010-08-13 22:07:20 +03:00
Petri Lehtinen
145032a57f Make object_key_t portable
A flexible array member is unportable. Use a table of length 1
instead. This needs some adjustment to the memory allocatio, too.
2010-08-12 21:35:23 +03:00
Petri Lehtinen
519d52e2bb Beautify the container_of macro
Use offsetof instead of zero pointer dereference trickery.
2010-08-12 21:34:51 +03:00
Petri Lehtinen
94182a5acc Replace inline with JSON_INLINE in json_object_iter_set() declaration 2010-08-12 21:10:12 +03:00
Petri Lehtinen
f71eb7fe17 Check for gcc before setting gcc specific CFLAGS 2010-08-12 21:00:09 +03:00
Petri Lehtinen
7ce70533c9 Move site configuration to jansson_config.h
This way, more site configuration can be more easily added later.
2010-08-10 22:16:55 +03:00
Petri Lehtinen
014c49c285 Change JSON integer's underlying type from int to long
This is a backwards incompatible change.
2010-08-10 21:45:18 +03:00
Petri Lehtinen
6e3ca5c45c Clarify the documentation
Couple some string and number information from the RFC conformance
chapter in the API reference, and refer to the RFC conformance chapter
from API reference for more information.

Also, state more clearly that a JSON text must have an array or object
as the top-level value, and better document the string comparison
performed by json_equal().
2010-06-16 21:34:10 +03:00
Petri Lehtinen
68f2861e92 Unify unsigned integer usage in the API
Replace all occurences of unsigned int and unsigned long with size_t.

This is a backwards incompatible change, as the signature of many API
functions changes.
2010-06-15 15:38:59 +03:00
Petri Lehtinen
b354f8a35a configure.ac: Remove unneeded AC_PROG_CXX 2010-06-14 22:30:15 +03:00
Petri Lehtinen
b461c652b4 Add a few missing changes to CHANGES for v1.3
These were forgotten when releasing.
2010-06-14 14:35:38 +03:00
Petri Lehtinen
2caac965d4 jansson 1.3 2010-06-13 20:37:33 +03:00
Petri Lehtinen
1347686dbf Remove the C++ interface 2010-06-12 22:45:49 +03:00
Petri Lehtinen
8b2b12e05f Merge branch '1.2' 2010-06-12 22:45:45 +03:00
Petri Lehtinen
1a090bbcd3 Fix a few memory leaks in tests
No changes to the actual library code.
2010-06-10 21:16:07 +03:00
Petri Lehtinen
dec3ad498e Merge branch '1.2' 2010-05-20 18:47:30 +03:00
Petri Lehtinen
978a47e2c5 Clarify the documentation on reference stealing
Provide an example usage pattern for reference stealing functions.
This way the user (hopely) sees more clearly how the reference
stealing functions are meant to be used.
2010-05-20 18:45:24 +03:00
Petri Lehtinen
453e4c0aa2 Zero the visited flag after an encoding error
When encoding an array or object ends in an error, the visited flag
wasn't zeroed, causing subsequent encoding attempts to fail. This
patch fixes the problem by always zeroing the visited flag.
2010-05-14 09:23:35 +03:00
Petri Lehtinen
2630980f49 Zero the visited flag after encoding an empty array or object
Encoding an empty array or object worked, but encoding it again
(possibly after adding some items) failed, because the visited flag
(used for detecting circular references) wasn't zeroed.
2010-05-14 09:05:56 +03:00
Petri Lehtinen
782acfe378 Merge branch '1.2' 2010-05-07 07:36:01 +03:00
Petri Lehtinen
f9475f9577 load.c: Make stream_init() static 2010-05-07 07:35:11 +03:00
Petri Lehtinen
8857aeadfd Merge branch '1.2'
Conflicts:
	CHANGES
	configure.ac
	doc/conf.py
2010-04-03 14:00:59 +03:00
Petri Lehtinen
047a1417fb jansson 1.2.1 2010-04-03 13:49:56 +03:00
Petri Lehtinen
ce42e30b8c doc: Distribute conformance.rst 2010-04-03 13:49:56 +03:00
Petri Lehtinen
4e63fcd55d Merge branch '1.2'
Conflicts:
	configure.ac
2010-03-28 21:44:41 +03:00
Petri Lehtinen
024106bbfb Require autoconf 2.60
The AC_TYPE_INT32_T macro first appeared in autoconf 2.60.
2010-03-28 21:19:40 +03:00
Petri Lehtinen
29ee3832cf Support compilers that don't have the "inline" keyword
Use AC_C_INLINE autoconf macro, include config.h where needed, and add
a define of JSON_INLINE to jansson.h that has the correct "inline"
keyword.
2010-03-28 21:14:08 +03:00
Petri Lehtinen
c7c2edae8a doc: Add chapter on RFC conformance
Thanks to Deron Meranda for providing the initial text.
2010-03-26 22:03:26 +02:00
Petri Lehtinen
bb89a5d4d3 Estimate real number underflows with 0.0
Earlier it was a decoding error.
2010-03-26 21:59:56 +02:00
Petri Lehtinen
f76966b438 Enhance tests for null byte 2010-03-26 21:29:36 +02:00
Petri Lehtinen
49880cbabe Merge branch '1.2' 2010-03-23 08:15:19 +02:00
Petri Lehtinen
f284e3c069 Fix reference counting on true, false and null
Initialize their reference counts to (unsigned int)-1 to disable
reference counting on them. It already was meant to work like this,
but the reference counts were just initialized to 1 instead of -1.

Thanks to Andrew Thompson for reporting this issue.
2010-03-23 08:12:32 +02:00
Petri Lehtinen
66a69f3f10 Ignore temporary files *~ 2010-03-19 08:11:48 +02:00
Andres Freund
7d5982e6fe c++ wrapper: add missing 'inline' statements to various constructors
the missing 'inline' leads to duplicated symbols if the header is
included into two separately compiled files.
2010-03-18 14:35:09 +01:00
Petri Lehtinen
a2a9107600 Don't include stdint.h anywhere
This should have fixed by commit 28682322, but there was one #include
left in utf.c. It now includes utf.h instead of stdint.h.
2010-03-18 07:22:43 +02:00
Petri Lehtinen
42621370c3 hashtable: Fix typo in comment 2010-02-11 21:17:19 +02:00
Petri Lehtinen
8e61b7c0f0 Merge branch 'c++-enhance-proxies' 2010-02-11 21:06:19 +02:00
Petri Lehtinen
35ddd2de20 Update CHANGES, change version to 1.2+ 2010-02-11 20:55:56 +02:00
Petri Lehtinen
f18ef5144a Implement JSON_PRESERVE_ORDER encoding flag
With this encoding flag, the object key-value pairs in output are in
the same order in which they were first inserted into the object.

To make this possible, a key of an object is now a serial number plus
a string. An object keeps an increasing counter which is used to
assign serial number to the keys. Hashing, comparison and public API
functions were changed to act only on the string part, i.e. the serial
number is ignored everywhere else but in the encoder, where it's used
to order object keys if JSON_PRESERVE_ORDER flag is used.
2010-02-11 20:48:56 +02:00
Petri Lehtinen
307167fb66 Optimize hashtable_set()
If a key already exists in the hashtable, use the existing pair
changing its value instead of removing the old one and allocating a
new pair.
2010-02-09 20:51:25 +02:00
Petri Lehtinen
7e8b128740 C++: Optimize PropertyProxy
When the property already exists in the object, we can store an
iterator pointing to that property, instead of duplicating the key.

When the property (key) is not present in the object, we still have to
duplicate the key.
2010-02-08 20:51:09 +02:00
Petri Lehtinen
acec2559a5 C++: Make proxies safer
If a user happens to store an ElementProxy or a PropertyProxy
instance, we need to take a reference to the JSON value they point to.
With PropertyProxy, the key needs to be copied as well.
2010-02-07 14:08:54 +02:00
Petri Lehtinen
286823227c Make int32_t available on all systems
Use AC_TYPE_INT32_T and include inttypes.h (if it exists) instead of
stdint.h for maximum portability.
2010-02-06 21:11:41 +02:00
Petri Lehtinen
8d75235ff2 Merge branch '1.2'
Conflicts:
	LICENSE
2010-02-04 21:13:57 +02:00
Petri Lehtinen
79e9dae9a0 Merge branch 'cleanup-c++-code' 2010-02-04 21:12:36 +02:00
Petri Lehtinen
f021ba00a2 C++: Fix test_cpp.cpp to work with VPATH builds
It reads an input file, and the file location is different with VPATH
builds. Read top_srcdir from environment and use it to find the file.
2010-02-04 21:10:04 +02:00
Petri Lehtinen
adb1b58627 C++: Add Value::dump_file(), load_file() and loads() that take an std::string 2010-02-04 21:08:42 +02:00
Petri Lehtinen
b8059a1880 C++: Rename some functions to better match the C API
Value::save_file -> Value::dump_file
Value::save_string -> Value::dumps
load_string -> loads
2010-02-04 21:08:38 +02:00
Petri Lehtinen
49d40f020b C++: #include <cstdio> in jansson.hpp
This is to avoid standard C functions ending up in namespace json, as
jansson.h is #included in there, and jansson.h in turn #includes
stdio.h.
2010-02-04 20:50:02 +02:00
Petri Lehtinen
910a2f318b C++: Rename test.json to test_cpp.json 2010-02-04 20:49:01 +02:00
Petri Lehtinen
08dc8d9baf Add year 2010 to copyright notices 2010-02-02 21:26:11 +02:00
Petri Lehtinen
c9fc055351 Add myself as another copyright holder for jansson.hpp and jansson.ipp 2010-02-02 21:14:50 +02:00
Petri Lehtinen
d1a0c3ffc2 C++: Rename jansson-impl.hpp to jansson.ipp
The .ipp suffix is for inlined template implementation code.

While at it, use #ifdef and #ifndef instead of #if defined().
2010-02-02 21:10:58 +02:00
Petri Lehtinen
b07e69c37a C++: Rename namespace json::_private to json::detail 2010-02-02 21:01:50 +02:00
Petri Lehtinen
2b43e7dbda C++: Untabify, reindent, delete trailing whitespace 2010-02-02 21:00:10 +02:00
Petri Lehtinen
5b1a666cf1 test/suites/api: Detect tests correctly
The C++ test case didn't work correctly in VPATH builds or with
VALGRIND=1.
2010-02-02 20:37:02 +02:00
Petri Lehtinen
b495b96547 Add functions json_object_iter_{at,set,set_new} 2010-02-01 21:07:19 +02:00
Petri Lehtinen
72e3948438 Merge branch '1.2' 2010-01-28 21:05:19 +02:00
Petri Lehtinen
f5662a82cd test/suites/api/test_object.c: Enhance tests for iterators 2010-01-28 21:04:21 +02:00
Petri Lehtinen
ab2e567685 test/suites/api: Fail when a test fails
The valgrind fix a while back apparently made the test system not
notice normal failures in suites/api.
2010-01-28 20:58:26 +02:00
Petri Lehtinen
d8ea2f8c4b run-tests.sh: Print the test name correctly when VERBOSE=1 2010-01-28 20:57:52 +02:00
Petri Lehtinen
aaae37afba doc/Makefile.am: Don't remove changes.rst in clean 2010-01-26 21:19:48 +02:00
Petri Lehtinen
04f7e27877 Update LICENSE 2010-01-21 22:31:06 +02:00
Petri Lehtinen
3dd29366b8 Merge branch 'c++-api' 2010-01-21 22:29:26 +02:00
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
Sean Middleditch
38950b081c add meaningful copyright to jansson-impl.hpp too 2010-01-18 21:55:41 -08:00
Sean Middleditch
56687e9b56 add meaningful copyright to jansson.hpp 2010-01-18 21:55:25 -08:00
Sean Middleditch
c9b33e3386 integrate jansson.hpp into build and test suite 2010-01-18 21:36:02 -08:00
Sean Middleditch
2ad4634de5 Merge branch 'master' of /home/elanthis/Source/janssonxx
Conflicts:
	.gitignore
2010-01-18 21:26:10 -08:00
Sean Middleditch
e080667729 replace json::from() with explicit Value() constructors 2010-01-18 19:24:25 -08:00
Sean Middleditch
ef6c35ae1b move static functions out of Value, add test driver to ensure linking works properly 2010-01-18 18:50:13 -08:00
Sean Middleditch
95bf762eeb rename jansson namespace to json 2010-01-18 18:37:13 -08:00
Petri Lehtinen
f9febb64c5 Merge branch '1.1'
Conflicts:
	Makefile.am
2010-01-17 13:57:20 +02:00
Sean Middleditch
dd36e4e838 rename files to match upstream's preferences 2010-01-16 20:27:13 -08:00
Sean Middleditch
df35adc438 add comments noting inefficiency of stream ops 2010-01-16 20:21:52 -08:00
Sean Middleditch
f88a5a0e6b added a couple minor comments 2010-01-16 20:17:48 -08:00
Sean Middleditch
cc06bc334a cleanup code 2010-01-16 20:15:33 -08:00
Sean Middleditch
2dc2b6bab7 rename ArrayProxy to ElementProxy and ObjectProxy to PropertyProxy 2010-01-16 01:40:16 -08:00
Sean Middleditch
49a64a6edf rename and move the _* private classes to _private namespace 2010-01-16 01:36:13 -08:00
Sean Middleditch
f0be52f9f8 add object property proxy support 2010-01-16 01:31:37 -08:00
Sean Middleditch
1bc0225441 add array element proxy support 2010-01-16 01:24:27 -08:00
Sean Middleditch
87df8bb0fe templatize janssonxx functionality to prepare for proxy setters 2010-01-16 01:13:19 -08: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
Sean Middleditch
69437a7183 dont attempt to create a std::string from NULL in as_string() 2010-01-13 18:35:07 -08:00
Sean Middleditch
63f762bc48 save flags default to 0 2010-01-13 18:34:17 -08:00
Sean Middleditch
5a0efe6536 add a safeguard against NULL return output stream 2010-01-13 18:33:19 -08:00
Sean Middleditch
01759517aa add Value::from(float) 2010-01-13 18:32:44 -08:00
Sean Middleditch
17805e5829 insert and remove methods 2010-01-12 16:17:11 -08:00
Sean Middleditch
492d95329a add proper attribution to janssonxx.h 2010-01-12 16:07:57 -08:00
Sean Middleditch
7ba18d3f0a use different temporaries in tests instead of reusing e3 2010-01-12 16:03:27 -08:00
Sean Middleditch
7d09af38c1 remove auto type conversion on array/object assignment 2010-01-12 15:38:47 -08:00
Sean Middleditch
8d3a9e347c rename as_json_t to as_json 2010-01-12 15:33:36 -08:00
Sean Middleditch
f79a81dad9 add (ugly) stream support 2010-01-12 15:29:45 -08:00
Sean Middleditch
b98e9d180c rename the set() methods 2010-01-12 15:14:57 -08: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
Sean Middleditch
8d5d2a93d5 remove some unnecessary checks 2010-01-12 04:30:02 -08:00
Sean Middleditch
d77c2e3fb0 cleanup the take_ownership function a bit 2010-01-12 04:26:30 -08:00
Sean Middleditch
7ef3202f83 added save_file and save_string methods 2010-01-12 04:20:17 -08:00
Sean Middleditch
36085ab49a include jansson.h inside the jansson namespace 2010-01-12 04:13:26 -08:00
Sean Middleditch
f743c4ee7f test object property assignment and clear 2010-01-12 01:41:17 -08:00
Sean Middleditch
c994eddec4 expand array assignment tests 2010-01-12 01:34:58 -08:00
Sean Middleditch
5a20e2695b add link to Jansson web to README 2010-01-12 01:31:08 -08:00
Sean Middleditch
cd18aa97f0 added README 2010-01-12 01:29:44 -08:00
Sean Middleditch
bd09127859 cleaner assignment behavior 2010-01-12 01:26:47 -08:00
Sean Middleditch
6818c117ee ignore test-bin output file 2010-01-12 01:10:38 -08:00
Sean Middleditch
39601c183a add tests 2010-01-12 01:10:20 -08:00
Sean Middleditch
1e3b41e8ea initial commit of janssonxx.h 2010-01-12 01:10:09 -08: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
b7bf96996f jansson 1.0.3 2009-09-14 14:32:41 +03:00
Petri Lehtinen
6d8c287032 load: Check for integer and real overlfows and underflows
Backported from master, commit 5406c2b3d3:
  * deleted test/testdata/invalid-stripped because the stripped tests
    don't exist in 1.0
2009-09-14 10:28:03 +03:00
Petri Lehtinen
ab3764ed0a test/json-compare.py: Use json module from Python >=2.6 or simplejson
Backported from master, commit 9d16ec755c
2009-09-14 10:27:51 +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
9cc6fbe580 Distribute CHANGES 2009-09-08 16:57:45 +03:00
Petri Lehtinen
827a01937f jansson 1.0.2 2009-09-08 16:48:26 +03:00
Petri Lehtinen
0f62dac627 load: Handle EOF correctly
In stream_get(), EOF never got it to stream->buffer and because of
this, stream_unget() failed on some situations. This patch makes
stream_get() handle EOF just like any other byte.

As a "side effect", lex_scan_string() now needs to unget the EOF, or
otherwise it ends up in error message on premature end of input.
2009-09-08 16:41:07 +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
ab2d93b724 Add CHANGES 2009-09-06 12:45:47 +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
325 changed files with 8420 additions and 1786 deletions

4
.gitignore vendored
View File

@@ -1,3 +1,4 @@
*~
*.o
*.a
.libs
@@ -8,6 +9,7 @@ aclocal.m4
autom4te.cache
config.guess
config.h
config.h.in
config.log
config.status
config.sub
@@ -21,3 +23,5 @@ missing
*.la
stamp-h1
*.pyc
*.pc
/src/jansson_config.h

352
CHANGES Normal file
View File

@@ -0,0 +1,352 @@
Version 2.2
===========
Released 2011-09-03
* New features:
- `json_dump_callback()`: Pass the encoder output to a callback
function in chunks.
* Bug fixes:
- `json_string_set()`: Check that target is a string and value is
not NULL.
* Other:
- Documentation typo fixes and clarifications.
Version 2.1
===========
Released 2011-06-10
* New features:
- `json_loadb()`: Decode a string with a given size, useful if the
string is not null terminated.
- Add ``JSON_ENCODE_ANY`` encoding flag to allow encoding any JSON
value. By default, only arrays and objects can be encoded. (#19)
- Add ``JSON_REJECT_DUPLICATES`` decoding flag to issue a decoding
error if any JSON object in the input contins duplicate keys. (#3)
- Add ``JSON_DISABLE_EOF_CHECK`` decoding flag to stop decoding after a
valid JSON input. This allows other data after the JSON data.
* Bug fixes:
- Fix an additional memory leak when memory allocation fails in
`json_object_set()` and friends.
- Clear errno before calling `strtod()` for better portability. (#27)
* Building:
- Avoid set-but-not-used warning/error in a test. (#20)
* Other:
- Minor clarifications to documentation.
Version 2.0.1
=============
Released 2011-03-31
* Bug fixes:
- Replace a few `malloc()` and `free()` calls with their
counterparts that support custom memory management.
- Fix object key hashing in json_unpack() strict checking mode.
- Fix the parentheses in ``JANSSON_VERSION_HEX`` macro.
- Fix `json_object_size()` return value.
- Fix a few compilation issues.
* Portability:
- Enhance portability of `va_copy()`.
- Test framework portability enhancements.
* Documentation:
- Distribute ``doc/upgrading.rst`` with the source tarball.
- Build documentation in strict mode in ``make distcheck``.
Version 2.0
===========
Released 2011-02-28
This release is backwards incompatible with the 1.x release series.
See the chapter "Upgrading from older versions" in documentation for
details.
* Backwards incompatible changes:
- Unify unsigned integer usage in the API: All occurences of
unsigned int and unsigned long have been replaced with size_t.
- Change JSON integer's underlying type to the widest signed integer
type available, i.e. long long if it's supported, otherwise long.
Add a typedef json_int_t that defines the type.
- Change the maximum indentation depth to 31 spaces in encoder. This
frees up bits from the flags parameter of encoding functions
`json_dumpf()`, `json_dumps()` and `json_dump_file()`.
- For future needs, add a flags parameter to all decoding functions
`json_loadf()`, `json_loads()` and `json_load_file()`.
* New features
- `json_pack()`, `json_pack_ex()`, `json_vpack_ex()`: Create JSON
values based on a format string.
- `json_unpack()`, `json_unpack_ex()`, `json_vunpack_ex()`: Simple
value extraction and validation functionality based on a format
string.
- Add column, position and source fields to the ``json_error_t``
struct.
- Enhance error reporting in the decoder.
- ``JANSSON_VERSION`` et al.: Preprocessor constants that define the
library version.
- `json_set_alloc_funcs()`: Set custom memory allocation functions.
* Fix many portability issues, especially on Windows.
* Configuration
- Add file ``jansson_config.h`` that contains site specific
configuration. It's created automatically by the configure script,
or can be created by hand if the configure script cannot be used.
The file ``jansson_config.h.win32`` can be used without
modifications on Windows systems.
- Add a section to documentation describing how to build Jansson on
Windows.
- Documentation now requires Sphinx 1.0 or newer.
Version 1.3
===========
Released 2010-06-13
* New functions:
- `json_object_iter_set()`, `json_object_iter_set_new()`: Change
object contents while iterating over it.
- `json_object_iter_at()`: Return an iterator that points to a
specific object item.
* New encoding flags:
- ``JSON_PRESERVE_ORDER``: Preserve the insertion order of object
keys.
* Bug fixes:
- Fix an error that occured when an array or object was first
encoded as empty, then populated with some data, and then
re-encoded
- Fix the situation like above, but when the first encoding resulted
in an error
* Documentation:
- Clarify the documentation on reference stealing, providing an
example usage pattern
Version 1.2.1
=============
Released 2010-04-03
* Bug fixes:
- Fix reference counting on ``true``, ``false`` and ``null``
- Estimate real number underflows in decoder with 0.0 instead of
issuing an error
* Portability:
- Make ``int32_t`` available on all systems
- Support compilers that don't have the ``inline`` keyword
- Require Autoconf 2.60 (for ``int32_t``)
* Tests:
- Print test names correctly when ``VERBOSE=1``
- ``test/suites/api``: Fail when a test fails
- Enhance tests for iterators
- Enhance tests for decoding texts that contain null bytes
* Documentation:
- Don't remove ``changes.rst`` in ``make clean``
- Add a chapter on RFC conformance
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
module is not found
* Distribute changelog (this file)
Version 1.0.2
=============
Released 2009-09-08
* Handle EOF correctly in decoder
Version 1.0.1
=============
Released 2009-09-04
* Fixed broken `json_is_boolean()`
Version 1.0
===========
Released 2009-08-25
* Initial release

View File

@@ -1,4 +1,4 @@
Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,2 +1,10 @@
EXTRA_DIST = LICENSE README.rst
EXTRA_DIST = CHANGES LICENSE README.rst
SUBDIRS = doc src test
# "make distcheck" builds the dvi target, so use it to check that the
# documentation is built correctly.
dvi:
$(MAKE) SPHINXOPTS_EXTRA=-W html
pkgconfigdir = $(libdir)/pkgconfig
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.
@@ -24,7 +24,13 @@ Compilation and Installation
If you obtained a source tarball, just use the standard autotools
commands::
$ ./configure && make && make install
$ ./configure
$ make
$ make install
To run the test suite, invoke::
$ make check
If the source has been checked out from a Git repository, the
./configure script has to be generated fist. The easiest way is to use
@@ -32,32 +38,22 @@ autoreconf::
$ autoreconf -i
To run the test suite, invoke::
$ make check
Python_ is required to run the tests.
Documentation
-------------
Documentation is in the ``doc/`` subdirectory. It's written in
reStructuredText_ with Sphinx_ annotations, so reading it in plain may
be inconvenient. For this reason, prebuilt HTML documentation is
available at http://www.digip.org/jansson/doc/.
Prebuilt HTML documentation is available at
http://www.digip.org/jansson/doc/.
To generate HTML documentation yourself, invoke::
The documentation source is in the ``doc/`` subdirectory. To generate
HTML documentation, invoke::
cd doc/
sphinx-build . .build/html
$ make html
... and point your browser to ``.build/html/index.html``. Sphinx_ is
required to generate the documentation.
Then, point your browser to ``doc/_build/html/index.html``. Sphinx_
1.0 or newer 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.1], [petri@digip.org])
AC_PREREQ([2.60])
AC_INIT([jansson], [2.2], [petri@digip.org])
AM_INIT_AUTOMAKE([1.10 foreign])
@@ -9,21 +9,41 @@ AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CC
AC_PROG_LIBTOOL
AM_CONDITIONAL([GCC], [test x$GCC = xyes])
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_INT32_T
AC_TYPE_LONG_LONG_INT
case $ac_cv_type_long_long_int in
yes) json_have_long_long=1;;
*) json_have_long_long=0;;
esac
AC_SUBST([json_have_long_long])
AC_C_INLINE
case $ac_cv_c_inline in
yes) json_inline=inline;;
no) json_inline=;;
*) json_inline=$ac_cv_c_inline;;
esac
AC_SUBST([json_inline])
# Checks for library functions.
AC_CONFIG_FILES([
jansson.pc
Makefile
doc/Makefile
src/Makefile
src/jansson_config.h
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,20 @@
EXTRA_DIST = conf.py apiref.rst index.rst ext/refcounting.py
EXTRA_DIST = conf.py apiref.rst changes.rst conformance.rst \
gettingstarted.rst github_commits.c index.rst tutorial.rst \
upgrading.rst ext/refcounting.py
SPHINXBUILD = sphinx-build
SPHINXOPTS = -d _build/doctrees $(SPHINXOPTS_EXTRA)
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

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.

File diff suppressed because it is too large Load Diff

5
doc/changes.rst Normal file
View File

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

View File

@@ -1,13 +1,10 @@
# -*- coding: utf-8 -*-
#
# Jansson documentation build configuration file, created by
# sphinx-quickstart on Thu Jul 30 11:35:32 2009.
# sphinx-quickstart on Sun Sep 5 21:47:20 2010.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# The contents of this file are pickled, so don't put values in the namespace
# that aren't pickleable (module imports are okay, they're removed automatically).
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
@@ -15,44 +12,45 @@
# serve to show the default.
import sys, os
sys.path.insert(0, os.path.abspath('ext'))
# If your extensions (or modules documented by autodoc) are in another directory,
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.append(os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('ext'))
# General configuration
# ---------------------
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['refcounting']
# Add any paths that contain templates here, relative to this directory.
templates_path = []
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8'
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'Jansson'
copyright = u'2009, Petri Lehtinen'
copyright = u'2009-2011, Petri Lehtinen'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '1.0'
version = '2.2'
# The full version, including alpha/beta/rc tags.
release = '1.0.1'
release = '2.2'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -64,15 +62,13 @@ release = '1.0.1'
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of documents that shouldn't be included in the build.
#unused_docs = []
# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = ['.build']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
default_role = 'c:func'
primary_domain = 'c'
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
@@ -88,14 +84,23 @@ exclude_trees = ['.build']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# Options for HTML output
# -----------------------
# The style sheet to use for HTML and HTML Help pages. A file of that name
# must exist either in Sphinx' static/ path, or in one of the custom paths
# given in html_static_path.
html_style = 'default.css'
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
@@ -116,7 +121,7 @@ html_style = 'default.css'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = []
#html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
@@ -134,7 +139,7 @@ html_static_path = []
#html_additional_pages = {}
# If false, no module index is generated.
#html_use_modindex = True
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
@@ -142,23 +147,28 @@ html_static_path = []
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, the reST sources are included in the HTML build as _sources/<name>.
#html_copy_source = True
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'Janssondoc'
# Options for LaTeX output
# ------------------------
# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
@@ -167,10 +177,10 @@ htmlhelp_basename = 'Janssondoc'
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, document class [howto/manual]).
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Jansson.tex', ur'Jansson Documentation',
ur'Petri Lehtinen', 'manual'),
('index', 'Jansson.tex', u'Jansson Documentation',
u'Petri Lehtinen', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
@@ -181,6 +191,12 @@ latex_documents = [
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
@@ -188,4 +204,14 @@ latex_documents = [
#latex_appendices = []
# If false, no module index is generated.
#latex_use_modindex = True
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'jansson', u'Jansson Documentation',
[u'Petri Lehtinen'], 1)
]

112
doc/conformance.rst Normal file
View File

@@ -0,0 +1,112 @@
.. _rfc-conformance:
***************
RFC Conformance
***************
JSON is specified in :rfc:`4627`, *"The application/json Media Type
for JavaScript Object Notation (JSON)"*. This chapter discusses
Jansson's conformance to this specification.
Character Encoding
==================
Jansson only supports UTF-8 encoded JSON texts. It does not support or
auto-detect any of the other encodings mentioned in the RFC, namely
UTF-16LE, UTF-16BE, UTF-32LE or UTF-32BE. Pure ASCII is supported, as
it's a subset of UTF-8.
Strings
=======
JSON strings are mapped to C-style null-terminated character arrays,
and UTF-8 encoding is used internally. Strings may not contain
embedded null characters, not even escaped ones.
For example, trying to decode the following JSON text leads to a parse
error::
["this string contains the null character: \u0000"]
All other Unicode codepoints U+0001 through U+10FFFF are allowed.
Unicode normalization or any other transformation is never performed
on any strings (string values or object keys). When checking for
equivalence of strings or object keys, the comparison is performed
byte by byte between the original UTF-8 representations of the
strings.
Numbers
=======
Real vs. Integer
----------------
JSON makes no distinction between real and integer numbers; Jansson
does. Real numbers are mapped to the ``double`` type and integers to
the ``json_int_t`` type, which is a typedef of ``long long`` or
``long``, depending on whether ``long long`` is supported by your
compiler or not.
A JSON number is considered to be a real number if its lexical
representation includes one of ``e``, ``E``, or ``.``; regardless if
its actual numeric value is a true integer (e.g., all of ``1E6``,
``3.0``, ``400E-2``, and ``3.14E3`` are mathematical integers, but
will be treated as real values).
All other JSON numbers are considered integers.
When encoding to JSON, real values are always represented
with a fractional part; e.g., the ``double`` value 3.0 will be
represented in JSON as ``3.0``, not ``3``.
Overflow, Underflow & Precision
-------------------------------
Real numbers whose absolute values are too small to be represented in
a C ``double`` will be silently estimated with 0.0. Thus, depending on
platform, JSON numbers very close to zero such as 1E-999 may result in
0.0.
Real numbers whose absolute values are too large to be represented in
a C ``double`` will result in an overflow error (a JSON decoding
error). Thus, depending on platform, JSON numbers like 1E+999 or
-1E+999 may result in a parsing error.
Likewise, integer numbers whose absolute values are too large to be
represented in the ``json_int_t`` type (see above) will result in an
overflow error (a JSON decoding error). Thus, depending on platform,
JSON numbers like 1000000000000000 may result in parsing error.
Parsing JSON real numbers may result in a loss of precision. As long
as overflow does not occur (i.e. a total loss of precision), the
rounded approximate value is silently used. Thus the JSON number
1.000000000000000005 may, depending on platform, result in the
``double`` value 1.0.
Signed zeros
------------
JSON makes no statement about what a number means; however Javascript
(ECMAscript) does state that +0.0 and -0.0 must be treated as being
distinct values, i.e. -0.0 |not-equal| 0.0. Jansson relies on the
underlying floating point library in the C environment in which it is
compiled. Therefore it is platform-dependent whether 0.0 and -0.0 will
be distinct values. Most platforms that use the IEEE 754
floating-point standard will support signed zeros.
Note that this only applies to floating-point; neither JSON, C, or
IEEE support the concept of signed integer zeros.
.. |not-equal| unicode:: U+2260
Types
-----
No support is provided in Jansson for any C numeric types other than
``json_int_t`` and ``double``. This excludes things such as unsigned
types, ``long double``, etc. Obviously, shorter types like ``short``,
``int``, ``long`` (if ``json_int_t`` is ``long long``) and ``float``
are implicitly handled via the ordinary C type coercion rules (subject
to overflow semantics). Also, no support or hooks are provided for any
supplemental "bignum" type add-on packages.

View File

@@ -19,7 +19,7 @@
<description of the json_object function>
:copyright: Copyright 2009 Petri Lehtinen <petri@digip.org>
:copyright: Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
:license: MIT, see LICENSE for details.
"""

123
doc/gettingstarted.rst Normal file
View File

@@ -0,0 +1,123 @@
***************
Getting Started
***************
.. highlight:: c
Compiling and Installing Jansson
================================
The Jansson source is available at
http://www.digip.org/jansson/releases/.
Unix-like systems
-----------------
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, the build system 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/
Other Systems
-------------
On Windows and other non Unix-like systems, you may be unable to run
the ``./configure`` script. In this case, follow these steps. All the
files mentioned can be found in the ``src/`` directory.
1. Create ``jansson_config.h``. This file has some platform-specific
parameters that are normally filled in by the ``./configure``
script:
- On Windows, rename ``jansson_config.h.win32`` to ``jansson_config.h``.
- On other systems, edit ``jansson_config.h.in``, replacing all
``@variable@`` placeholders, and rename the file to
``jansson_config.h``.
2. Make ``jansson.h`` and ``jansson_config.h`` available to the
compiler, so that they can be found when compiling programs that
use Jansson.
3. Compile all the ``.c`` files (in the ``src/`` directory) into a
library file. Make the library available to the compiler, as in
step 2.
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_ 1.0
or newer 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-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <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[])
{
size_t 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, 0, &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,43 @@
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
upgrading
tutorial
conformance
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::
size_t 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 :func:`json_loads()` to decode the JSON text we got
as a response::
root = json_loads(text, 0, &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 :func:`json_loads()` fails, it
returns *NULL* and sets error information to the :type:`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, :func:`json_object_get()`
returns *NULL*, but :func:`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 :func:`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 :func:`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 :func:`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
:func:`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 :func:`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.

76
doc/upgrading.rst Normal file
View File

@@ -0,0 +1,76 @@
.. highlight:: c
******************
Upgrading from 1.x
******************
This chapter lists the backwards incompatible changes introduced in
Jansson 2.0, and the steps that are needed for upgrading your code.
**The incompatibilities are not dramatic.** The biggest change is that
all decoding functions now require and extra parameter. Most programs
can be modified to work with 2.0 by adding a ``0`` as the second
parameter to all calls of :func:`json_loads()`, :func:`json_loadf()`
and :func:`json_load_file()`.
Compatibility
=============
Jansson 2.0 is backwards incompatible with the Jansson 1.x releases.
It is ABI incompatible, i.e. all programs dynamically linking to the
Jansson library need to be recompiled. It's also API incompatible,
i.e. the source code of programs using Jansson 1.x may need
modifications to make them compile against Jansson 2.0.
All the 2.x releases are guaranteed to be backwards compatible for
both ABI and API, so no recompilation or source changes are needed
when upgrading from 2.x to 2.y.
List of Incompatible Changes
============================
**Decoding flags**
For future needs, a ``flags`` parameter was added as the second
parameter to all decoding functions, i.e. :func:`json_loads()`,
:func:`json_loadf()` and :func:`json_load_file()`. All calls to
these functions need to be changed by adding a ``0`` as the second
argument. For example::
/* old code */
json_loads(input, &error);
/* new code */
json_loads(input, 0, &error);
**Underlying type of JSON integers**
The underlying C type of JSON integers has been changed from
:type:`int` to the widest available signed integer type, i.e.
:type:`long long` or :type:`long`, depending on whether
:type:`long long` is supported on your system or not. This makes
the whole 64-bit integer range available on most modern systems.
``jansson.h`` has a typedef :type:`json_int_t` to the underlying
integer type. :type:`int` should still be used in most cases when
dealing with smallish JSON integers, as the compiler handles
implicit type coercion. Only when the full 64-bit range is needed,
:type:`json_int_t` should be explicitly used.
**Maximum encoder indentation depth**
The maximum argument of the ``JSON_INDENT()`` macro has been
changed from 255 to 31, to free up bits from the ``flags``
parameter of :func:`json_dumps()`, :func:`json_dumpf()` and
:func:`json_dump_file()`. If your code uses a bigger indentation
than 31, it needs to be changed.
**Unsigned integers in API functions**
Version 2.0 unifies unsigned integer usage in the API. All uses of
:type:`unsigned int` and :type:`unsigned long` have been replaced
with :type:`size_t`. This includes flags, container sizes, etc.
This should not require source code changes, as both
:type:`unsigned int` and :type:`unsigned long` are usually
compatible with :type:`size_t`.

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

@@ -1,18 +1,25 @@
include_HEADERS = jansson.h
include_HEADERS = jansson.h jansson_config.h
lib_LTLIBRARIES = libjansson.la
libjansson_la_SOURCES = \
dump.c \
error.c \
hashtable.c \
hashtable.h \
jansson_private.h \
load.c \
memory.c \
pack_unpack.c \
strbuffer.c \
strbuffer.h \
utf.c \
utf.h \
util.h \
value.c
libjansson_la_LDFLAGS = -version-info 0:1:0
libjansson_la_LDFLAGS = \
-export-symbols-regex '^json_' \
-version-info 6:0:2
AM_CFLAGS = -Wall -Wextra -Werror -std=c99
if GCC
# These flags are gcc specific
AM_CFLAGS = -Wall -Wextra -Wdeclaration-after-statement -Werror
endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -9,12 +9,15 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <jansson.h>
#include "jansson_private.h"
#include "strbuffer.h"
#include "utf.h"
typedef int (*dump_func)(const char *buffer, int size, void *data);
#define MAX_INTEGER_STR_LENGTH 100
#define MAX_REAL_STR_LENGTH 100
struct string
{
@@ -23,12 +26,12 @@ struct string
int size;
};
static int dump_to_strbuffer(const char *buffer, int size, void *data)
static int dump_to_strbuffer(const char *buffer, size_t size, void *data)
{
return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
}
static int dump_to_file(const char *buffer, int size, void *data)
static int dump_to_file(const char *buffer, size_t size, void *data)
{
FILE *dest = (FILE *)data;
if(fwrite(buffer, size, 1, dest) != 1)
@@ -36,54 +39,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)
/* 32 spaces (the maximum indentation size) */
static char whitespace[] = " ";
static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t 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, json_dump_callback_t 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 +117,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 +145,29 @@ 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,
dump_func dump, void *data)
static int object_key_compare_keys(const void *key1, const void *key2)
{
return strcmp((*(const object_key_t **)key1)->key,
(*(const object_key_t **)key2)->key);
}
static int object_key_compare_serials(const void *key1, const void *key2)
{
return (*(const object_key_t **)key1)->serial -
(*(const object_key_t **)key2)->serial;
}
static int do_dump(const json_t *json, size_t flags, int depth,
json_dump_callback_t dump, void *data)
{
int ascii = flags & JSON_ENSURE_ASCII ? 1 : 0;
switch(json_typeof(json)) {
case JSON_NULL:
return dump("null", 4, data);
@@ -126,103 +180,231 @@ 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,
"%" JSON_INTEGER_FORMAT,
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)
goto array_error;
array->visited = 1;
n = json_array_size(json);
if(dump("[", 1, data))
return -1;
if(n == 0)
goto array_error;
if(n == 0) {
array->visited = 0;
return dump("]", 1, data);
if(dump_indent(flags, depth + 1, dump, data))
return -1;
}
if(dump_indent(flags, depth + 1, 0, dump, data))
goto array_error;
for(i = 0; i < n; ++i) {
if(do_dump(json_array_get(json, i), flags, depth + 1,
dump, data))
return -1;
goto array_error;
if(i < n - 1)
{
if(dump(",", 1, data) ||
dump_indent(flags, depth + 1, dump, data))
return -1;
dump_indent(flags, depth + 1, 1, dump, data))
goto array_error;
}
else
{
if(dump_indent(flags, depth, dump, data))
return -1;
if(dump_indent(flags, depth, 0, dump, data))
goto array_error;
}
}
array->visited = 0;
return dump("]", 1, data);
array_error:
array->visited = 0;
return -1;
}
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)
goto object_error;
object->visited = 1;
iter = json_object_iter((json_t *)json);
if(dump("{", 1, data))
return -1;
if(!iter)
goto object_error;
if(!iter) {
object->visited = 0;
return dump("}", 1, data);
if(dump_indent(flags, depth + 1, dump, data))
return -1;
while(iter)
{
void *next = json_object_iter_next((json_t *)json, iter);
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))
return -1;
if(next)
{
if(dump(",", 1, data) ||
dump_indent(flags, depth + 1, dump, data))
return -1;
}
else
{
if(dump_indent(flags, depth, dump, data))
return -1;
}
iter = next;
}
if(dump_indent(flags, depth + 1, 0, dump, data))
goto object_error;
if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER)
{
const object_key_t **keys;
size_t size, i;
int (*cmp_func)(const void *, const void *);
size = json_object_size(json);
keys = jsonp_malloc(size * sizeof(object_key_t *));
if(!keys)
goto object_error;
i = 0;
while(iter)
{
keys[i] = jsonp_object_iter_fullkey(iter);
iter = json_object_iter_next((json_t *)json, iter);
i++;
}
assert(i == size);
if(flags & JSON_SORT_KEYS)
cmp_func = object_key_compare_keys;
else
cmp_func = object_key_compare_serials;
qsort(keys, size, sizeof(object_key_t *), cmp_func);
for(i = 0; i < size; i++)
{
const char *key;
json_t *value;
key = keys[i]->key;
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))
{
jsonp_free(keys);
goto object_error;
}
if(i < size - 1)
{
if(dump(",", 1, data) ||
dump_indent(flags, depth + 1, 1, dump, data))
{
jsonp_free(keys);
goto object_error;
}
}
else
{
if(dump_indent(flags, depth, 0, dump, data))
{
jsonp_free(keys);
goto object_error;
}
}
}
jsonp_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))
goto object_error;
if(next)
{
if(dump(",", 1, data) ||
dump_indent(flags, depth + 1, 1, dump, data))
goto object_error;
}
else
{
if(dump_indent(flags, depth, 0, dump, data))
goto object_error;
}
iter = next;
}
}
object->visited = 0;
return dump("}", 1, data);
object_error:
object->visited = 0;
return -1;
}
default:
@@ -232,40 +414,41 @@ 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, size_t flags)
{
strbuffer_t strbuff;
char *result;
if(!json_is_array(json) && !json_is_object(json))
return NULL;
if(!(flags & JSON_ENCODE_ANY)) {
if(!json_is_array(json) && !json_is_object(json))
return NULL;
}
if(strbuffer_init(&strbuff))
return NULL;
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));
result = jsonp_strdup(strbuffer_value(&strbuff));
strbuffer_close(&strbuff);
return result;
}
int json_dumpf(const json_t *json, FILE *output, uint32_t flags)
int json_dumpf(const json_t *json, FILE *output, size_t flags)
{
if(!json_is_array(json) && !json_is_object(json))
return -1;
if(!(flags & JSON_ENCODE_ANY)) {
if(!json_is_array(json) && !json_is_object(json))
return -1;
}
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, size_t flags)
{
int result;
@@ -278,3 +461,13 @@ int json_dump_file(const json_t *json, const char *path, uint32_t flags)
fclose(output);
return result;
}
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
{
if(!(flags & JSON_ENCODE_ANY)) {
if(!json_is_array(json) && !json_is_object(json))
return -1;
}
return do_dump(json, flags, 0, callback, data);
}

62
src/error.c Normal file
View File

@@ -0,0 +1,62 @@
#include <string.h>
#include "jansson_private.h"
void jsonp_error_init(json_error_t *error, const char *source)
{
if(error)
{
error->text[0] = '\0';
error->line = -1;
error->column = -1;
error->position = 0;
if(source)
jsonp_error_set_source(error, source);
else
error->source[0] = '\0';
}
}
void jsonp_error_set_source(json_error_t *error, const char *source)
{
size_t length;
if(!error || !source)
return;
length = strlen(source);
if(length < JSON_ERROR_SOURCE_LENGTH)
strcpy(error->source, source);
else {
size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4;
strcpy(error->source, "...");
strcpy(error->source + 3, source + extra);
}
}
void jsonp_error_set(json_error_t *error, int line, int column,
size_t position, const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
jsonp_error_vset(error, line, column, position, msg, ap);
va_end(ap);
}
void jsonp_error_vset(json_error_t *error, int line, int column,
size_t position, const char *msg, va_list ap)
{
if(!error)
return;
if(error->text[0] != '\0') {
/* error already set */
return;
}
error->line = line;
error->column = column;
error->position = position;
vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH, msg, ap);
}

View File

@@ -1,29 +1,28 @@
/*
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* This library 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 <jansson_config.h> /* for JSON_INLINE */
#include "jansson_private.h" /* for container_of() */
#include "hashtable.h"
typedef struct hashtable_list list_t;
typedef struct hashtable_pair pair_t;
typedef struct hashtable_bucket bucket_t;
#define container_of(ptr_, type_, member_) \
((type_ *)((char *)ptr_ - (size_t)&((type_ *)0)->member_))
#define list_to_pair(list_) container_of(list_, pair_t, list)
static inline void list_init(list_t *list)
static JSON_INLINE void list_init(list_t *list)
{
list->next = list;
list->prev = list;
}
static inline void list_insert(list_t *list, list_t *node)
static JSON_INLINE void list_insert(list_t *list, list_t *node)
{
node->next = list;
node->prev = list->prev;
@@ -31,13 +30,13 @@ static inline void list_insert(list_t *list, list_t *node)
list->prev = node;
}
static inline void list_remove(list_t *list)
static JSON_INLINE void list_remove(list_t *list)
{
list->prev->next = list->next;
list->next->prev = list->prev;
}
static inline int bucket_is_empty(hashtable_t *hashtable, bucket_t *bucket)
static JSON_INLINE int bucket_is_empty(hashtable_t *hashtable, bucket_t *bucket)
{
return bucket->first == &hashtable->list && bucket->first == bucket->last;
}
@@ -57,22 +56,22 @@ static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket,
}
}
static unsigned int primes[] = {
static size_t primes[] = {
5, 13, 23, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593,
49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
12582917, 25165843, 50331653, 100663319, 201326611, 402653189,
805306457, 1610612741
};
static const unsigned int num_primes = sizeof(primes) / sizeof(unsigned int);
static const size_t num_primes = sizeof(primes) / sizeof(size_t);
static inline unsigned int num_buckets(hashtable_t *hashtable)
static JSON_INLINE size_t num_buckets(hashtable_t *hashtable)
{
return primes[hashtable->num_buckets];
}
static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
const void *key, unsigned int hash)
const void *key, size_t hash)
{
list_t *list;
pair_t *pair;
@@ -98,11 +97,11 @@ static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
/* returns 0 on success, -1 if key was not found */
static int hashtable_do_del(hashtable_t *hashtable,
const void *key, unsigned int hash)
const void *key, size_t hash)
{
pair_t *pair;
bucket_t *bucket;
unsigned int index;
size_t index;
index = hash % num_buckets(hashtable);
bucket = &hashtable->buckets[index];
@@ -127,24 +126,41 @@ static int hashtable_do_del(hashtable_t *hashtable,
if(hashtable->free_value)
hashtable->free_value(pair->value);
free(pair);
jsonp_free(pair);
hashtable->size--;
return 0;
}
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);
jsonp_free(pair);
}
}
static int hashtable_do_rehash(hashtable_t *hashtable)
{
list_t *list, *next;
pair_t *pair;
unsigned int i, index, new_size;
size_t i, index, new_size;
free(hashtable->buckets);
jsonp_free(hashtable->buckets);
hashtable->num_buckets++;
new_size = num_buckets(hashtable);
hashtable->buckets = malloc(new_size * sizeof(bucket_t));
hashtable->buckets = jsonp_malloc(new_size * sizeof(bucket_t));
if(!hashtable->buckets)
return -1;
@@ -171,13 +187,13 @@ static int hashtable_do_rehash(hashtable_t *hashtable)
hashtable_t *hashtable_create(key_hash_fn hash_key, key_cmp_fn cmp_keys,
free_fn free_key, free_fn free_value)
{
hashtable_t *hashtable = malloc(sizeof(hashtable_t));
hashtable_t *hashtable = jsonp_malloc(sizeof(hashtable_t));
if(!hashtable)
return NULL;
if(hashtable_init(hashtable, hash_key, cmp_keys, free_key, free_value))
{
free(hashtable);
jsonp_free(hashtable);
return NULL;
}
@@ -187,18 +203,18 @@ hashtable_t *hashtable_create(key_hash_fn hash_key, key_cmp_fn cmp_keys,
void hashtable_destroy(hashtable_t *hashtable)
{
hashtable_close(hashtable);
free(hashtable);
jsonp_free(hashtable);
}
int hashtable_init(hashtable_t *hashtable,
key_hash_fn hash_key, key_cmp_fn cmp_keys,
free_fn free_key, free_fn free_value)
{
unsigned int i;
size_t i;
hashtable->size = 0;
hashtable->num_buckets = 0; /* index to primes[] */
hashtable->buckets = malloc(num_buckets(hashtable) * sizeof(bucket_t));
hashtable->buckets = jsonp_malloc(num_buckets(hashtable) * sizeof(bucket_t));
if(!hashtable->buckets)
return -1;
@@ -220,60 +236,56 @@ 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);
}
free(hashtable->buckets);
hashtable_do_clear(hashtable);
jsonp_free(hashtable->buckets);
}
int hashtable_set(hashtable_t *hashtable, void *key, void *value)
{
pair_t *pair;
bucket_t *bucket;
unsigned int hash, index;
hash = hashtable->hash_key(key);
/* if the key already exists, delete it */
hashtable_do_del(hashtable, key, hash);
size_t hash, index;
/* rehash if the load ratio exceeds 1 */
if(hashtable->size >= num_buckets(hashtable))
if(hashtable_do_rehash(hashtable))
return -1;
pair = malloc(sizeof(pair_t));
if(!pair)
return -1;
pair->key = key;
pair->value = value;
pair->hash = hash;
list_init(&pair->list);
hash = hashtable->hash_key(key);
index = hash % num_buckets(hashtable);
bucket = &hashtable->buckets[index];
pair = hashtable_find_pair(hashtable, bucket, key, hash);
insert_to_bucket(hashtable, bucket, &pair->list);
if(pair)
{
if(hashtable->free_key)
hashtable->free_key(key);
if(hashtable->free_value)
hashtable->free_value(pair->value);
pair->value = value;
}
else
{
pair = jsonp_malloc(sizeof(pair_t));
if(!pair)
return -1;
hashtable->size++;
pair->key = key;
pair->value = value;
pair->hash = hash;
list_init(&pair->list);
insert_to_bucket(hashtable, bucket, &pair->list);
hashtable->size++;
}
return 0;
}
void *hashtable_get(hashtable_t *hashtable, const void *key)
{
pair_t *pair;
unsigned int hash;
size_t hash;
bucket_t *bucket;
hash = hashtable->hash_key(key);
@@ -288,15 +300,47 @@ void *hashtable_get(hashtable_t *hashtable, const void *key)
int hashtable_del(hashtable_t *hashtable, const void *key)
{
unsigned int hash = hashtable->hash_key(key);
size_t hash = hashtable->hash_key(key);
return hashtable_do_del(hashtable, key, hash);
}
void hashtable_clear(hashtable_t *hashtable)
{
size_t 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);
}
void *hashtable_iter_at(hashtable_t *hashtable, const void *key)
{
pair_t *pair;
size_t hash;
bucket_t *bucket;
hash = hashtable->hash_key(key);
bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
pair = hashtable_find_pair(hashtable, bucket, key, hash);
if(!pair)
return NULL;
return &pair->list;
}
void *hashtable_iter_next(hashtable_t *hashtable, void *iter)
{
list_t *list = (list_t *)iter;
@@ -316,3 +360,13 @@ void *hashtable_iter_value(void *iter)
pair_t *pair = list_to_pair((list_t *)iter);
return pair->value;
}
void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value)
{
pair_t *pair = list_to_pair((list_t *)iter);
if(hashtable->free_value)
hashtable->free_value(pair->value);
pair->value = value;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -8,19 +8,19 @@
#ifndef HASHTABLE_H
#define HASHTABLE_H
typedef unsigned int (*key_hash_fn)(const void *key);
typedef size_t (*key_hash_fn)(const void *key);
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 {
void *key;
void *value;
unsigned int hash;
size_t hash;
struct hashtable_list list;
};
@@ -30,9 +30,9 @@ struct hashtable_bucket {
};
typedef struct hashtable {
unsigned int size;
size_t size;
struct hashtable_bucket *buckets;
unsigned int num_buckets; /* index to primes[] */
size_t num_buckets; /* index to primes[] */
struct hashtable_list list;
key_hash_fn hash_key;
@@ -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
*
@@ -151,6 +160,17 @@ int hashtable_del(hashtable_t *hashtable, const void *key);
*/
void *hashtable_iter(hashtable_t *hashtable);
/**
* hashtable_iter_at - Return an iterator at a specific key
*
* @hashtable: The hashtable object
* @key: The key that the iterator should point to
*
* Like hashtable_iter() but returns an iterator pointing to a
* specific key.
*/
void *hashtable_iter_at(hashtable_t *hashtable, const void *key);
/**
* hashtable_iter_next - Advance an iterator
*
@@ -176,4 +196,12 @@ void *hashtable_iter_key(void *iter);
*/
void *hashtable_iter_value(void *iter);
/**
* hashtable_iter_set - Set the value pointed by an iterator
*
* @iter: The iterator
* @value: The value to set
*/
void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value);
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -9,7 +9,30 @@
#define JANSSON_H
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h> /* for size_t */
#include <stdarg.h>
#include <jansson_config.h>
#ifdef __cplusplus
extern "C" {
#endif
/* version */
#define JANSSON_MAJOR_VERSION 2
#define JANSSON_MINOR_VERSION 2
#define JANSSON_MICRO_VERSION 0
/* Micro version is omitted if it's 0 */
#define JANSSON_VERSION "2.2"
/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this
for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */
#define JANSSON_VERSION_HEX ((JANSSON_MAJOR_VERSION << 16) | \
(JANSSON_MINOR_VERSION << 8) | \
(JANSSON_MICRO_VERSION << 0))
/* types */
@@ -26,9 +49,17 @@ typedef enum {
typedef struct {
json_type type;
unsigned long refcount;
size_t refcount;
} json_t;
#if JSON_INTEGER_IS_LONG_LONG
#define JSON_INTEGER_FORMAT "lld"
typedef long long json_int_t;
#else
#define JSON_INTEGER_FORMAT "ld"
typedef long json_int_t;
#endif /* JSON_INTEGER_IS_LONG_LONG */
#define json_typeof(json) ((json)->type)
#define json_is_object(json) (json && json_typeof(json) == JSON_OBJECT)
#define json_is_array(json) (json && json_typeof(json) == JSON_ARRAY)
@@ -46,15 +77,17 @@ typedef struct {
json_t *json_object(void);
json_t *json_array(void);
json_t *json_string(const char *value);
json_t *json_integer(int value);
json_t *json_string_nocheck(const char *value);
json_t *json_integer(json_int_t value);
json_t *json_real(double value);
json_t *json_true(void);
json_t *json_false(void);
json_t *json_null(void);
static inline json_t *json_incref(json_t *json)
static JSON_INLINE
json_t *json_incref(json_t *json)
{
if(json)
if(json && json->refcount != (size_t)-1)
++json->refcount;
return json;
}
@@ -62,51 +95,161 @@ static inline json_t *json_incref(json_t *json)
/* do not call json_delete directly */
void json_delete(json_t *json);
static inline void json_decref(json_t *json)
static JSON_INLINE
void json_decref(json_t *json)
{
if(json && --json->refcount == 0)
if(json && json->refcount != (size_t)-1 && --json->refcount == 0)
json_delete(json);
}
/* error reporting */
#define JSON_ERROR_TEXT_LENGTH 160
#define JSON_ERROR_SOURCE_LENGTH 80
typedef struct {
int line;
int column;
int position;
char source[JSON_ERROR_SOURCE_LENGTH];
char text[JSON_ERROR_TEXT_LENGTH];
} json_error_t;
/* getters, setters, manipulation */
size_t 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_at(json_t *object, const char *key);
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);
int json_object_iter_set_new(json_t *object, void *iter, json_t *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);
static JSON_INLINE
int json_object_set(json_t *object, const char *key, json_t *value)
{
return json_object_set_new(object, key, json_incref(value));
}
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 JSON_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));
}
static JSON_INLINE
int json_object_iter_set(json_t *object, void *iter, json_t *value)
{
return json_object_iter_set_new(object, iter, json_incref(value));
}
size_t json_array_size(const json_t *array);
json_t *json_array_get(const json_t *array, size_t index);
int json_array_set_new(json_t *array, size_t index, json_t *value);
int json_array_append_new(json_t *array, json_t *value);
int json_array_insert_new(json_t *array, size_t index, json_t *value);
int json_array_remove(json_t *array, size_t index);
int json_array_clear(json_t *array);
int json_array_extend(json_t *array, json_t *other);
static JSON_INLINE
int json_array_set(json_t *array, size_t index, json_t *value)
{
return json_array_set_new(array, index, json_incref(value));
}
static JSON_INLINE
int json_array_append(json_t *array, json_t *value)
{
return json_array_append_new(array, json_incref(value));
}
static JSON_INLINE
int json_array_insert(json_t *array, size_t index, json_t *value)
{
return json_array_insert_new(array, index, json_incref(value));
}
const char *json_string_value(const json_t *string);
json_int_t 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, json_int_t value);
int json_real_set(json_t *real, double value);
/* loading, printing */
#define JSON_ERROR_TEXT_LENGTH 160
/* pack, unpack */
typedef struct {
char text[JSON_ERROR_TEXT_LENGTH];
int line;
} json_error_t;
json_t *json_pack(const char *fmt, ...);
json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...);
json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap);
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_VALIDATE_ONLY 0x1
#define JSON_STRICT 0x2
#define JSON_INDENT(n) (n & 0xFF)
int json_unpack(json_t *root, const char *fmt, ...);
int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...);
int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, va_list ap);
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);
/* 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);
/* decoding */
#define JSON_REJECT_DUPLICATES 0x1
#define JSON_DISABLE_EOF_CHECK 0x2
json_t *json_loads(const char *input, size_t flags, json_error_t *error);
json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error);
json_t *json_loadf(FILE *input, size_t flags, json_error_t *error);
json_t *json_load_file(const char *path, size_t flags, json_error_t *error);
/* encoding */
#define JSON_INDENT(n) (n & 0x1F)
#define JSON_COMPACT 0x20
#define JSON_ENSURE_ASCII 0x40
#define JSON_SORT_KEYS 0x80
#define JSON_PRESERVE_ORDER 0x100
#define JSON_ENCODE_ANY 0x200
typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);
char *json_dumps(const json_t *json, size_t flags);
int json_dumpf(const json_t *json, FILE *output, size_t flags);
int json_dump_file(const json_t *json, const char *path, size_t flags);
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags);
/* custom memory allocation */
typedef void *(*json_malloc_t)(size_t);
typedef void (*json_free_t)(void *);
void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn);
#ifdef __cplusplus
}
#endif
#endif

34
src/jansson_config.h.in Normal file
View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2010-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*
*
* This file specifies a part of the site-specific configuration for
* Jansson, namely those things that affect the public API in
* jansson.h.
*
* The configure script copies this file to jansson_config.h and
* replaces @var@ substitutions by values that fit your system. If you
* cannot run the configure script, you can do the value substitution
* by hand.
*/
#ifndef JANSSON_CONFIG_H
#define JANSSON_CONFIG_H
/* If your compiler supports the inline keyword in C, JSON_INLINE is
defined to `inline', otherwise empty. In C++, the inline is always
supported. */
#ifdef __cplusplus
#define JSON_INLINE inline
#else
#define JSON_INLINE @json_inline@
#endif
/* If your compiler supports the `long long` type,
JSON_INTEGER_IS_LONG_LONG is defined to 1, otherwise to 0. */
#define JSON_INTEGER_IS_LONG_LONG @json_have_long_long@
#endif

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2010-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*
*
* This file specifies a part of the site-specific configuration for
* Jansson, namely those things that affect the public API in
* jansson.h.
*
* The configure script copies this file to jansson_config.h and
* replaces @var@ substitutions by values that fit your system. If you
* cannot run the configure script, you can do the value substitution
* by hand.
*/
#ifndef JANSSON_CONFIG_H
#define JANSSON_CONFIG_H
/* If your compiler supports the inline keyword in C, JSON_INLINE is
defined to `inline', otherwise empty. In C++, the inline is always
supported. */
#ifdef __cplusplus
#define JSON_INLINE inline
#else
#define JSON_INLINE
#endif
/* If your compiler supports the `long long` type,
JSON_INTEGER_IS_LONG_LONG is defined to 1, otherwise to 0. */
#define JSON_INTEGER_IS_LONG_LONG 1
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -8,8 +8,84 @@
#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 <stddef.h>
#include "jansson.h"
#include "hashtable.h"
#define container_of(ptr_, type_, member_) \
((type_ *)((char *)ptr_ - offsetof(type_, member_)))
/* On some platforms, max() may already be defined */
#ifndef max
#define max(a, b) ((a) > (b) ? (a) : (b))
#endif
/* va_copy is a C99 feature. In C89 implementations, it's sometimes
available as __va_copy. If not, memcpy() should do the trick. */
#ifndef va_copy
#ifdef __va_copy
#define va_copy __va_copy
#else
#define va_copy(a, b) memcpy(&(a), &(b), sizeof(va_list))
#endif
#endif
typedef struct {
json_t json;
hashtable_t hashtable;
size_t serial;
int visited;
} json_object_t;
typedef struct {
json_t json;
size_t size;
size_t 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;
json_int_t 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)
size_t jsonp_hash_str(const void *ptr);
int jsonp_str_equal(const void *ptr1, const void *ptr2);
typedef struct {
size_t serial;
char key[1];
} object_key_t;
const object_key_t *jsonp_object_iter_fullkey(void *iter);
void jsonp_error_init(json_error_t *error, const char *source);
void jsonp_error_set_source(json_error_t *error, const char *source);
void jsonp_error_set(json_error_t *error, int line, int column,
size_t position, const char *msg, ...);
void jsonp_error_vset(json_error_t *error, int line, int column,
size_t position, const char *msg, va_list ap);
/* Wrappers for custom memory functions */
void* jsonp_malloc(size_t size);
void jsonp_free(void *ptr);
char *jsonp_strdup(const char *str);
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -8,11 +8,10 @@
#define _GNU_SOURCE
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <assert.h>
#include <jansson.h>
@@ -20,6 +19,10 @@
#include "strbuffer.h"
#include "utf.h"
#define STREAM_STATE_OK 0
#define STREAM_STATE_EOF -1
#define STREAM_STATE_ERROR -2
#define TOKEN_INVALID -1
#define TOKEN_EOF 0
#define TOKEN_STRING 256
@@ -29,115 +32,125 @@
#define TOKEN_FALSE 260
#define TOKEN_NULL 261
/* read one byte from stream, return EOF on end of file */
/* Read one byte from stream, convert to unsigned char, then int, and
return. return EOF on end of file. This corresponds to the
behaviour of fgetc(). */
typedef int (*get_func)(void *data);
/* return non-zero if end of file has been reached */
typedef int (*eof_func)(void *data);
typedef struct {
get_func get;
eof_func eof;
void *data;
int stream_pos;
char buffer[5];
int buffer_pos;
int state;
int line;
int column, last_column;
size_t position;
} stream_t;
typedef struct {
stream_t stream;
strbuffer_t saved_text;
int token;
int line, column;
union {
char *string;
int integer;
json_int_t integer;
double real;
} value;
} lex_t;
#define stream_to_lex(stream) container_of(stream, lex_t, stream)
/*** error reporting ***/
static void error_init(json_error_t *error)
{
if(error)
{
error->text[0] = '\0';
error->line = -1;
}
}
static void error_set(json_error_t *error, const lex_t *lex,
const char *msg, ...)
{
va_list ap;
char text[JSON_ERROR_TEXT_LENGTH];
char msg_text[JSON_ERROR_TEXT_LENGTH];
if(!error || error->text[0] != '\0') {
/* error already set */
int line = -1, col = -1;
size_t pos = 0;
const char *result = msg_text;
if(!error)
return;
}
va_start(ap, msg);
vsnprintf(text, JSON_ERROR_TEXT_LENGTH, msg, ap);
vsnprintf(msg_text, JSON_ERROR_TEXT_LENGTH, msg, ap);
va_end(ap);
if(lex)
{
const char *saved_text = strbuffer_value(&lex->saved_text);
error->line = lex->line;
char msg_with_context[JSON_ERROR_TEXT_LENGTH];
line = lex->stream.line;
col = lex->stream.column;
pos = lex->stream.position;
if(saved_text && saved_text[0])
{
if(lex->saved_text.length <= 20) {
snprintf(error->text, JSON_ERROR_TEXT_LENGTH,
"%s near '%s'", text, saved_text);
snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
"%s near '%s'", msg_text, saved_text);
result = msg_with_context;
}
else
snprintf(error->text, JSON_ERROR_TEXT_LENGTH, "%s", text);
}
else
{
snprintf(error->text, JSON_ERROR_TEXT_LENGTH,
"%s near end of file", text);
if(lex->stream.state == STREAM_STATE_ERROR) {
/* No context for UTF-8 decoding errors */
result = msg_text;
}
else {
snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
"%s near end of file", msg_text);
result = msg_with_context;
}
}
}
else
{
error->line = -1;
snprintf(error->text, JSON_ERROR_TEXT_LENGTH, "%s", text);
}
jsonp_error_set(error, line, col, pos, "%s", result);
}
/*** lexical analyzer ***/
void stream_init(stream_t *stream, get_func get, eof_func eof, void *data)
static void
stream_init(stream_t *stream, get_func get, void *data)
{
stream->get = get;
stream->eof = eof;
stream->data = data;
stream->stream_pos = 0;
stream->buffer[0] = '\0';
stream->buffer_pos = 0;
stream->state = STREAM_STATE_OK;
stream->line = 1;
stream->column = 0;
stream->position = 0;
}
static char stream_get(stream_t *stream, json_error_t *error)
static int stream_get(stream_t *stream, json_error_t *error)
{
char c;
int c;
if(stream->state != STREAM_STATE_OK)
return stream->state;
if(!stream->buffer[stream->buffer_pos])
{
stream->buffer[0] = stream->get(stream->data);
c = stream->get(stream->data);
if(c == EOF) {
stream->state = STREAM_STATE_EOF;
return STREAM_STATE_EOF;
}
stream->buffer[0] = c;
stream->buffer_pos = 0;
c = stream->buffer[0];
if(c == EOF && stream->eof(stream->data))
return EOF;
if(c < 0)
if(0x80 <= c && c <= 0xFF)
{
/* multi-byte UTF-8 sequence */
int i, count;
@@ -151,33 +164,50 @@ 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;
stream->buffer[count] = '\0';
}
else {
else
stream->buffer[1] = '\0';
stream->stream_pos++;
}
}
return stream->buffer[stream->buffer_pos++];
c = stream->buffer[stream->buffer_pos++];
stream->position++;
if(c == '\n') {
stream->line++;
stream->last_column = stream->column;
stream->column = 0;
}
else if(utf8_check_first(c)) {
/* track the Unicode character column, so increment only if
this is the first character of a UTF-8 sequence */
stream->column++;
}
return c;
out:
error_set(error, NULL, "unable to decode byte 0x%x at position %d",
(unsigned char)c, stream->stream_pos);
stream->buffer[0] = EOF;
stream->buffer[1] = '\0';
stream->buffer_pos = 1;
return EOF;
stream->state = STREAM_STATE_ERROR;
error_set(error, stream_to_lex(stream), "unable to decode byte 0x%x", c);
return STREAM_STATE_ERROR;
}
static void stream_unget(stream_t *stream, char c)
static void stream_unget(stream_t *stream, int c)
{
if(c == STREAM_STATE_EOF || c == STREAM_STATE_ERROR)
return;
stream->position--;
if(c == '\n') {
stream->line--;
stream->column = stream->last_column;
}
else if(utf8_check_first(c))
stream->column--;
assert(stream->buffer_pos > 0);
stream->buffer_pos--;
assert(stream->buffer[stream->buffer_pos] == c);
@@ -189,29 +219,32 @@ static int lex_get(lex_t *lex, json_error_t *error)
return stream_get(&lex->stream, error);
}
static int lex_eof(lex_t *lex)
{
return lex->stream.eof(lex->stream.data);
}
static void lex_save(lex_t *lex, char c)
static void lex_save(lex_t *lex, int c)
{
strbuffer_append_byte(&lex->saved_text, c);
}
static int lex_get_save(lex_t *lex, json_error_t *error)
{
char c = stream_get(&lex->stream, error);
lex_save(lex, c);
int c = stream_get(&lex->stream, error);
if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR)
lex_save(lex, c);
return c;
}
static void lex_unget_unsave(lex_t *lex, char c)
static void lex_unget(lex_t *lex, int c)
{
char d;
stream_unget(&lex->stream, c);
d = strbuffer_pop(&lex->saved_text);
assert(c == d);
}
static void lex_unget_unsave(lex_t *lex, int c)
{
if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR) {
char d;
stream_unget(&lex->stream, c);
d = strbuffer_pop(&lex->saved_text);
assert(c == d);
}
}
static void lex_save_cached(lex_t *lex)
@@ -220,14 +253,15 @@ static void lex_save_cached(lex_t *lex)
{
lex_save(lex, lex->stream.buffer[lex->stream.buffer_pos]);
lex->stream.buffer_pos++;
lex->stream.position++;
}
}
/* 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');
@@ -249,7 +283,7 @@ static int decode_unicode_escape(const char *str)
static void lex_scan_string(lex_t *lex, json_error_t *error)
{
char c;
int c;
const char *p;
char *t;
int i;
@@ -257,13 +291,14 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
lex->value.string = NULL;
lex->token = TOKEN_INVALID;
/* skip the " */
c = lex_get_save(lex, error);
while(c != '"') {
if(c == EOF) {
if(lex_eof(lex))
error_set(error, lex, "premature end of input");
if(c == STREAM_STATE_ERROR)
goto out;
else if(c == STREAM_STATE_EOF) {
error_set(error, lex, "premature end of input");
goto out;
}
@@ -283,7 +318,6 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
c = lex_get_save(lex, error);
for(i = 0; i < 4; i++) {
if(!isxdigit(c)) {
lex_unget_unsave(lex, c);
error_set(error, lex, "invalid escape");
goto out;
}
@@ -294,7 +328,6 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
c == 'f' || c == 'n' || c == 'r' || c == 't')
c = lex_get_save(lex, error);
else {
lex_unget_unsave(lex, c);
error_set(error, lex, "invalid escape");
goto out;
}
@@ -310,7 +343,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
- two \uXXXX escapes (length 12) forming an UTF-16 surrogate pair
are converted to 4 bytes
*/
lex->value.string = malloc(lex->saved_text.length + 1);
lex->value.string = jsonp_malloc(lex->saved_text.length + 1);
if(!lex->value.string) {
/* this is not very nice, since TOKEN_INVALID is returned */
goto out;
@@ -328,7 +361,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;
@@ -336,14 +369,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 */
@@ -399,13 +433,20 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
return;
out:
free(lex->value.string);
jsonp_free(lex->value.string);
}
static void lex_scan_number(lex_t *lex, char c, json_error_t *error)
#if JSON_INTEGER_IS_LONG_LONG
#define json_strtoint strtoll
#else
#define json_strtoint strtol
#endif
static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
{
const char *saved_text;
char *end;
double value;
lex->token = TOKEN_INVALID;
@@ -419,27 +460,46 @@ static void 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') {
json_int_t value;
lex_unget_unsave(lex, c);
lex->token = TOKEN_INTEGER;
saved_text = strbuffer_value(&lex->saved_text);
lex->value.integer = strtol(saved_text, &end, 10);
errno = 0;
value = json_strtoint(saved_text, &end, 10);
if(errno == ERANGE) {
if(value < 0)
error_set(error, lex, "too big negative integer");
else
error_set(error, lex, "too big integer");
goto out;
}
assert(end == saved_text + lex->saved_text.length);
return;
lex->token = TOKEN_INTEGER;
lex->value.integer = value;
return 0;
}
if(c == '.') {
c = lex_get(lex, error);
if(!isdigit(c))
if(!isdigit(c)) {
lex_unget(lex, c);
goto out;
}
lex_save(lex, c);
c = lex_get_save(lex, error);
@@ -463,41 +523,47 @@ static void lex_scan_number(lex_t *lex, char c, json_error_t *error)
}
lex_unget_unsave(lex, c);
lex->token = TOKEN_REAL;
saved_text = strbuffer_value(&lex->saved_text);
lex->value.real = strtod(saved_text, &end);
errno = 0;
value = strtod(saved_text, &end);
assert(end == saved_text + lex->saved_text.length);
if(errno == ERANGE && value != 0) {
error_set(error, lex, "real number overflow");
goto out;
}
lex->token = TOKEN_REAL;
lex->value.real = value;
return 0;
out:
return;
return -1;
}
static int lex_scan(lex_t *lex, json_error_t *error)
{
char c;
int c;
strbuffer_clear(&lex->saved_text);
if(lex->token == TOKEN_STRING) {
free(lex->value.string);
lex->value.string = NULL;
jsonp_free(lex->value.string);
lex->value.string = NULL;
}
c = lex_get(lex, error);
while(c == ' ' || c == '\t' || c == '\n' || c == '\r')
{
if(c == '\n')
lex->line++;
c = lex_get(lex, error);
if(c == STREAM_STATE_EOF) {
lex->token = TOKEN_EOF;
goto out;
}
if(c == EOF) {
if(lex_eof(lex))
lex->token = TOKEN_EOF;
else
lex->token = TOKEN_INVALID;
if(c == STREAM_STATE_ERROR) {
lex->token = TOKEN_INVALID;
goto out;
}
@@ -509,8 +575,10 @@ static int lex_scan(lex_t *lex, json_error_t *error)
else if(c == '"')
lex_scan_string(lex, error);
else if(isdigit(c) || c == '-')
lex_scan_number(lex, c, error);
else if(isdigit(c) || c == '-') {
if(lex_scan_number(lex, c, error))
goto out;
}
else if(isupper(c) || islower(c)) {
/* eat up the whole identifier for clearer error messages */
@@ -544,31 +612,40 @@ out:
return lex->token;
}
static int lex_init(lex_t *lex, get_func get, eof_func eof, void *data)
static char *lex_steal_string(lex_t *lex)
{
stream_init(&lex->stream, get, eof, data);
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, void *data)
{
stream_init(&lex->stream, get, data);
if(strbuffer_init(&lex->saved_text))
return -1;
lex->token = TOKEN_INVALID;
lex->line = 1;
return 0;
}
static void lex_close(lex_t *lex)
{
if(lex->token == TOKEN_STRING)
free(lex->value.string);
jsonp_free(lex->value.string);
strbuffer_close(&lex->saved_text);
}
/*** parser ***/
static json_t *parse_value(lex_t *lex, json_error_t *error);
static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error);
static json_t *parse_object(lex_t *lex, json_error_t *error)
static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
{
json_t *object = json_object();
if(!object)
@@ -587,32 +664,40 @@ 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;
if(flags & JSON_REJECT_DUPLICATES) {
if(json_object_get(object, key)) {
jsonp_free(key);
error_set(error, lex, "duplicate object key");
goto error;
}
}
lex_scan(lex, error);
if(lex->token != ':') {
free(key);
jsonp_free(key);
error_set(error, lex, "':' expected");
goto error;
}
lex_scan(lex, error);
value = parse_value(lex, error);
value = parse_value(lex, flags, error);
if(!value) {
free(key);
jsonp_free(key);
goto error;
}
if(json_object_set_nocheck(object, key, value)) {
free(key);
jsonp_free(key);
json_decref(value);
goto error;
}
json_decref(value);
free(key);
jsonp_free(key);
lex_scan(lex, error);
if(lex->token != ',')
@@ -633,7 +718,7 @@ error:
return NULL;
}
static json_t *parse_array(lex_t *lex, json_error_t *error)
static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error)
{
json_t *array = json_array();
if(!array)
@@ -644,7 +729,7 @@ static json_t *parse_array(lex_t *lex, json_error_t *error)
return array;
while(lex->token) {
json_t *elem = parse_value(lex, error);
json_t *elem = parse_value(lex, flags, error);
if(!elem)
goto error;
@@ -673,7 +758,7 @@ error:
return NULL;
}
static json_t *parse_value(lex_t *lex, json_error_t *error)
static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
{
json_t *json;
@@ -706,11 +791,11 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
break;
case '{':
json = parse_object(lex, error);
json = parse_object(lex, flags, error);
break;
case '[':
json = parse_array(lex, error);
json = parse_array(lex, flags, error);
break;
case TOKEN_INVALID:
@@ -728,9 +813,9 @@ 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, size_t flags, json_error_t *error)
{
error_init(error);
json_t *result;
lex_scan(lex, error);
if(lex->token != '[' && lex->token != '{') {
@@ -738,7 +823,20 @@ json_t *parse_json(lex_t *lex, json_error_t *error)
return NULL;
}
return parse_value(lex, error);
result = parse_value(lex, flags, error);
if(!result)
return NULL;
if(!(flags & JSON_DISABLE_EOF_CHECK)) {
lex_scan(lex, error);
if(lex->token != TOKEN_EOF) {
error_set(error, lex, "end of file expected");
json_decref(result);
result = NULL;
}
}
return result;
}
typedef struct
@@ -757,74 +855,96 @@ static int string_get(void *data)
else
{
stream->pos++;
return c;
return (unsigned char)c;
}
}
static int string_eof(void *data)
{
string_data_t *stream = (string_data_t *)data;
return (stream->data[stream->pos] == '\0');
}
json_t *json_loads(const char *string, json_error_t *error)
json_t *json_loads(const char *string, size_t flags, json_error_t *error)
{
lex_t lex;
json_t *result;
string_data_t stream_data;
string_data_t stream_data = {
.data = string,
.pos = 0
};
stream_data.data = string;
stream_data.pos = 0;
if(lex_init(&lex, string_get, string_eof, (void *)&stream_data))
if(lex_init(&lex, string_get, (void *)&stream_data))
return NULL;
result = parse_json(&lex, error);
if(!result)
goto out;
jsonp_error_init(error, "<string>");
result = parse_json(&lex, flags, error);
lex_scan(&lex, error);
if(lex.token != TOKEN_EOF) {
error_set(error, &lex, "end of file expected");
json_decref(result);
result = NULL;
}
out:
lex_close(&lex);
return result;
}
json_t *json_loadf(FILE *input, json_error_t *error)
typedef struct
{
const char *data;
size_t len;
size_t pos;
} buffer_data_t;
static int buffer_get(void *data)
{
char c;
buffer_data_t *stream = data;
if(stream->pos >= stream->len)
return EOF;
c = stream->data[stream->pos];
stream->pos++;
return (unsigned char)c;
}
json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error)
{
lex_t lex;
json_t *result;
buffer_data_t stream_data;
if(lex_init(&lex, (get_func)fgetc, (eof_func)feof, input))
stream_data.data = buffer;
stream_data.pos = 0;
stream_data.len = buflen;
if(lex_init(&lex, buffer_get, (void *)&stream_data))
return NULL;
result = parse_json(&lex, error);
if(!result)
goto out;
jsonp_error_init(error, "<buffer>");
result = parse_json(&lex, flags, error);
lex_scan(&lex, error);
if(lex.token != TOKEN_EOF) {
error_set(error, &lex, "end of file expected");
json_decref(result);
result = NULL;
}
out:
lex_close(&lex);
return result;
}
json_t *json_load_file(const char *path, json_error_t *error)
json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
{
lex_t lex;
const char *source;
json_t *result;
if(lex_init(&lex, (get_func)fgetc, input))
return NULL;
if(input == stdin)
source = "<stdin>";
else
source = "<stream>";
jsonp_error_init(error, source);
result = parse_json(&lex, flags, error);
lex_close(&lex);
return result;
}
json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
{
json_t *result;
FILE *fp;
jsonp_error_init(error, path);
fp = fopen(path, "r");
if(!fp)
{
@@ -833,7 +953,7 @@ json_t *json_load_file(const char *path, json_error_t *error)
return NULL;
}
result = json_loadf(fp, error);
result = json_loadf(fp, flags, error);
fclose(fp);
return result;

51
src/memory.c Normal file
View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2011 Basile Starynkevitch <basile@starynkevitch.net>
*
* 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 "jansson_private.h"
/* memory function pointers */
static json_malloc_t do_malloc = malloc;
static json_free_t do_free = free;
void *jsonp_malloc(size_t size)
{
if(!size)
return NULL;
return (*do_malloc)(size);
}
void jsonp_free(void *ptr)
{
if(!ptr)
return;
(*do_free)(ptr);
}
char *jsonp_strdup(const char *str)
{
char *new_str;
new_str = jsonp_malloc(strlen(str) + 1);
if(!new_str)
return NULL;
strcpy(new_str, str);
return new_str;
}
void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn)
{
do_malloc = malloc_fn;
do_free = free_fn;
}

610
src/pack_unpack.c Normal file
View File

@@ -0,0 +1,610 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2011 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
*
* 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 "jansson_private.h"
#include "utf.h"
typedef struct {
const char *start;
const char *fmt;
char token;
json_error_t *error;
size_t flags;
int line;
int column;
} scanner_t;
static const char *type_names[] = {
"object",
"array",
"string",
"integer",
"real",
"true",
"false",
"null"
};
#define type_name(x) type_names[json_typeof(x)]
static const char *unpack_value_starters = "{[siIbfFOon";
static void scanner_init(scanner_t *s, json_error_t *error,
size_t flags, const char *fmt)
{
s->error = error;
s->flags = flags;
s->fmt = s->start = fmt;
s->line = 1;
s->column = 0;
}
static void next_token(scanner_t *s)
{
const char *t = s->fmt;
s->column++;
/* skip space and ignored chars */
while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') {
if(*t == '\n') {
s->line++;
s->column = 1;
}
else
s->column++;
t++;
}
s->token = *t;
t++;
s->fmt = t;
}
static void set_error(scanner_t *s, const char *source, const char *fmt, ...)
{
va_list ap;
size_t pos;
va_start(ap, fmt);
pos = (size_t)(s->fmt - s->start);
jsonp_error_vset(s->error, s->line, s->column, pos, fmt, ap);
jsonp_error_set_source(s->error, source);
va_end(ap);
}
static json_t *pack(scanner_t *s, va_list *ap);
static json_t *pack_object(scanner_t *s, va_list *ap)
{
json_t *object = json_object();
next_token(s);
while(s->token != '}') {
const char *key;
json_t *value;
if(!s->token) {
set_error(s, "<format>", "Unexpected end of format string");
goto error;
}
if(s->token != 's') {
set_error(s, "<format>", "Expected format 's', got '%c'", s->token);
goto error;
}
key = va_arg(*ap, const char *);
if(!key) {
set_error(s, "<args>", "NULL object key");
goto error;
}
if(!utf8_check_string(key, -1)) {
set_error(s, "<args>", "Invalid UTF-8 in object key");
goto error;
}
next_token(s);
value = pack(s, ap);
if(!value)
goto error;
if(json_object_set_new_nocheck(object, key, value)) {
set_error(s, "<internal>", "Unable to add key \"%s\"", key);
goto error;
}
next_token(s);
}
return object;
error:
json_decref(object);
return NULL;
}
static json_t *pack_array(scanner_t *s, va_list *ap)
{
json_t *array = json_array();
next_token(s);
while(s->token != ']') {
json_t *value;
if(!s->token) {
set_error(s, "<format>", "Unexpected end of format string");
goto error;
}
value = pack(s, ap);
if(!value)
goto error;
if(json_array_append_new(array, value)) {
set_error(s, "<internal>", "Unable to append to array");
goto error;
}
next_token(s);
}
return array;
error:
json_decref(array);
return NULL;
}
static json_t *pack(scanner_t *s, va_list *ap)
{
switch(s->token) {
case '{':
return pack_object(s, ap);
case '[':
return pack_array(s, ap);
case 's': /* string */
{
const char *str = va_arg(*ap, const char *);
if(!str) {
set_error(s, "<args>", "NULL string argument");
return NULL;
}
if(!utf8_check_string(str, -1)) {
set_error(s, "<args>", "Invalid UTF-8 string");
return NULL;
}
return json_string_nocheck(str);
}
case 'n': /* null */
return json_null();
case 'b': /* boolean */
return va_arg(*ap, int) ? json_true() : json_false();
case 'i': /* integer from int */
return json_integer(va_arg(*ap, int));
case 'I': /* integer from json_int_t */
return json_integer(va_arg(*ap, json_int_t));
case 'f': /* real */
return json_real(va_arg(*ap, double));
case 'O': /* a json_t object; increments refcount */
return json_incref(va_arg(*ap, json_t *));
case 'o': /* a json_t object; doesn't increment refcount */
return va_arg(*ap, json_t *);
default:
set_error(s, "<format>", "Unexpected format character '%c'",
s->token);
return NULL;
}
}
static int unpack(scanner_t *s, json_t *root, va_list *ap);
static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
{
int ret = -1;
int strict = 0;
/* Use a set (emulated by a hashtable) to check that all object
keys are accessed. Checking that the correct number of keys
were accessed is not enough, as the same key can be unpacked
multiple times.
*/
hashtable_t key_set;
if(hashtable_init(&key_set, jsonp_hash_str, jsonp_str_equal, NULL, NULL)) {
set_error(s, "<internal>", "Out of memory");
return -1;
}
if(!json_is_object(root)) {
set_error(s, "<validation>", "Expected object, got %s",
type_name(root));
goto out;
}
next_token(s);
while(s->token != '}') {
const char *key;
json_t *value;
if(strict != 0) {
set_error(s, "<format>", "Expected '}' after '%c', got '%c'",
(strict == 1 ? '!' : '*'), s->token);
goto out;
}
if(!s->token) {
set_error(s, "<format>", "Unexpected end of format string");
goto out;
}
if(s->token == '!' || s->token == '*') {
strict = (s->token == '!' ? 1 : -1);
next_token(s);
continue;
}
if(s->token != 's') {
set_error(s, "<format>", "Expected format 's', got '%c'", s->token);
goto out;
}
key = va_arg(*ap, const char *);
if(!key) {
set_error(s, "<args>", "NULL object key");
goto out;
}
next_token(s);
value = json_object_get(root, key);
if(!value) {
set_error(s, "<validation>", "Object item not found: %s", key);
goto out;
}
if(unpack(s, value, ap))
goto out;
hashtable_set(&key_set, (void *)key, NULL);
next_token(s);
}
if(strict == 0 && (s->flags & JSON_STRICT))
strict = 1;
if(strict == 1 && key_set.size != json_object_size(root)) {
long diff = (long)json_object_size(root) - (long)key_set.size;
set_error(s, "<validation>", "%li object item(s) left unpacked", diff);
goto out;
}
ret = 0;
out:
hashtable_close(&key_set);
return ret;
}
static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
{
size_t i = 0;
int strict = 0;
if(!json_is_array(root)) {
set_error(s, "<validation>", "Expected array, got %s", type_name(root));
return -1;
}
next_token(s);
while(s->token != ']') {
json_t *value;
if(strict != 0) {
set_error(s, "<format>", "Expected ']' after '%c', got '%c'",
(strict == 1 ? '!' : '*'),
s->token);
return -1;
}
if(!s->token) {
set_error(s, "<format>", "Unexpected end of format string");
return -1;
}
if(s->token == '!' || s->token == '*') {
strict = (s->token == '!' ? 1 : -1);
next_token(s);
continue;
}
if(!strchr(unpack_value_starters, s->token)) {
set_error(s, "<format>", "Unexpected format character '%c'",
s->token);
return -1;
}
value = json_array_get(root, i);
if(!value) {
set_error(s, "<validation>", "Array index %lu out of range",
(unsigned long)i);
return -1;
}
if(unpack(s, value, ap))
return -1;
next_token(s);
i++;
}
if(strict == 0 && (s->flags & JSON_STRICT))
strict = 1;
if(strict == 1 && i != json_array_size(root)) {
long diff = (long)json_array_size(root) - (long)i;
set_error(s, "<validation>", "%li array item(s) left unpacked", diff);
return -1;
}
return 0;
}
static int unpack(scanner_t *s, json_t *root, va_list *ap)
{
switch(s->token)
{
case '{':
return unpack_object(s, root, ap);
case '[':
return unpack_array(s, root, ap);
case 's':
if(!json_is_string(root)) {
set_error(s, "<validation>", "Expected string, got %s",
type_name(root));
return -1;
}
if(!(s->flags & JSON_VALIDATE_ONLY)) {
const char **str;
str = va_arg(*ap, const char **);
if(!str) {
set_error(s, "<args>", "NULL string argument");
return -1;
}
*str = json_string_value(root);
}
return 0;
case 'i':
if(!json_is_integer(root)) {
set_error(s, "<validation>", "Expected integer, got %s",
type_name(root));
return -1;
}
if(!(s->flags & JSON_VALIDATE_ONLY))
*va_arg(*ap, int*) = json_integer_value(root);
return 0;
case 'I':
if(!json_is_integer(root)) {
set_error(s, "<validation>", "Expected integer, got %s",
type_name(root));
return -1;
}
if(!(s->flags & JSON_VALIDATE_ONLY))
*va_arg(*ap, json_int_t*) = json_integer_value(root);
return 0;
case 'b':
if(!json_is_boolean(root)) {
set_error(s, "<validation>", "Expected true or false, got %s",
type_name(root));
return -1;
}
if(!(s->flags & JSON_VALIDATE_ONLY))
*va_arg(*ap, int*) = json_is_true(root);
return 0;
case 'f':
if(!json_is_real(root)) {
set_error(s, "<validation>", "Expected real, got %s",
type_name(root));
return -1;
}
if(!(s->flags & JSON_VALIDATE_ONLY))
*va_arg(*ap, double*) = json_real_value(root);
return 0;
case 'F':
if(!json_is_number(root)) {
set_error(s, "<validation>", "Expected real or integer, got %s",
type_name(root));
return -1;
}
if(!(s->flags & JSON_VALIDATE_ONLY))
*va_arg(*ap, double*) = json_number_value(root);
return 0;
case 'O':
if(!(s->flags & JSON_VALIDATE_ONLY))
json_incref(root);
/* Fall through */
case 'o':
if(!(s->flags & JSON_VALIDATE_ONLY))
*va_arg(*ap, json_t**) = root;
return 0;
case 'n':
/* Never assign, just validate */
if(!json_is_null(root)) {
set_error(s, "<validation>", "Expected null, got %s",
type_name(root));
return -1;
}
return 0;
default:
set_error(s, "<format>", "Unexpected format character '%c'",
s->token);
return -1;
}
}
json_t *json_vpack_ex(json_error_t *error, size_t flags,
const char *fmt, va_list ap)
{
scanner_t s;
va_list ap_copy;
json_t *value;
if(!fmt || !*fmt) {
jsonp_error_init(error, "<format>");
jsonp_error_set(error, -1, -1, 0, "NULL or empty format string");
return NULL;
}
jsonp_error_init(error, NULL);
scanner_init(&s, error, flags, fmt);
next_token(&s);
va_copy(ap_copy, ap);
value = pack(&s, &ap_copy);
va_end(ap_copy);
if(!value)
return NULL;
next_token(&s);
if(s.token) {
json_decref(value);
set_error(&s, "<format>", "Garbage after format string");
return NULL;
}
return value;
}
json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...)
{
json_t *value;
va_list ap;
va_start(ap, fmt);
value = json_vpack_ex(error, flags, fmt, ap);
va_end(ap);
return value;
}
json_t *json_pack(const char *fmt, ...)
{
json_t *value;
va_list ap;
va_start(ap, fmt);
value = json_vpack_ex(NULL, 0, fmt, ap);
va_end(ap);
return value;
}
int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags,
const char *fmt, va_list ap)
{
scanner_t s;
va_list ap_copy;
if(!root) {
jsonp_error_init(error, "<root>");
jsonp_error_set(error, -1, -1, 0, "NULL root value");
return -1;
}
if(!fmt || !*fmt) {
jsonp_error_init(error, "<format>");
jsonp_error_set(error, -1, -1, 0, "NULL or empty format string");
return -1;
}
jsonp_error_init(error, NULL);
scanner_init(&s, error, flags, fmt);
next_token(&s);
va_copy(ap_copy, ap);
if(unpack(&s, root, &ap_copy)) {
va_end(ap_copy);
return -1;
}
va_end(ap_copy);
next_token(&s);
if(s.token) {
set_error(&s, "<format>", "Garbage after format string");
return -1;
}
return 0;
}
int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...)
{
int ret;
va_list ap;
va_start(ap, fmt);
ret = json_vunpack_ex(root, error, flags, fmt, ap);
va_end(ap);
return ret;
}
int json_unpack(json_t *root, const char *fmt, ...)
{
int ret;
va_list ap;
va_start(ap, fmt);
ret = json_vunpack_ex(root, NULL, 0, fmt, ap);
va_end(ap);
return ret;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -8,18 +8,19 @@
#define _GNU_SOURCE
#include <stdlib.h>
#include <string.h>
#include "jansson_private.h"
#include "strbuffer.h"
#include "util.h"
#define STRBUFFER_MIN_SIZE 16
#define STRBUFFER_FACTOR 2
#define STRBUFFER_SIZE_MAX ((size_t)-1)
int strbuffer_init(strbuffer_t *strbuff)
{
strbuff->size = STRBUFFER_MIN_SIZE;
strbuff->length = 0;
strbuff->value = malloc(strbuff->size);
strbuff->value = jsonp_malloc(strbuff->size);
if(!strbuff->value)
return -1;
@@ -30,7 +31,7 @@ int strbuffer_init(strbuffer_t *strbuff)
void strbuffer_close(strbuffer_t *strbuff)
{
free(strbuff->value);
jsonp_free(strbuff->value);
strbuff->size = 0;
strbuff->length = 0;
strbuff->value = NULL;
@@ -64,16 +65,31 @@ int strbuffer_append_byte(strbuffer_t *strbuff, char byte)
return strbuffer_append_bytes(strbuff, &byte, 1);
}
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, int size)
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size)
{
if(strbuff->length + size >= strbuff->size)
if(size >= strbuff->size - strbuff->length)
{
strbuff->size = max(strbuff->size * STRBUFFER_FACTOR,
strbuff->length + size + 1);
size_t new_size;
char *new_value;
strbuff->value = realloc(strbuff->value, strbuff->size);
if(!strbuff->value)
/* avoid integer overflow */
if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR
|| size > STRBUFFER_SIZE_MAX - 1
|| strbuff->length > STRBUFFER_SIZE_MAX - 1 - size)
return -1;
new_size = max(strbuff->size * STRBUFFER_FACTOR,
strbuff->length + size + 1);
new_value = jsonp_malloc(new_size);
if(!new_value)
return -1;
memcpy(new_value, strbuff->value, strbuff->length);
jsonp_free(strbuff->value);
strbuff->value = new_value;
strbuff->size = new_size;
}
memcpy(strbuff->value + strbuff->length, data, size);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -10,8 +10,8 @@
typedef struct {
char *value;
int length; /* bytes used */
int size; /* bytes allocated */
size_t length; /* bytes used */
size_t size; /* bytes allocated */
} strbuffer_t;
int strbuffer_init(strbuffer_t *strbuff);
@@ -24,7 +24,7 @@ char *strbuffer_steal_value(strbuffer_t *strbuff);
int strbuffer_append(strbuffer_t *strbuff, const char *string);
int strbuffer_append_byte(strbuffer_t *strbuff, char byte);
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, int size);
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size);
char strbuffer_pop(strbuffer_t *strbuff);

View File

@@ -1,13 +1,14 @@
/*
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <string.h>
#include "utf.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

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -8,10 +8,31 @@
#ifndef UTF_H
#define UTF_H
#ifdef HAVE_CONFIG_H
#include <config.h>
#ifdef HAVE_INTTYPES_H
/* inttypes.h includes stdint.h in a standard environment, so there's
no need to include stdint.h separately. If inttypes.h doesn't define
int32_t, it's defined in config.h. */
#include <inttypes.h>
#endif /* HAVE_INTTYPES_H */
#else /* !HAVE_CONFIG_H */
#ifdef _WIN32
typedef int int32_t;
#else /* !_WIN32 */
/* Assume a standard environment */
#include <inttypes.h>
#endif /* _WIN32 */
#endif /* HAVE_CONFIG_H */
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

@@ -1,13 +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.
*/
#ifndef UTIL_H
#define UTIL_H
#define max(a, b) ((a) > (b) ? (a) : (b))
#endif

File diff suppressed because it is too large Load Diff

23
test/.gitignore vendored
View File

@@ -1,7 +1,16 @@
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_copy
suites/api/test_cpp
suites/api/test_dump
suites/api/test_dump_callback
suites/api/test_equal
suites/api/test_load
suites/api/test_loadb
suites/api/test_memory_funcs
suites/api/test_number
suites/api/test_object
suites/api/test_pack
suites/api/test_simple
suites/api/test_unpack

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

124
test/bin/json_process.c Normal file
View File

@@ -0,0 +1,124 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.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;
}
/* Return a pointer to the first non-whitespace character of str.
Modifies str so that all trailing whitespace characters are
replaced by '\0'. */
static const char *strip(char *str)
{
size_t length;
char *result = str;
while(*result && isspace(*result))
result++;
length = strlen(result);
if(length == 0)
return result;
while(isspace(result[length - 1]))
result[--length] = '\0';
return result;
}
int main(int argc, char *argv[])
{
int indent = 0;
size_t 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_PRESERVE_ORDER"))
flags |= JSON_PRESERVE_ORDER;
if(getenv_int("JSON_SORT_KEYS"))
flags |= JSON_SORT_KEYS;
if(getenv_int("STRIP")) {
/* Load to memory, strip leading and trailing whitespace */
size_t size = 0, used = 0;
char *buffer = NULL;
while(1) {
int count;
size = (size == 0 ? 128 : size * 2);
buffer = realloc(buffer, size);
if(!buffer) {
fprintf(stderr, "Unable to allocate %d bytes\n", (int)size);
return 1;
}
count = fread(buffer + used, 1, size - used, stdin);
if(count < size - used) {
buffer[used + count] = '\0';
break;
}
used += count;
}
json = json_loads(strip(buffer), 0, &error);
free(buffer);
}
else
json = json_loadf(stdin, 0, &error);
if(!json) {
fprintf(stderr, "%d %d %d\n%s\n",
error.line, error.column, error.position,
error.text);
return 1;
}
json_dumpf(json, stdout, flags);
json_decref(json);
return 0;
}

View File

@@ -1,42 +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 simplejson
import sys
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:
json = simplejson.load(jsonfile)
except ValueError, err:
print "%s is malformed: %s" % (filename, err)
sys.exit(1)
finally:
jsonfile.close()
return json
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;
}

49
test/run-suites Executable file
View File

@@ -0,0 +1,49 @@
#!/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
[ -z "$STOP" ] && STOP=0
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))
[ $STOP -eq 1 ] && break
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

89
test/scripts/run-tests.sh Normal file
View File

@@ -0,0 +1,89 @@
# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
#
# Jansson is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
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
[ -z "$STOP" ] && STOP=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
printf '%s... ' "$test_name"
fi
run_test
case $? in
0)
# Success
if [ $VERBOSE -eq 1 ]; then
printf 'ok\n'
else
printf '.'
fi
rm -rf $test_log
;;
77)
# Skip
if [ $VERBOSE -eq 1 ]; then
printf 'skipped\n'
else
printf 'S'
fi
rm -rf $test_log
;;
*)
# Failure
if [ $VERBOSE -eq 1 ]; then
printf 'FAILED\n'
else
printf 'F'
fi
[ $STOP -eq 1 ] && break
;;
esac
done
if [ $VERBOSE -eq 0 ]; then
printf '\n'
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-2011 Petri Lehtinen <petri@digip.org>
#
# Jansson is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
[ -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)

View File

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

View File

@@ -0,0 +1,34 @@
EXTRA_DIST = run
check_PROGRAMS = \
test_array \
test_copy \
test_dump \
test_dump_callback \
test_equal \
test_load \
test_loadb \
test_memory_funcs \
test_number \
test_object \
test_pack \
test_simple \
test_unpack
test_array_SOURCES = test_array.c util.h
test_copy_SOURCES = test_copy.c util.h
test_dump_SOURCES = test_dump.c util.h
test_dump_callback_SOURCES = test_dump_callback.c util.h
test_load_SOURCES = test_load.c util.h
test_loadb_SOURCES = test_loadb.c util.h
test_memory_funcs_SOURCES = test_memory_funcs.c util.h
test_number_SOURCES = test_number.c util.h
test_object_SOURCES = test_object.c util.h
test_pack_SOURCES = test_pack.c util.h
test_simple_SOURCES = test_simple.c util.h
test_unpack_SOURCES = test_unpack.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

102
test/suites/api/check-exports Executable file
View File

@@ -0,0 +1,102 @@
#!/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_at
json_object_iter_next
json_object_iter_key
json_object_iter_value
json_object_iter_set_new
json_dumps
json_dumpf
json_dump_file
json_dump_callback
json_loads
json_loadf
json_load_file
json_loadb
json_equal
json_copy
json_deep_copy
json_pack
json_pack_ex
json_vpack_ex
json_unpack
json_unpack_ex
json_vunpack_ex
json_set_alloc_funcs
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 >/dev/null >$test_log/symbols 2>/dev/null \
|| exit 77 # Skip if "nm -D" doesn't seem to work
grep ' T ' $test_log/symbols | cut -d' ' -f3 | sort >$test_log/output
if ! cmp -s $test_log/exports $test_log/output; then
diff -u $test_log/exports $test_log/output >&2
exit 1
fi

36
test/suites/api/run Executable file
View File

@@ -0,0 +1,36 @@
#!/bin/sh
#
# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
#
# Jansson is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
is_test() {
case "$test_name" in
*.c|check-exports)
return 0
;;
*)
return 1
;;
esac
}
run_test() {
if [ "$test_name" = "check-exports" ]; 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 \
|| return 1
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-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <jansson.h>
#include "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-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <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;
size_t i;
array = json_loads(json_array_text, 0, 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;
size_t i;
array = json_loads(json_array_text, 0, 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, 0, 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, 0, 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;
}

142
test/suites/api/test_dump.c Normal file
View File

@@ -0,0 +1,142 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <jansson.h>
#include <string.h>
#include "util.h"
static void encode_twice()
{
/* Encode an empty object/array, add an item, encode again */
json_t *json;
char *result;
json = json_object();
result = json_dumps(json, 0);
if(!result || strcmp(result, "{}"))
fail("json_dumps failed");
free(result);
json_object_set_new(json, "foo", json_integer(5));
result = json_dumps(json, 0);
if(!result || strcmp(result, "{\"foo\": 5}"))
fail("json_dumps failed");
free(result);
json_decref(json);
json = json_array();
result = json_dumps(json, 0);
if(!result || strcmp(result, "[]"))
fail("json_dumps failed");
free(result);
json_array_append_new(json, json_integer(5));
result = json_dumps(json, 0);
if(!result || strcmp(result, "[5]"))
fail("json_dumps failed");
free(result);
json_decref(json);
}
static void circular_references()
{
/* Construct a JSON object/array with a circular reference:
object: {"a": {"b": {"c": <circular reference to $.a>}}}
array: [[[<circular reference to the $[0] array>]]]
Encode it, remove the circular reference and encode again.
*/
json_t *json;
char *result;
json = json_object();
json_object_set_new(json, "a", json_object());
json_object_set_new(json_object_get(json, "a"), "b", json_object());
json_object_set(json_object_get(json_object_get(json, "a"), "b"), "c",
json_object_get(json, "a"));
if(json_dumps(json, 0))
fail("json_dumps encoded a circular reference!");
json_object_del(json_object_get(json_object_get(json, "a"), "b"), "c");
result = json_dumps(json, 0);
if(!result || strcmp(result, "{\"a\": {\"b\": {}}}"))
fail("json_dumps failed!");
free(result);
json_decref(json);
json = json_array();
json_array_append_new(json, json_array());
json_array_append_new(json_array_get(json, 0), json_array());
json_array_append(json_array_get(json_array_get(json, 0), 0),
json_array_get(json, 0));
if(json_dumps(json, 0))
fail("json_dumps encoded a circular reference!");
json_array_remove(json_array_get(json_array_get(json, 0), 0), 0);
result = json_dumps(json, 0);
if(!result || strcmp(result, "[[[]]]"))
fail("json_dumps failed!");
free(result);
json_decref(json);
}
static void encode_other_than_array_or_object()
{
/* Encoding anything other than array or object should only
* succeed if the JSON_ENCODE_ANY flag is used */
json_t *json;
FILE *fp = NULL;
char *result;
json = json_string("foo");
if(json_dumps(json, 0) != NULL)
fail("json_dumps encoded a string!");
if(json_dumpf(json, fp, 0) == 0)
fail("json_dumpf encoded a string!");
result = json_dumps(json, JSON_ENCODE_ANY);
if(!result || strcmp(result, "\"foo\"") != 0)
fail("json_dumps failed to encode a string with JSON_ENCODE_ANY");
free(result);
json_decref(json);
json = json_integer(42);
if(json_dumps(json, 0) != NULL)
fail("json_dumps encoded an integer!");
if(json_dumpf(json, fp, 0) == 0)
fail("json_dumpf encoded an integer!");
result = json_dumps(json, JSON_ENCODE_ANY);
if(!result || strcmp(result, "42") != 0)
fail("json_dumps failed to encode an integer with JSON_ENCODE_ANY");
free(result);
json_decref(json);
}
int main()
{
encode_twice();
circular_references();
encode_other_than_array_or_object();
return 0;
}

View File

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

View File

@@ -0,0 +1,190 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <jansson.h>
#include "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, 0, NULL);
value2 = json_loads(complex_json, 0, 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,60 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <jansson.h>
#include <string.h>
#include "util.h"
static void file_not_found()
{
json_t *json;
json_error_t error;
json = json_load_file("/path/to/nonexistent/file.json", 0, &error);
if(json)
fail("json_load_file returned non-NULL for a nonexistent file");
if(error.line != -1)
fail("json_load_file returned an invalid line number");
if(strcmp(error.text, "unable to open /path/to/nonexistent/file.json: No such file or directory") != 0)
fail("json_load_file returned an invalid error message");
}
static void reject_duplicates()
{
json_error_t error;
if(json_loads("{\"foo\": 1, \"foo\": 2}", JSON_REJECT_DUPLICATES, &error))
fail("json_loads did not detect a duplicate key");
check_error("duplicate object key near '\"foo\"'", "<string>", 1, 16, 16);
}
static void disable_eof_check()
{
json_error_t error;
json_t *json;
const char *text = "{\"foo\": 1} garbage";
if(json_loads(text, 0, &error))
fail("json_loads did not detect garbage after JSON text");
check_error("end of file expected near 'garbage'", "<string>", 1, 18, 18);
json = json_loads(text, JSON_DISABLE_EOF_CHECK, &error);
if(!json)
fail("json_loads failed with JSON_DISABLE_EOF_CHECK");
json_decref(json);
}
int main()
{
file_not_found();
reject_duplicates();
disable_eof_check();
return 0;
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <jansson.h>
#include <string.h>
#include "util.h"
int main()
{
json_t *json;
json_error_t error;
const char str[] = "[\"A\", {\"B\": \"C\"}, 1, 2, 3]garbage";
size_t len = strlen(str) - strlen("garbage");
json = json_loadb(str, len, 0, &error);
if(!json) {
fail("json_loadb failed on a valid JSON buffer");
}
json_decref(json);
json = json_loadb(str, len - 1, 0, &error);
if (json) {
json_decref(json);
fail("json_loadb should have failed on an incomplete buffer, but it didn't");
}
if(error.line != 1) {
fail("json_loadb returned an invalid line number on fail");
}
if(strcmp(error.text, "']' expected near end of file") != 0) {
fail("json_loadb returned an invalid error message for an unclosed top-level array");
}
return 0;
}

View File

@@ -0,0 +1,84 @@
#include <string.h>
#include <jansson.h>
#include "util.h"
static int malloc_called = 0;
static int free_called = 0;
/* helper */
static void create_and_free_complex_object()
{
json_t *obj;
obj = json_pack("{s:i,s:n,s:b,s:b,s:{s:s},s:[i,i,i]",
"foo", 42,
"bar",
"baz", 1,
"qux", 0,
"alice", "bar", "baz",
"bob", 9, 8, 7);
json_decref(obj);
}
static void *my_malloc(size_t size)
{
malloc_called += 1;
return malloc(size);
}
static void my_free(void *ptr)
{
free_called += 1;
free(ptr);
}
static void test_simple()
{
json_set_alloc_funcs(my_malloc, my_free);
create_and_free_complex_object();
if(malloc_called != 27 || free_called != 27)
fail("Custom allocation failed");
}
/*
Test the secure memory functions code given in the API reference
documentation, but by using plain memset instead of
guaranteed_memset().
*/
static void *secure_malloc(size_t size)
{
/* Store the memory area size in the beginning of the block */
void *ptr = malloc(size + 8);
*((size_t *)ptr) = size;
return ptr + 8;
}
static void secure_free(void *ptr)
{
size_t size;
ptr -= 8;
size = *((size_t *)ptr);
/*guaranteed_*/memset(ptr, 0, size);
free(ptr);
}
static void test_secure_funcs(void)
{
json_set_alloc_funcs(secure_malloc, secure_free);
create_and_free_complex_object();
}
int main()
{
test_simple();
test_secure_funcs();
return 0;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.

View File

@@ -0,0 +1,451 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <jansson.h>
#include <string.h>
#include "util.h"
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_iterators()
{
json_t *object, *foo, *bar, *baz;
void *iter;
if(json_object_iter(NULL))
fail("able to iterate over NULL");
if(json_object_iter_next(NULL, NULL))
fail("able to increment an iterator on a NULL object");
object = json_object();
foo = json_string("foo");
bar = json_string("bar");
baz = json_string("baz");
if(!object || !foo || !bar || !bar)
fail("unable to create values");
if(json_object_iter_next(object, NULL))
fail("able to increment a NULL iterator");
if(json_object_set(object, "a", foo) ||
json_object_set(object, "b", bar) ||
json_object_set(object, "c", baz))
fail("unable to populate object");
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) != foo)
fail("iterating failed: wrong value");
iter = json_object_iter_next(object, iter);
if(!iter)
fail("unable to increment iterator");
if(strcmp(json_object_iter_key(iter), "b"))
fail("iterating failed: wrong key");
if(json_object_iter_value(iter) != bar)
fail("iterating failed: wrong value");
iter = json_object_iter_next(object, iter);
if(!iter)
fail("unable to increment iterator");
if(strcmp(json_object_iter_key(iter), "c"))
fail("iterating failed: wrong key");
if(json_object_iter_value(iter) != baz)
fail("iterating failed: wrong value");
if(json_object_iter_next(object, iter) != NULL)
fail("able to iterate over the end");
if(json_object_iter_at(object, "foo"))
fail("json_object_iter_at() succeeds for non-existent key");
iter = json_object_iter_at(object, "b");
if(!iter)
fail("json_object_iter_at() fails for an existing key");
if(strcmp(json_object_iter_key(iter), "b"))
fail("iterating failed: wrong key");
if(json_object_iter_value(iter) != bar)
fail("iterating failed: wrong value");
iter = json_object_iter_next(object, iter);
if(!iter)
fail("unable to increment iterator");
if(strcmp(json_object_iter_key(iter), "c"))
fail("iterating failed: wrong key");
if(json_object_iter_value(iter) != baz)
fail("iterating failed: wrong value");
if(json_object_iter_set(object, iter, bar))
fail("unable to set value at iterator");
if(strcmp(json_object_iter_key(iter), "c"))
fail("json_object_iter_key() fails after json_object_iter_set()");
if(json_object_iter_value(iter) != bar)
fail("json_object_iter_value() fails after json_object_iter_set()");
if(json_object_get(object, "c") != bar)
fail("json_object_get() fails after json_object_iter_set()");
json_decref(object);
json_decref(foo);
json_decref(bar);
json_decref(baz);
}
static void test_misc()
{
json_t *object, *string, *other_string, *value;
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");
/* 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);
}
static void test_preserve_order()
{
json_t *object;
char *result;
const char *expected = "{\"foobar\": 1, \"bazquux\": 6, \"lorem ipsum\": 3, \"sit amet\": 5, \"helicopter\": 7}";
object = json_object();
json_object_set_new(object, "foobar", json_integer(1));
json_object_set_new(object, "bazquux", json_integer(2));
json_object_set_new(object, "lorem ipsum", json_integer(3));
json_object_set_new(object, "dolor", json_integer(4));
json_object_set_new(object, "sit amet", json_integer(5));
/* changing a value should preserve the order */
json_object_set_new(object, "bazquux", json_integer(6));
/* deletion shouldn't change the order of others */
json_object_del(object, "dolor");
/* add a new item just to make sure */
json_object_set_new(object, "helicopter", json_integer(7));
result = json_dumps(object, JSON_PRESERVE_ORDER);
if(strcmp(expected, result) != 0) {
fprintf(stderr, "%s != %s", expected, result);
fail("JSON_PRESERVE_ORDER doesn't work");
}
free(result);
json_decref(object);
}
int main()
{
test_misc();
test_clear();
test_update();
test_circular();
test_set_nocheck();
test_iterators();
test_preserve_order();
return 0;
}

232
test/suites/api/test_pack.c Normal file
View File

@@ -0,0 +1,232 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2010-2011 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
*
* 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 <stdio.h>
#include "util.h"
int main()
{
json_t *value;
int i;
json_error_t error;
/*
* Simple, valid json_pack cases
*/
/* true */
value = json_pack("b", 1);
if(!json_is_true(value))
fail("json_pack boolean failed");
if(value->refcount != (ssize_t)-1)
fail("json_pack boolean refcount failed");
json_decref(value);
/* false */
value = json_pack("b", 0);
if(!json_is_false(value))
fail("json_pack boolean failed");
if(value->refcount != (ssize_t)-1)
fail("json_pack boolean refcount failed");
json_decref(value);
/* null */
value = json_pack("n");
if(!json_is_null(value))
fail("json_pack null failed");
if(value->refcount != (ssize_t)-1)
fail("json_pack null refcount failed");
json_decref(value);
/* integer */
value = json_pack("i", 1);
if(!json_is_integer(value) || json_integer_value(value) != 1)
fail("json_pack integer failed");
if(value->refcount != (ssize_t)1)
fail("json_pack integer refcount failed");
json_decref(value);
/* integer from json_int_t */
value = json_pack("I", (json_int_t)555555);
if(!json_is_integer(value) || json_integer_value(value) != 555555)
fail("json_pack json_int_t failed");
if(value->refcount != (ssize_t)1)
fail("json_pack integer refcount failed");
json_decref(value);
/* real */
value = json_pack("f", 1.0);
if(!json_is_real(value) || json_real_value(value) != 1.0)
fail("json_pack real failed");
if(value->refcount != (ssize_t)1)
fail("json_pack real refcount failed");
json_decref(value);
/* string */
value = json_pack("s", "test");
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack string failed");
if(value->refcount != (ssize_t)1)
fail("json_pack string refcount failed");
json_decref(value);
/* empty object */
value = json_pack("{}", 1.0);
if(!json_is_object(value) || json_object_size(value) != 0)
fail("json_pack empty object failed");
if(value->refcount != (ssize_t)1)
fail("json_pack empty object refcount failed");
json_decref(value);
/* empty list */
value = json_pack("[]", 1.0);
if(!json_is_array(value) || json_array_size(value) != 0)
fail("json_pack empty list failed");
if(value->refcount != (ssize_t)1)
fail("json_pack empty list failed");
json_decref(value);
/* non-incref'd object */
value = json_pack("o", json_integer(1));
if(!json_is_integer(value) || json_integer_value(value) != 1)
fail("json_pack object failed");
if(value->refcount != (ssize_t)1)
fail("json_pack integer refcount failed");
json_decref(value);
/* incref'd object */
value = json_pack("O", json_integer(1));
if(!json_is_integer(value) || json_integer_value(value) != 1)
fail("json_pack object failed");
if(value->refcount != (ssize_t)2)
fail("json_pack integer refcount failed");
json_decref(value);
json_decref(value);
/* simple object */
value = json_pack("{s:[]}", "foo");
if(!json_is_object(value) || json_object_size(value) != 1)
fail("json_pack array failed");
if(!json_is_array(json_object_get(value, "foo")))
fail("json_pack array failed");
if(json_object_get(value, "foo")->refcount != (ssize_t)1)
fail("json_pack object refcount failed");
json_decref(value);
/* simple array */
value = json_pack("[i,i,i]", 0, 1, 2);
if(!json_is_array(value) || json_array_size(value) != 3)
fail("json_pack object failed");
for(i=0; i<3; i++)
{
if(!json_is_integer(json_array_get(value, i)) ||
json_integer_value(json_array_get(value, i)) != i)
fail("json_pack integer array failed");
}
json_decref(value);
/* Whitespace; regular string */
value = json_pack(" s ", "test");
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack string (with whitespace) failed");
json_decref(value);
/* Whitespace; empty array */
value = json_pack("[ ]");
if(!json_is_array(value) || json_array_size(value) != 0)
fail("json_pack empty array (with whitespace) failed");
json_decref(value);
/* Whitespace; array */
value = json_pack("[ i , i, i ] ", 1, 2, 3);
if(!json_is_array(value) || json_array_size(value) != 3)
fail("json_pack array (with whitespace) failed");
json_decref(value);
/*
* Invalid cases
*/
/* newline in format string */
if(json_pack_ex(&error, 0, "{\n\n1"))
fail("json_pack failed to catch invalid format '1'");
check_error("Expected format 's', got '1'", "<format>", 3, 1, 4);
/* mismatched open/close array/object */
if(json_pack_ex(&error, 0, "[}"))
fail("json_pack failed to catch mismatched '}'");
check_error("Unexpected format character '}'", "<format>", 1, 2, 2);
if(json_pack_ex(&error, 0, "{]"))
fail("json_pack failed to catch mismatched ']'");
check_error("Expected format 's', got ']'", "<format>", 1, 2, 2);
/* missing close array */
if(json_pack_ex(&error, 0, "["))
fail("json_pack failed to catch missing ']'");
check_error("Unexpected end of format string", "<format>", 1, 2, 2);
/* missing close object */
if(json_pack_ex(&error, 0, "{"))
fail("json_pack failed to catch missing '}'");
check_error("Unexpected end of format string", "<format>", 1, 2, 2);
/* garbage after format string */
if(json_pack_ex(&error, 0, "[i]a", 42))
fail("json_pack failed to catch garbage after format string");
check_error("Garbage after format string", "<format>", 1, 4, 4);
if(json_pack_ex(&error, 0, "ia", 42))
fail("json_pack failed to catch garbage after format string");
check_error("Garbage after format string", "<format>", 1, 2, 2);
/* NULL string */
if(json_pack_ex(&error, 0, "s", NULL))
fail("json_pack failed to catch null argument string");
check_error("NULL string argument", "<args>", 1, 1, 1);
/* NULL format */
if(json_pack_ex(&error, 0, NULL))
fail("json_pack failed to catch NULL format string");
check_error("NULL or empty format string", "<format>", -1, -1, 0);
/* NULL key */
if(json_pack_ex(&error, 0, "{s:i}", NULL, 1))
fail("json_pack failed to catch NULL key");
check_error("NULL object key", "<args>", 1, 2, 2);
/* More complicated checks for row/columns */
if(json_pack_ex(&error, 0, "{ {}: s }", "foo"))
fail("json_pack failed to catch object as key");
check_error("Expected format 's', got '{'", "<format>", 1, 3, 3);
/* Complex object */
if(json_pack_ex(&error, 0, "{ s: {}, s:[ii{} }", "foo", "bar", 12, 13))
fail("json_pack failed to catch missing ]");
check_error("Unexpected format character '}'", "<format>", 1, 19, 19);
/* Complex array */
if(json_pack_ex(&error, 0, "[[[[[ [[[[[ [[[[ }]]]] ]]]] ]]]]]"))
fail("json_pack failed to catch extra }");
check_error("Unexpected format character '}'", "<format>", 1, 21, 21);
/* Invalid UTF-8 in object key */
if(json_pack_ex(&error, 0, "{s:i}", "\xff\xff", 42))
fail("json_pack failed to catch invalid UTF-8 in an object key");
check_error("Invalid UTF-8 in object key", "<args>", 1, 2, 2);
/* Invalid UTF-8 in a string */
if(json_pack_ex(&error, 0, "{s:s}", "foo", "\xff\xff"))
fail("json_pack failed to catch invalid UTF-8 in a string");
check_error("Invalid UTF-8 string", "<args>", 1, 4, 4);
return 0;
}

View File

@@ -0,0 +1,185 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <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);
/* Test reference counting on singletons (true, false, null) */
value = json_true();
if(value->refcount != (size_t)-1)
fail("refcounting true works incorrectly");
json_decref(value);
if(value->refcount != (size_t)-1)
fail("refcounting true works incorrectly");
json_incref(value);
if(value->refcount != (size_t)-1)
fail("refcounting true works incorrectly");
value = json_false();
if(value->refcount != (size_t)-1)
fail("refcounting false works incorrectly");
json_decref(value);
if(value->refcount != (size_t)-1)
fail("refcounting false works incorrectly");
json_incref(value);
if(value->refcount != (size_t)-1)
fail("refcounting false works incorrectly");
value = json_null();
if(value->refcount != (size_t)-1)
fail("refcounting null works incorrectly");
json_decref(value);
if(value->refcount != (size_t)-1)
fail("refcounting null works incorrectly");
json_incref(value);
if(value->refcount != (size_t)-1)
fail("refcounting null works incorrectly");
return 0;
}

View File

@@ -0,0 +1,341 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2010-2011 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
*
* 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 <stdio.h>
#include "util.h"
int main()
{
json_t *j, *j2;
int i1, i2, i3;
json_int_t I1;
int rv;
double f;
char *s;
json_error_t error;
/*
* Simple, valid json_pack cases
*/
/* true */
rv = json_unpack(json_true(), "b", &i1);
if(rv || !i1)
fail("json_unpack boolean failed");
/* false */
rv = json_unpack(json_false(), "b", &i1);
if(rv || i1)
fail("json_unpack boolean failed");
/* null */
if(json_unpack(json_null(), "n"))
fail("json_unpack null failed");
/* integer */
j = json_integer(42);
rv = json_unpack(j, "i", &i1);
if(rv || i1 != 42)
fail("json_unpack integer failed");
json_decref(j);
/* json_int_t */
j = json_integer(5555555);
rv = json_unpack(j, "I", &I1);
if(rv || I1 != 5555555)
fail("json_unpack json_int_t failed");
json_decref(j);
/* real */
j = json_real(1.7);
rv = json_unpack(j, "f", &f);
if(rv || f != 1.7)
fail("json_unpack real failed");
json_decref(j);
/* number */
j = json_integer(12345);
rv = json_unpack(j, "F", &f);
if(rv || f != 12345.0)
fail("json_unpack (real or) integer failed");
json_decref(j);
j = json_real(1.7);
rv = json_unpack(j, "F", &f);
if(rv || f != 1.7)
fail("json_unpack real (or integer) failed");
json_decref(j);
/* string */
j = json_string("foo");
rv = json_unpack(j, "s", &s);
if(rv || strcmp(s, "foo"))
fail("json_unpack string failed");
json_decref(j);
/* empty object */
j = json_object();
if(json_unpack(j, "{}"))
fail("json_unpack empty object failed");
json_decref(j);
/* empty list */
j = json_array();
if(json_unpack(j, "[]"))
fail("json_unpack empty list failed");
json_decref(j);
/* non-incref'd object */
j = json_object();
rv = json_unpack(j, "o", &j2);
if(j2 != j || j->refcount != 1)
fail("json_unpack object failed");
json_decref(j);
/* incref'd object */
j = json_object();
rv = json_unpack(j, "O", &j2);
if(j2 != j || j->refcount != 2)
fail("json_unpack object failed");
json_decref(j);
json_decref(j);
/* simple object */
j = json_pack("{s:i}", "foo", 42);
rv = json_unpack(j, "{s:i}", "foo", &i1);
if(rv || i1 != 42)
fail("json_unpack simple object failed");
json_decref(j);
/* simple array */
j = json_pack("[iii]", 1, 2, 3);
rv = json_unpack(j, "[i,i,i]", &i1, &i2, &i3);
if(rv || i1 != 1 || i2 != 2 || i3 != 3)
fail("json_unpack simple array failed");
json_decref(j);
/* object with many items & strict checking */
j = json_pack("{s:i, s:i, s:i}", "a", 1, "b", 2, "c", 3);
rv = json_unpack(j, "{s:i, s:i, s:i}", "a", &i1, "b", &i2, "c", &i3);
if(rv || i1 != 1 || i2 != 2 || i3 != 3)
fail("json_unpack object with many items failed");
json_decref(j);
/*
* Invalid cases
*/
j = json_integer(42);
if(!json_unpack_ex(j, &error, 0, "z"))
fail("json_unpack succeeded with invalid format character");
check_error("Unexpected format character 'z'", "<format>", 1, 1, 1);
if(!json_unpack_ex(NULL, &error, 0, "[i]"))
fail("json_unpack succeeded with NULL root");
check_error("NULL root value", "<root>", -1, -1, 0);
json_decref(j);
/* mismatched open/close array/object */
j = json_pack("[]");
if(!json_unpack_ex(j, &error, 0, "[}"))
fail("json_unpack failed to catch mismatched ']'");
check_error("Unexpected format character '}'", "<format>", 1, 2, 2);
json_decref(j);
j = json_pack("{}");
if(!json_unpack_ex(j, &error, 0, "{]"))
fail("json_unpack failed to catch mismatched '}'");
check_error("Expected format 's', got ']'", "<format>", 1, 2, 2);
json_decref(j);
/* missing close array */
j = json_pack("[]");
if(!json_unpack_ex(j, &error, 0, "["))
fail("json_unpack failed to catch missing ']'");
check_error("Unexpected end of format string", "<format>", 1, 2, 2);
json_decref(j);
/* missing close object */
j = json_pack("{}");
if(!json_unpack_ex(j, &error, 0, "{"))
fail("json_unpack failed to catch missing '}'");
check_error("Unexpected end of format string", "<format>", 1, 2, 2);
json_decref(j);
/* garbage after format string */
j = json_pack("[i]", 42);
if(!json_unpack_ex(j, &error, 0, "[i]a", &i1))
fail("json_unpack failed to catch garbage after format string");
check_error("Garbage after format string", "<format>", 1, 4, 4);
json_decref(j);
j = json_integer(12345);
if(!json_unpack_ex(j, &error, 0, "ia", &i1))
fail("json_unpack failed to catch garbage after format string");
check_error("Garbage after format string", "<format>", 1, 2, 2);
json_decref(j);
/* NULL format string */
j = json_pack("[]");
if(!json_unpack_ex(j, &error, 0, NULL))
fail("json_unpack failed to catch null format string");
check_error("NULL or empty format string", "<format>", -1, -1, 0);
json_decref(j);
/* NULL string pointer */
j = json_string("foobie");
if(!json_unpack_ex(j, &error, 0, "s", NULL))
fail("json_unpack failed to catch null string pointer");
check_error("NULL string argument", "<args>", 1, 1, 1);
json_decref(j);
/* invalid types */
j = json_integer(42);
j2 = json_string("foo");
if(!json_unpack_ex(j, &error, 0, "s"))
fail("json_unpack failed to catch invalid type");
check_error("Expected string, got integer", "<validation>", 1, 1, 1);
if(!json_unpack_ex(j, &error, 0, "n"))
fail("json_unpack failed to catch invalid type");
check_error("Expected null, got integer", "<validation>", 1, 1, 1);
if(!json_unpack_ex(j, &error, 0, "b"))
fail("json_unpack failed to catch invalid type");
check_error("Expected true or false, got integer", "<validation>", 1, 1, 1);
if(!json_unpack_ex(j2, &error, 0, "i"))
fail("json_unpack failed to catch invalid type");
check_error("Expected integer, got string", "<validation>", 1, 1, 1);
if(!json_unpack_ex(j2, &error, 0, "I"))
fail("json_unpack failed to catch invalid type");
check_error("Expected integer, got string", "<validation>", 1, 1, 1);
if(!json_unpack_ex(j, &error, 0, "f"))
fail("json_unpack failed to catch invalid type");
check_error("Expected real, got integer", "<validation>", 1, 1, 1);
if(!json_unpack_ex(j2, &error, 0, "F"))
fail("json_unpack failed to catch invalid type");
check_error("Expected real or integer, got string", "<validation>", 1, 1, 1);
if(!json_unpack_ex(j, &error, 0, "[i]"))
fail("json_unpack failed to catch invalid type");
check_error("Expected array, got integer", "<validation>", 1, 1, 1);
if(!json_unpack_ex(j, &error, 0, "{si}", "foo"))
fail("json_unpack failed to catch invalid type");
check_error("Expected object, got integer", "<validation>", 1, 1, 1);
json_decref(j);
json_decref(j2);
/* Array index out of range */
j = json_pack("[i]", 1);
if(!json_unpack_ex(j, &error, 0, "[ii]", &i1, &i2))
fail("json_unpack failed to catch index out of array bounds");
check_error("Array index 1 out of range", "<validation>", 1, 3, 3);
json_decref(j);
/* NULL object key */
j = json_pack("{si}", "foo", 42);
if(!json_unpack_ex(j, &error, 0, "{si}", NULL, &i1))
fail("json_unpack failed to catch null string pointer");
check_error("NULL object key", "<args>", 1, 2, 2);
json_decref(j);
/* Object key not found */
j = json_pack("{si}", "foo", 42);
if(!json_unpack_ex(j, &error, 0, "{si}", "baz", &i1))
fail("json_unpack failed to catch null string pointer");
check_error("Object item not found: baz", "<validation>", 1, 3, 3);
json_decref(j);
/*
* Strict validation
*/
j = json_pack("[iii]", 1, 2, 3);
rv = json_unpack(j, "[iii!]", &i1, &i2, &i3);
if(rv || i1 != 1 || i2 != 2 || i3 != 3)
fail("json_unpack array with strict validation failed");
json_decref(j);
j = json_pack("[iii]", 1, 2, 3);
if(!json_unpack_ex(j, &error, 0, "[ii!]", &i1, &i2))
fail("json_unpack array with strict validation failed");
check_error("1 array item(s) left unpacked", "<validation>", 1, 5, 5);
json_decref(j);
/* Like above, but with JSON_STRICT instead of '!' format */
j = json_pack("[iii]", 1, 2, 3);
if(!json_unpack_ex(j, &error, JSON_STRICT, "[ii]", &i1, &i2))
fail("json_unpack array with strict validation failed");
check_error("1 array item(s) left unpacked", "<validation>", 1, 4, 4);
json_decref(j);
j = json_pack("{s:s, s:i}", "foo", "bar", "baz", 42);
rv = json_unpack(j, "{sssi!}", "foo", &s, "baz", &i1);
if(rv || strcmp(s, "bar") != 0 || i1 != 42)
fail("json_unpack object with strict validation failed");
json_decref(j);
/* Unpack the same item twice */
j = json_pack("{s:s, s:i}", "foo", "bar", "baz", 42);
if(!json_unpack_ex(j, &error, 0, "{s:s,s:s!}", "foo", &s, "foo", &s))
fail("json_unpack object with strict validation failed");
check_error("1 object item(s) left unpacked", "<validation>", 1, 10, 10);
json_decref(j);
j = json_pack("[i,{s:i,s:n},[i,i]]", 1, "foo", 2, "bar", 3, 4);
if(json_unpack_ex(j, NULL, JSON_STRICT | JSON_VALIDATE_ONLY,
"[i{sisn}[ii]]", "foo", "bar"))
fail("json_unpack complex value with strict validation failed");
json_decref(j);
/* ! and * must be last */
j = json_pack("[ii]", 1, 2);
if(!json_unpack_ex(j, &error, 0, "[i!i]", &i1, &i2))
fail("json_unpack failed to catch ! in the middle of an array");
check_error("Expected ']' after '!', got 'i'", "<format>", 1, 4, 4);
if(!json_unpack_ex(j, &error, 0, "[i*i]", &i1, &i2))
fail("json_unpack failed to catch * in the middle of an array");
check_error("Expected ']' after '*', got 'i'", "<format>", 1, 4, 4);
json_decref(j);
j = json_pack("{sssi}", "foo", "bar", "baz", 42);
if(!json_unpack_ex(j, &error, 0, "{ss!si}", "foo", &s, "baz", &i1))
fail("json_unpack failed to catch ! in the middle of an object");
check_error("Expected '}' after '!', got 's'", "<format>", 1, 5, 5);
if(!json_unpack_ex(j, &error, 0, "{ss*si}", "foo", &s, "baz", &i1))
fail("json_unpack failed to catch ! in the middle of an object");
check_error("Expected '}' after '*', got 's'", "<format>", 1, 5, 5);
json_decref(j);
/* Error in nested object */
j = json_pack("{s{snsn}}", "foo", "bar", "baz");
if(!json_unpack_ex(j, &error, 0, "{s{sn!}}", "foo", "bar"))
fail("json_unpack nested object with strict validation failed");
check_error("1 object item(s) left unpacked", "<validation>", 1, 7, 7);
json_decref(j);
/* Error in nested array */
j = json_pack("[[ii]]", 1, 2);
if(!json_unpack_ex(j, &error, 0, "[[i!]]", &i1))
fail("json_unpack nested array with strict validation failed");
check_error("1 array item(s) left unpacked", "<validation>", 1, 5, 5);
json_decref(j);
return 0;
}

55
test/suites/api/util.h Normal file
View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#ifndef UTIL_H
#define UTIL_H
#include <stdio.h>
#include <stdlib.h>
#include <jansson.h>
#define failhdr fprintf(stderr, "%s:%s:%d: ", __FILE__, __FUNCTION__, __LINE__)
#define fail(msg) \
do { \
failhdr; \
fprintf(stderr, "%s\n", msg); \
exit(1); \
} while(0)
/* Assumes json_error_t error */
#define check_error(text_, source_, line_, column_, position_) \
do { \
if(strcmp(error.text, text_) != 0) { \
failhdr; \
fprintf(stderr, "text: \"%s\" != \"%s\"\n", error.text, text_); \
exit(1); \
} \
if(strcmp(error.source, source_) != 0) { \
failhdr; \
\
fprintf(stderr, "source: \"%s\" != \"%s\"\n", error.source, source_); \
exit(1); \
} \
if(error.line != line_) { \
failhdr; \
fprintf(stderr, "line: %d != %d\n", error.line, line_); \
exit(1); \
} \
if(error.column != column_) { \
failhdr; \
fprintf(stderr, "column: %d != %d\n", error.column, column_); \
exit(1); \
} \
if(error.position != position_) { \
failhdr; \
fprintf(stderr, "position: %d != %d\n", error.position, position_); \
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}

View File

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

View File

@@ -0,0 +1 @@
{"foo": 1, "bar": 2, "asdf": 3, "deadbeef": 4, "badc0ffee": 5, "qwerty": 6}

View File

@@ -0,0 +1 @@
{"foo": 1, "bar": 2, "asdf": 3, "deadbeef": 4, "badc0ffee": 5, "qwerty": 6}

32
test/suites/encoding-flags/run Executable file
View File

@@ -0,0 +1,32 @@
#!/bin/sh
#
# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
#
# Jansson is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
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

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