101 Commits
v1.3 ... v2.1

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

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

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

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

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

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

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

2
.gitignore vendored
View File

@@ -24,4 +24,4 @@ missing
stamp-h1
*.pyc
*.pc
/src/jansson.h
/src/jansson_config.h

132
CHANGES
View File

@@ -1,8 +1,140 @@
Version 2.1 (in development)
============================
* New features:
- `json_loadb()`: Decode a string with a given size, useful if the
string is not null terminated.
- Add ``JSON_ENCODE_ANY`` encoding flag to allow encoding any JSON
value. By default, only arrays and objects can be encoded. (#19)
- Add ``JSON_REJECT_DUPLICATES`` decoding flag to issue a decoding
error if any JSON object in the input contins duplicate keys. (#3)
- Add ``JSON_DISABLE_EOF_CHECK`` decoding flag to stop decoding after a
valid JSON input. This allows other data after the JSON data.
* Bug fixes:
- Fix an additional memory leak when memory allocation fails in
`json_object_set()` and friends.
- Clear errno before calling `strtod()` for better portability. (#27)
* Building:
- Avoid set-but-not-used warning/error in a test. (#20)
* Other:
- Minor clarifications to documentation.
Version 2.0.1
=============
Released 2011-03-31
* Bug fixes:
- Replace a few `malloc()` and `free()` calls with their
counterparts that support custom memory management.
- Fix object key hashing in json_unpack() strict checking mode.
- Fix the parentheses in ``JANSSON_VERSION_HEX`` macro.
- Fix `json_object_size()` return value.
- Fix a few compilation issues.
* Portability:
- Enhance portability of `va_copy()`.
- Test framework portability enhancements.
* Documentation:
- Distribute ``doc/upgrading.rst`` with the source tarball.
- Build documentation in strict mode in ``make distcheck``.
Version 2.0
===========
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

View File

@@ -1,4 +1,4 @@
Copyright (c) 2009, 2010 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,7 +1,10 @@
EXTRA_DIST = CHANGES LICENSE README.rst
SUBDIRS = doc src test
check-local: html
# "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

@@ -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,28 +38,22 @@ autoreconf::
$ autoreconf -i
To run the test suite, invoke::
$ make check
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::
make html
$ make html
and point your browser to ``doc/_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
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
.. _Sphinx: http://sphinx.pocoo.org/

View File

@@ -1,5 +1,5 @@
AC_PREREQ([2.60])
AC_INIT([jansson], [1.3], [petri@digip.org])
AC_INIT([jansson], [2.1], [petri@digip.org])
AM_INIT_AUTOMAKE([1.10 foreign])
@@ -8,8 +8,8 @@ AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CC
AC_PROG_CXX
AC_PROG_LIBTOOL
AM_CONDITIONAL([GCC], [test x$GCC = xyes])
# Checks for libraries.
@@ -18,6 +18,13 @@ AC_PROG_LIBTOOL
# 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;;
@@ -33,7 +40,7 @@ AC_CONFIG_FILES([
Makefile
doc/Makefile
src/Makefile
src/jansson.h
src/jansson_config.h
test/Makefile
test/bin/Makefile
test/suites/Makefile

View File

@@ -1,9 +1,9 @@
EXTRA_DIST = conf.py apiref.rst changes.rst conformance.rst \
gettingstarted.rst github_commits.c index.rst tutorial.rst \
ext/refcounting.py
upgrading.rst ext/refcounting.py
SPHINXBUILD = sphinx-build
SPHINXOPTS = -d _build/doctrees -W
SPHINXOPTS = -d _build/doctrees $(SPHINXOPTS_EXTRA)
html-local:
$(SPHINXBUILD) -b html $(SPHINXOPTS) $(srcdir) _build/html

File diff suppressed because it is too large Load Diff

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, 2010 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.3'
version = '2.1'
# The full version, including alpha/beta/rc tags.
release = '1.3'
release = '2.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -64,15 +62,13 @@ release = '1.3'
# 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 = 'cfunc'
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 @@ default_role = 'cfunc'
# 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)
]

View File

@@ -1,3 +1,5 @@
.. _rfc-conformance:
***************
RFC Conformance
***************
@@ -28,6 +30,12 @@ error::
All other Unicode codepoints U+0001 through U+10FFFF are allowed.
Unicode normalization or any other transformation is never performed
on any strings (string values or object keys). When checking for
equivalence of strings or object keys, the comparison is performed
byte by byte between the original UTF-8 representations of the
strings.
Numbers
=======
@@ -36,7 +44,9 @@ 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 ``int`` type.
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
@@ -54,19 +64,19 @@ 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
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`` type will result in an overflow error (a JSON decoding
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 ``int`` type will result in an overflow error (a
JSON decoding error). Thus, depending on platform, JSON numbers like
1000000000000000 may result in parsing error.
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
@@ -94,9 +104,9 @@ Types
-----
No support is provided in Jansson for any C numeric types other than
``int`` and ``double``. This excludes things such as unsigned types,
``long``, ``long long``, ``long double``, etc. Obviously, shorter
types like ``short`` 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.
``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 (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
:copyright: Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
:license: MIT, see LICENSE for details.
"""

View File

@@ -10,6 +10,9 @@ 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::
@@ -37,7 +40,7 @@ please report them.
If you obtained the source from a Git repository (or any other source
control system), there's no ``./configure`` script as it's not kept in
version control. To create the script, Autotools needs to be
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``::
@@ -51,18 +54,30 @@ used as described above.
.. _libtool: http://www.gnu.org/software/libtool/
Installing Prebuilt Binary Packages
-----------------------------------
Other Systems
-------------
Binary ``.deb`` packages for Ubuntu are available in `this PPA`_ at
Launchpad_. Follow the instructions in the PPA ("Technical details
about this PPA" link) to take the PPA into use. Then install the -dev
package::
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.
sudo apt-get install libjansson-dev
1. Create ``jansson_config.h``. This file has some platform-specific
parameters that are normally filled in by the ``./configure``
script:
.. _this PPA: http://launchpad.net/~petri/+archive/ppa
.. _Launchpad: http://launchpad.net/
- 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
@@ -77,14 +92,14 @@ documentation, invoke::
make html
and point your browser to ``doc/_build/html/index.html``. Sphinx_ is
required to generate the documentation.
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
Compiling Programs that Use Jansson
===================================
Jansson involves one C header file, :file:`jansson.h`, so it's enough

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2010 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.
@@ -96,7 +96,7 @@ static char *request(const char *url)
int main(int argc, char *argv[])
{
unsigned int i;
size_t i;
char *text;
char url[URL_SIZE];
@@ -117,7 +117,7 @@ int main(int argc, char *argv[])
if(!text)
return 1;
root = json_loads(text, &error);
root = json_loads(text, 0, &error);
free(text);
if(!root)

View File

@@ -33,6 +33,7 @@ Contents
:maxdepth: 2
gettingstarted
upgrading
tutorial
conformance
apiref

View File

@@ -118,7 +118,7 @@ first newline in the commit message::
The main function follows. In the beginning, we first declare a bunch
of variables and check the command line parameters::
unsigned int i;
size_t i;
char *text;
char url[URL_SIZE];
@@ -149,10 +149,10 @@ If an error occurs, our function ``request`` prints the error and
returns *NULL*, so it's enough to just return 1 from the main
function.
Next we'll call :cfunc:`json_loads()` to decode the JSON text we got
Next we'll call :func:`json_loads()` to decode the JSON text we got
as a response::
root = json_loads(text, &error);
root = json_loads(text, 0, &error);
free(text);
if(!root)
@@ -162,8 +162,8 @@ as a response::
}
We don't need the JSON text anymore, so we can free the ``text``
variable right after decoding it. If :cfunc:`json_loads()` fails, it
returns *NULL* and sets error information to the :ctype:`json_error_t`
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.
@@ -182,8 +182,8 @@ First, we'll extract the ``commits`` array from the JSON response::
This is the array that contains objects describing latest commits in
the repository. We check that the returned value really is an array.
If the key ``commits`` doesn't exist, :cfunc:`json_object_get()`
returns *NULL*, but :cfunc:`json_is_array()` handles this case, too.
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::
@@ -200,9 +200,9 @@ Then we proceed to loop over all the commits in the array::
}
...
The function :cfunc:`json_array_size()` returns the size of a JSON
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 :cfunc:`json_array_get()`.
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
@@ -225,7 +225,7 @@ they both are JSON strings::
And finally, we'll print the first 8 characters of the commit ID and
the first line of the commit message. A C-style string is extracted
from a JSON string using :cfunc:`json_string_value()`::
from a JSON string using :func:`json_string_value()`::
message_text = json_string_value(message);
printf("%.8s %.*s\n",
@@ -235,9 +235,9 @@ from a JSON string using :cfunc:`json_string_value()`::
}
After sending the HTTP request, we decoded the JSON text using
:cfunc:`json_loads()`, remember? It returns a *new reference* to the
: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 :cfunc:`json_decref()`. This way
to decrease the reference count using :func:`json_decref()`. This way
Jansson can release the resources::
json_decref(root);

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

View File

@@ -1,20 +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 = \
-export-symbols-regex '^json_' \
-version-info 3:0:3
-version-info 5:0:1
AM_CFLAGS = -Wall -Wextra -Werror
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, 2010 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.
@@ -41,10 +41,10 @@ static int dump_to_file(const char *buffer, int size, void *data)
return 0;
}
/* 256 spaces (the maximum indentation size) */
static char whitespace[] = " ";
/* 32 spaces (the maximum indentation size) */
static char whitespace[] = " ";
static int dump_indent(unsigned long flags, int depth, int space, dump_func dump, void *data)
static int dump_indent(size_t flags, int depth, int space, dump_func dump, void *data)
{
if(JSON_INDENT(flags) > 0)
{
@@ -165,7 +165,7 @@ static int object_key_compare_serials(const void *key1, const void *key2)
(*(const object_key_t **)key2)->serial;
}
static int do_dump(const json_t *json, unsigned long flags, int depth,
static int do_dump(const json_t *json, size_t flags, int depth,
dump_func dump, void *data)
{
int ascii = flags & JSON_ENSURE_ASCII ? 1 : 0;
@@ -185,7 +185,9 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
char buffer[MAX_INTEGER_STR_LENGTH];
int size;
size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, "%d", json_integer_value(json));
size = snprintf(buffer, MAX_INTEGER_STR_LENGTH,
"%" JSON_INTEGER_FORMAT,
json_integer_value(json));
if(size >= MAX_INTEGER_STR_LENGTH)
return -1;
@@ -307,12 +309,11 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER)
{
const object_key_t **keys;
unsigned int size;
unsigned int i;
size_t size, i;
int (*cmp_func)(const void *, const void *);
size = json_object_size(json);
keys = malloc(size * sizeof(object_key_t *));
keys = jsonp_malloc(size * sizeof(object_key_t *));
if(!keys)
goto object_error;
@@ -345,7 +346,7 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
if(dump(separator, separator_length, data) ||
do_dump(value, flags, depth + 1, dump, data))
{
free(keys);
jsonp_free(keys);
goto object_error;
}
@@ -354,7 +355,7 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
if(dump(",", 1, data) ||
dump_indent(flags, depth + 1, 1, dump, data))
{
free(keys);
jsonp_free(keys);
goto object_error;
}
}
@@ -362,13 +363,13 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
{
if(dump_indent(flags, depth, 0, dump, data))
{
free(keys);
jsonp_free(keys);
goto object_error;
}
}
}
free(keys);
jsonp_free(keys);
}
else
{
@@ -415,13 +416,15 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
}
char *json_dumps(const json_t *json, unsigned long 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;
@@ -431,21 +434,23 @@ char *json_dumps(const json_t *json, unsigned long flags)
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, unsigned long flags)
int json_dumpf(const json_t *json, FILE *output, size_t flags)
{
if(!json_is_array(json) && !json_is_object(json))
return -1;
if(!(flags & JSON_ENCODE_ANY)) {
if(!json_is_array(json) && !json_is_object(json))
return -1;
}
return do_dump(json, flags, 0, dump_to_file, (void *)output);
}
int json_dump_file(const json_t *json, const char *path, unsigned long flags)
int json_dump_file(const json_t *json, const char *path, size_t flags)
{
int result;

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,31 +1,28 @@
/*
* Copyright (c) 2009, 2010 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 <config.h>
#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;
@@ -33,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;
}
@@ -59,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;
@@ -100,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];
@@ -129,7 +126,7 @@ static int hashtable_do_del(hashtable_t *hashtable,
if(hashtable->free_value)
hashtable->free_value(pair->value);
free(pair);
jsonp_free(pair);
hashtable->size--;
return 0;
@@ -148,7 +145,7 @@ static void hashtable_do_clear(hashtable_t *hashtable)
hashtable->free_key(pair->key);
if(hashtable->free_value)
hashtable->free_value(pair->value);
free(pair);
jsonp_free(pair);
}
}
@@ -156,14 +153,14 @@ 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;
@@ -190,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;
}
@@ -206,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;
@@ -240,14 +237,14 @@ int hashtable_init(hashtable_t *hashtable,
void hashtable_close(hashtable_t *hashtable)
{
hashtable_do_clear(hashtable);
free(hashtable->buckets);
jsonp_free(hashtable->buckets);
}
int hashtable_set(hashtable_t *hashtable, void *key, void *value)
{
pair_t *pair;
bucket_t *bucket;
unsigned int hash, index;
size_t hash, index;
/* rehash if the load ratio exceeds 1 */
if(hashtable->size >= num_buckets(hashtable))
@@ -269,7 +266,7 @@ int hashtable_set(hashtable_t *hashtable, void *key, void *value)
}
else
{
pair = malloc(sizeof(pair_t));
pair = jsonp_malloc(sizeof(pair_t));
if(!pair)
return -1;
@@ -288,7 +285,7 @@ int hashtable_set(hashtable_t *hashtable, void *key, void *value)
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);
@@ -303,13 +300,13 @@ 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)
{
unsigned int i;
size_t i;
hashtable_do_clear(hashtable);
@@ -331,7 +328,7 @@ void *hashtable_iter(hashtable_t *hashtable)
void *hashtable_iter_at(hashtable_t *hashtable, const void *key)
{
pair_t *pair;
unsigned int hash;
size_t hash;
bucket_t *bucket;
hash = hashtable->hash_key(key);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2010 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,7 +8,7 @@
#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);
@@ -20,7 +20,7 @@ struct hashtable_list {
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;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2010 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,14 +9,31 @@
#define JANSSON_H
#include <stdio.h>
#include <stdlib.h> /* for size_t */
#include <stdarg.h>
#ifndef __cplusplus
#define JSON_INLINE @json_inline@
#else
#define JSON_INLINE inline
#include <jansson_config.h>
#ifdef __cplusplus
extern "C" {
#endif
/* version */
#define JANSSON_MAJOR_VERSION 2
#define JANSSON_MINOR_VERSION 1
#define JANSSON_MICRO_VERSION 0
/* Micro version is omitted if it's 0 */
#define JANSSON_VERSION "2.1"
/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this
for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */
#define JANSSON_VERSION_HEX ((JANSSON_MAJOR_VERSION << 16) | \
(JANSSON_MINOR_VERSION << 8) | \
(JANSSON_MICRO_VERSION << 0))
/* types */
typedef enum {
@@ -32,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)
@@ -53,7 +78,7 @@ json_t *json_object(void);
json_t *json_array(void);
json_t *json_string(const char *value);
json_t *json_string_nocheck(const char *value);
json_t *json_integer(int value);
json_t *json_integer(json_int_t value);
json_t *json_real(double value);
json_t *json_true(void);
json_t *json_false(void);
@@ -62,7 +87,7 @@ json_t *json_null(void);
static JSON_INLINE
json_t *json_incref(json_t *json)
{
if(json && json->refcount != (unsigned int)-1)
if(json && json->refcount != (size_t)-1)
++json->refcount;
return json;
}
@@ -73,14 +98,28 @@ void json_delete(json_t *json);
static JSON_INLINE
void json_decref(json_t *json)
{
if(json && json->refcount != (unsigned int)-1 && --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 */
unsigned int json_object_size(const json_t *object);
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_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);
@@ -106,23 +145,23 @@ 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 inline
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));
}
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_new(json_t *array, unsigned int index, json_t *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, unsigned int index, json_t *value);
int json_array_remove(json_t *array, unsigned int index);
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, unsigned int index, json_t *value)
int json_array_set(json_t *array, size_t index, json_t *value)
{
return json_array_set_new(array, index, json_incref(value));
}
@@ -134,22 +173,36 @@ int json_array_append(json_t *array, json_t *value)
}
static JSON_INLINE
int json_array_insert(json_t *array, unsigned int index, json_t *value)
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);
int json_integer_value(const json_t *integer);
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, int value);
int json_integer_set(json_t *integer, json_int_t value);
int json_real_set(json_t *real, double value);
/* pack, unpack */
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);
#define JSON_VALIDATE_ONLY 0x1
#define JSON_STRICT 0x2
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);
/* equality */
int json_equal(json_t *value1, json_t *value2);
@@ -161,28 +214,37 @@ json_t *json_copy(json_t *value);
json_t *json_deep_copy(json_t *value);
/* loading, printing */
/* decoding */
#define JSON_ERROR_TEXT_LENGTH 160
#define JSON_REJECT_DUPLICATES 0x1
#define JSON_DISABLE_EOF_CHECK 0x2
typedef struct {
char text[JSON_ERROR_TEXT_LENGTH];
int line;
} json_error_t;
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);
json_t *json_loads(const char *input, json_error_t *error);
json_t *json_loadf(FILE *input, json_error_t *error);
json_t *json_load_file(const char *path, json_error_t *error);
#define JSON_INDENT(n) (n & 0xFF)
#define JSON_COMPACT 0x100
#define JSON_ENSURE_ASCII 0x200
#define JSON_SORT_KEYS 0x400
#define JSON_PRESERVE_ORDER 0x800
/* encoding */
char *json_dumps(const json_t *json, unsigned long flags);
int json_dumpf(const json_t *json, FILE *output, unsigned long flags);
int json_dump_file(const json_t *json, const char *path, unsigned long flags);
#define JSON_INDENT(n) (n & 0x1F)
#define JSON_COMPACT 0x20
#define JSON_ENSURE_ASCII 0x40
#define JSON_SORT_KEYS 0x80
#define JSON_PRESERVE_ORDER 0x100
#define JSON_ENCODE_ANY 0x200
char *json_dumps(const json_t *json, size_t flags);
int json_dumpf(const json_t *json, FILE *output, size_t flags);
int json_dump_file(const json_t *json, const char *path, 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
}

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, 2010 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,23 +8,39 @@
#ifndef JANSSON_PRIVATE_H
#define JANSSON_PRIVATE_H
#include <stddef.h>
#include "jansson.h"
#include "hashtable.h"
#define container_of(ptr_, type_, member_) \
((type_ *)((char *)ptr_ - (size_t)&((type_ *)0)->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;
unsigned long serial;
size_t serial;
int visited;
} json_object_t;
typedef struct {
json_t json;
unsigned int size;
unsigned int entries;
size_t size;
size_t entries;
json_t **table;
int visited;
} json_array_t;
@@ -41,7 +57,7 @@ typedef struct {
typedef struct {
json_t json;
int value;
json_int_t value;
} json_integer_t;
#define json_to_object(json_) container_of(json_, json_object_t, json)
@@ -50,11 +66,26 @@ typedef struct {
#define json_to_real(json_) container_of(json_, json_real_t, json)
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
size_t jsonp_hash_str(const void *ptr);
int jsonp_str_equal(const void *ptr1, const void *ptr2);
typedef struct {
unsigned long serial;
char key[];
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, 2010 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.
@@ -12,7 +12,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.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,113 +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 ***/
static void
stream_init(stream_t *stream, get_func get, eof_func eof, void *data)
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((unsigned char)c >= 0x80 && c != (char)EOF)
if(0x80 <= c && c <= 0xFF)
{
/* multi-byte UTF-8 sequence */
int i, count;
@@ -152,30 +167,47 @@ static char stream_get(stream_t *stream, json_error_t *error)
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);
@@ -187,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)
@@ -218,6 +253,7 @@ 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++;
}
}
@@ -247,7 +283,7 @@ static int32_t 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;
@@ -258,14 +294,15 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
c = lex_get_save(lex, error);
while(c != '"') {
if(c == (char)EOF) {
lex_unget_unsave(lex, c);
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;
}
else if((unsigned char)c <= 0x1F) {
else if(0 <= c && c <= 0x1F) {
/* control character */
lex_unget_unsave(lex, c);
if(c == '\n')
@@ -281,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;
}
@@ -292,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;
}
@@ -308,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;
@@ -398,10 +433,16 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
return;
out:
free(lex->value.string);
jsonp_free(lex->value.string);
}
static int 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;
@@ -425,37 +466,40 @@ static int lex_scan_number(lex_t *lex, char c, json_error_t *error)
c = lex_get_save(lex, error);
}
else {
lex_unget_unsave(lex, c);
goto out;
lex_unget_unsave(lex, c);
goto out;
}
if(c != '.' && c != 'E' && c != 'e') {
long value;
json_int_t value;
lex_unget_unsave(lex, c);
saved_text = strbuffer_value(&lex->saved_text);
value = 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);
if((value == LONG_MAX && errno == ERANGE) || value > INT_MAX) {
error_set(error, lex, "too big integer");
goto out;
}
else if((value == LONG_MIN && errno == ERANGE) || value < INT_MIN) {
error_set(error, lex, "too big negative integer");
goto out;
}
lex->token = TOKEN_INTEGER;
lex->value.integer = (int)value;
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);
@@ -481,6 +525,7 @@ static int lex_scan_number(lex_t *lex, char c, json_error_t *error)
lex_unget_unsave(lex, c);
saved_text = strbuffer_value(&lex->saved_text);
errno = 0;
value = strtod(saved_text, &end);
assert(end == saved_text + lex->saved_text.length);
@@ -499,29 +544,26 @@ out:
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);
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 == (char)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;
}
@@ -581,31 +623,29 @@ static char *lex_steal_string(lex_t *lex)
return result;
}
static int lex_init(lex_t *lex, get_func get, eof_func eof, void *data)
static int lex_init(lex_t *lex, get_func get, void *data)
{
stream_init(&lex->stream, get, eof, 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)
@@ -628,28 +668,36 @@ static json_t *parse_object(lex_t *lex, json_error_t *error)
if(!key)
return NULL;
if(flags & JSON_REJECT_DUPLICATES) {
if(json_object_get(object, key)) {
jsonp_free(key);
error_set(error, lex, "duplicate object key");
goto error;
}
}
lex_scan(lex, error);
if(lex->token != ':') {
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 != ',')
@@ -670,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)
@@ -681,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;
@@ -710,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;
@@ -743,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:
@@ -765,9 +813,9 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
return json;
}
static json_t *parse_json(lex_t *lex, json_error_t *error)
static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
{
error_init(error);
json_t *result;
lex_scan(lex, error);
if(lex->token != '[' && lex->token != '{') {
@@ -775,7 +823,20 @@ static 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
@@ -794,75 +855,95 @@ 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;
error_init(error);
jsonp_error_init(error, path);
fp = fopen(path, "r");
if(!fp)
@@ -872,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, 2010 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,8 @@
#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
@@ -19,7 +19,7 @@ int strbuffer_init(strbuffer_t *strbuff)
strbuff->size = STRBUFFER_MIN_SIZE;
strbuff->length = 0;
strbuff->value = malloc(strbuff->size);
strbuff->value = jsonp_malloc(strbuff->size);
if(!strbuff->value)
return -1;
@@ -30,7 +30,7 @@ int strbuffer_init(strbuffer_t *strbuff)
void strbuffer_close(strbuffer_t *strbuff)
{
free(strbuff->value);
jsonp_free(strbuff->value);
strbuff->size = 0;
strbuff->length = 0;
strbuff->value = NULL;
@@ -68,12 +68,21 @@ int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, int size)
{
if(strbuff->length + size >= strbuff->size)
{
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)
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, 2010 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

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2010 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

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2010 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,6 +8,7 @@
#ifndef UTF_H
#define UTF_H
#ifdef HAVE_CONFIG_H
#include <config.h>
#ifdef HAVE_INTTYPES_H
@@ -15,7 +16,17 @@
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
#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);

View File

@@ -1,13 +0,0 @@
/*
* Copyright (c) 2009, 2010 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

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2010 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.
@@ -7,8 +7,7 @@
#define _GNU_SOURCE
#include <config.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
@@ -16,10 +15,9 @@
#include "hashtable.h"
#include "jansson_private.h"
#include "utf.h"
#include "util.h"
static inline void json_init(json_t *json, json_type type)
static JSON_INLINE void json_init(json_t *json, json_type type)
{
json->type = type;
json->refcount = 1;
@@ -28,20 +26,15 @@ static inline void json_init(json_t *json, json_type type)
/*** object ***/
/* This macro just returns a pointer that's a few bytes backwards from
string. This makes it possible to pass a pointer to object_key_t
when only the string inside it is used, without actually creating
an object_key_t instance. */
#define string_to_key(string) container_of(string, object_key_t, key)
static unsigned int hash_key(const void *ptr)
/* From http://www.cse.yorku.ca/~oz/hash.html */
size_t jsonp_hash_str(const void *ptr)
{
const char *str = ((const object_key_t *)ptr)->key;
const char *str = (const char *)ptr;
unsigned int hash = 5381;
unsigned int c;
size_t hash = 5381;
size_t c;
while((c = (unsigned int)*str))
while((c = (size_t)*str))
{
hash = ((hash << 5) + hash) + c;
str++;
@@ -50,10 +43,26 @@ static unsigned int hash_key(const void *ptr)
return hash;
}
int jsonp_str_equal(const void *ptr1, const void *ptr2)
{
return strcmp((const char *)ptr1, (const char *)ptr2) == 0;
}
/* This macro just returns a pointer that's a few bytes backwards from
string. This makes it possible to pass a pointer to object_key_t
when only the string inside it is used, without actually creating
an object_key_t instance. */
#define string_to_key(string) container_of(string, object_key_t, key)
static size_t hash_key(const void *ptr)
{
return jsonp_hash_str(((const object_key_t *)ptr)->key);
}
static int key_equal(const void *ptr1, const void *ptr2)
{
return strcmp(((const object_key_t *)ptr1)->key,
((const object_key_t *)ptr2)->key) == 0;
return jsonp_str_equal(((const object_key_t *)ptr1)->key,
((const object_key_t *)ptr2)->key);
}
static void value_decref(void *value)
@@ -63,15 +72,16 @@ static void value_decref(void *value)
json_t *json_object(void)
{
json_object_t *object = malloc(sizeof(json_object_t));
json_object_t *object = jsonp_malloc(sizeof(json_object_t));
if(!object)
return NULL;
json_init(&object->json, JSON_OBJECT);
if(hashtable_init(&object->hashtable, hash_key, key_equal,
free, value_decref))
if(hashtable_init(&object->hashtable,
hash_key, key_equal,
jsonp_free, value_decref))
{
free(object);
jsonp_free(object);
return NULL;
}
@@ -84,15 +94,15 @@ json_t *json_object(void)
static void json_delete_object(json_object_t *object)
{
hashtable_close(&object->hashtable);
free(object);
jsonp_free(object);
}
unsigned int json_object_size(const json_t *json)
size_t json_object_size(const json_t *json)
{
json_object_t *object;
if(!json_is_object(json))
return -1;
return 0;
object = json_to_object(json);
return object->hashtable.size;
@@ -124,9 +134,15 @@ int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
}
object = json_to_object(json);
k = malloc(sizeof(object_key_t) + strlen(key) + 1);
/* offsetof(...) returns the size of object_key_t without the
last, flexible member. This way, the correct amount is
allocated. */
k = jsonp_malloc(offsetof(object_key_t, key) + strlen(key) + 1);
if(!k)
{
json_decref(value);
return -1;
}
k->serial = object->serial++;
strcpy(k->key, key);
@@ -350,7 +366,7 @@ static json_t *json_object_deep_copy(json_t *object)
json_t *json_array(void)
{
json_array_t *array = malloc(sizeof(json_array_t));
json_array_t *array = jsonp_malloc(sizeof(json_array_t));
if(!array)
return NULL;
json_init(&array->json, JSON_ARRAY);
@@ -358,9 +374,9 @@ json_t *json_array(void)
array->entries = 0;
array->size = 8;
array->table = malloc(array->size * sizeof(json_t *));
array->table = jsonp_malloc(array->size * sizeof(json_t *));
if(!array->table) {
free(array);
jsonp_free(array);
return NULL;
}
@@ -371,16 +387,16 @@ json_t *json_array(void)
static void json_delete_array(json_array_t *array)
{
unsigned int i;
size_t i;
for(i = 0; i < array->entries; i++)
json_decref(array->table[i]);
free(array->table);
free(array);
jsonp_free(array->table);
jsonp_free(array);
}
unsigned int json_array_size(const json_t *json)
size_t json_array_size(const json_t *json)
{
if(!json_is_array(json))
return 0;
@@ -388,7 +404,7 @@ unsigned int json_array_size(const json_t *json)
return json_to_array(json)->entries;
}
json_t *json_array_get(const json_t *json, unsigned int index)
json_t *json_array_get(const json_t *json, size_t index)
{
json_array_t *array;
if(!json_is_array(json))
@@ -401,7 +417,7 @@ json_t *json_array_get(const json_t *json, unsigned int index)
return array->table[index];
}
int json_array_set_new(json_t *json, unsigned int index, json_t *value)
int json_array_set_new(json_t *json, size_t index, json_t *value)
{
json_array_t *array;
@@ -427,24 +443,24 @@ int json_array_set_new(json_t *json, unsigned int index, json_t *value)
return 0;
}
static void array_move(json_array_t *array, unsigned int dest,
unsigned int src, unsigned int count)
static void array_move(json_array_t *array, size_t dest,
size_t src, size_t count)
{
memmove(&array->table[dest], &array->table[src], count * sizeof(json_t *));
}
static void array_copy(json_t **dest, unsigned int dpos,
json_t **src, unsigned int spos,
unsigned int count)
static void array_copy(json_t **dest, size_t dpos,
json_t **src, size_t spos,
size_t count)
{
memcpy(&dest[dpos], &src[spos], count * sizeof(json_t *));
}
static json_t **json_array_grow(json_array_t *array,
unsigned int amount,
size_t amount,
int copy)
{
unsigned int new_size;
size_t new_size;
json_t **old_table, **new_table;
if(array->entries + amount <= array->size)
@@ -453,7 +469,7 @@ static json_t **json_array_grow(json_array_t *array,
old_table = array->table;
new_size = max(array->size + amount, array->size * 2);
new_table = malloc(new_size * sizeof(json_t *));
new_table = jsonp_malloc(new_size * sizeof(json_t *));
if(!new_table)
return NULL;
@@ -462,7 +478,7 @@ static json_t **json_array_grow(json_array_t *array,
if(copy) {
array_copy(array->table, 0, old_table, 0, array->entries);
free(old_table);
jsonp_free(old_table);
return array->table;
}
@@ -494,7 +510,7 @@ int json_array_append_new(json_t *json, json_t *value)
return 0;
}
int json_array_insert_new(json_t *json, unsigned int index, json_t *value)
int json_array_insert_new(json_t *json, size_t index, json_t *value)
{
json_array_t *array;
json_t **old_table;
@@ -523,7 +539,7 @@ int json_array_insert_new(json_t *json, unsigned int index, json_t *value)
array_copy(array->table, 0, old_table, 0, index);
array_copy(array->table, index + 1, old_table, index,
array->entries - index);
free(old_table);
jsonp_free(old_table);
}
else
array_move(array, index + 1, index, array->entries - index);
@@ -534,7 +550,7 @@ int json_array_insert_new(json_t *json, unsigned int index, json_t *value)
return 0;
}
int json_array_remove(json_t *json, unsigned int index)
int json_array_remove(json_t *json, size_t index)
{
json_array_t *array;
@@ -556,7 +572,7 @@ int json_array_remove(json_t *json, unsigned int index)
int json_array_clear(json_t *json)
{
json_array_t *array;
unsigned int i;
size_t i;
if(!json_is_array(json))
return -1;
@@ -572,7 +588,7 @@ int json_array_clear(json_t *json)
int json_array_extend(json_t *json, json_t *other_json)
{
json_array_t *array, *other;
unsigned int i;
size_t i;
if(!json_is_array(json) || !json_is_array(other_json))
return -1;
@@ -593,7 +609,7 @@ int json_array_extend(json_t *json, json_t *other_json)
static int json_array_equal(json_t *array1, json_t *array2)
{
unsigned int i, size;
size_t i, size;
size = json_array_size(array1);
if(size != json_array_size(array2))
@@ -616,7 +632,7 @@ static int json_array_equal(json_t *array1, json_t *array2)
static json_t *json_array_copy(json_t *array)
{
json_t *result;
unsigned int i;
size_t i;
result = json_array();
if(!result)
@@ -631,7 +647,7 @@ static json_t *json_array_copy(json_t *array)
static json_t *json_array_deep_copy(json_t *array)
{
json_t *result;
unsigned int i;
size_t i;
result = json_array();
if(!result)
@@ -652,14 +668,14 @@ json_t *json_string_nocheck(const char *value)
if(!value)
return NULL;
string = malloc(sizeof(json_string_t));
string = jsonp_malloc(sizeof(json_string_t));
if(!string)
return NULL;
json_init(&string->json, JSON_STRING);
string->value = strdup(value);
string->value = jsonp_strdup(value);
if(!string->value) {
free(string);
jsonp_free(string);
return NULL;
}
@@ -687,12 +703,12 @@ int json_string_set_nocheck(json_t *json, const char *value)
char *dup;
json_string_t *string;
dup = strdup(value);
dup = jsonp_strdup(value);
if(!dup)
return -1;
string = json_to_string(json);
free(string->value);
jsonp_free(string->value);
string->value = dup;
return 0;
@@ -708,8 +724,8 @@ int json_string_set(json_t *json, const char *value)
static void json_delete_string(json_string_t *string)
{
free(string->value);
free(string);
jsonp_free(string->value);
jsonp_free(string);
}
static int json_string_equal(json_t *string1, json_t *string2)
@@ -725,9 +741,9 @@ static json_t *json_string_copy(json_t *string)
/*** integer ***/
json_t *json_integer(int value)
json_t *json_integer(json_int_t value)
{
json_integer_t *integer = malloc(sizeof(json_integer_t));
json_integer_t *integer = jsonp_malloc(sizeof(json_integer_t));
if(!integer)
return NULL;
json_init(&integer->json, JSON_INTEGER);
@@ -736,7 +752,7 @@ json_t *json_integer(int value)
return &integer->json;
}
int json_integer_value(const json_t *json)
json_int_t json_integer_value(const json_t *json)
{
if(!json_is_integer(json))
return 0;
@@ -744,7 +760,7 @@ int json_integer_value(const json_t *json)
return json_to_integer(json)->value;
}
int json_integer_set(json_t *json, int value)
int json_integer_set(json_t *json, json_int_t value)
{
if(!json_is_integer(json))
return -1;
@@ -756,7 +772,7 @@ int json_integer_set(json_t *json, int value)
static void json_delete_integer(json_integer_t *integer)
{
free(integer);
jsonp_free(integer);
}
static int json_integer_equal(json_t *integer1, json_t *integer2)
@@ -774,7 +790,7 @@ static json_t *json_integer_copy(json_t *integer)
json_t *json_real(double value)
{
json_real_t *real = malloc(sizeof(json_real_t));
json_real_t *real = jsonp_malloc(sizeof(json_real_t));
if(!real)
return NULL;
json_init(&real->json, JSON_REAL);
@@ -803,7 +819,7 @@ int json_real_set(json_t *json, double value)
static void json_delete_real(json_real_t *real)
{
free(real);
jsonp_free(real);
}
static int json_real_equal(json_t *real1, json_t *real2)
@@ -834,30 +850,21 @@ double json_number_value(const json_t *json)
json_t *json_true(void)
{
static json_t the_true = {
.type = JSON_TRUE,
.refcount = (unsigned int)-1
};
static json_t the_true = {JSON_TRUE, (size_t)-1};
return &the_true;
}
json_t *json_false(void)
{
static json_t the_false = {
.type = JSON_FALSE,
.refcount = (unsigned int)-1
};
static json_t the_false = {JSON_FALSE, (size_t)-1};
return &the_false;
}
json_t *json_null(void)
{
static json_t the_null = {
.type = JSON_NULL,
.refcount = (unsigned int)-1
};
static json_t the_null = {JSON_NULL, (size_t)-1};
return &the_null;
}

8
test/.gitignore vendored
View File

@@ -1,11 +1,15 @@
logs
bin/json_process
suites/api/test_array
suites/api/test_equal
suites/api/test_copy
suites/api/test_cpp
suites/api/test_dump
suites/api/test_equal
suites/api/test_load
suites/api/test_loadb
suites/api/test_memory_funcs
suites/api/test_number
suites/api/test_object
suites/api/test_pack
suites/api/test_simple
suites/api/test_cpp
suites/api/test_unpack

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2010 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.
@@ -7,6 +7,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <jansson.h>
static int getenv_int(const char *name)
@@ -25,10 +27,30 @@ static int getenv_int(const char *name)
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;
unsigned int flags = 0;
size_t flags = 0;
json_t *json;
json_error_t error;
@@ -59,9 +81,39 @@ int main(int argc, char *argv[])
if(getenv_int("JSON_SORT_KEYS"))
flags |= JSON_SORT_KEYS;
json = json_loadf(stdin, &error);
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\n%s\n", error.line, error.text);
fprintf(stderr, "%d %d %d\n%s\n",
error.line, error.column, error.position,
error.text);
return 1;
}

View File

@@ -20,6 +20,8 @@ if [ -z "$SUITES" ]; then
done
fi
[ -z "$STOP" ] && STOP=0
export suites_srcdir=$top_srcdir/test/suites
export suites_builddir=suites
export scriptdir=$top_srcdir/test/scripts
@@ -34,6 +36,7 @@ for suite in $SUITES; do
passed=$(($passed+1))
else
failed=$(($failed+1))
[ $STOP -eq 1 ] && break
fi
done

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2009, 2010 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.
@@ -12,6 +12,7 @@ suite_log=$logdir/$suite_name
[ -z "$VERBOSE" ] && VERBOSE=0
[ -z "$STOP" ] && STOP=0
. $scriptdir/valgrind.sh
@@ -29,29 +30,46 @@ for test_path in $suite_srcdir/*; do
rm -rf $test_log
mkdir -p $test_log
if [ $VERBOSE -eq 1 ]; then
echo -n "$test_name... "
printf '%s... ' "$test_name"
fi
if run_test; then
# Success
if [ $VERBOSE -eq 1 ]; then
echo "ok"
else
echo -n "."
fi
rm -rf $test_log
else
# Failure
if [ $VERBOSE -eq 1 ]; then
echo "FAILED"
else
echo -n "F"
fi
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
echo
printf '\n'
fi
if [ -n "$(ls -A $suite_log)" ]; then

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2009, 2010 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

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

View File

@@ -2,21 +2,29 @@ EXTRA_DIST = run
check_PROGRAMS = \
test_array \
test_equal \
test_copy \
test_dump \
test_equal \
test_load \
test_simple \
test_loadb \
test_memory_funcs \
test_number \
test_object
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_load_SOURCES = test_load.c util.h
test_simple_SOURCES = test_simple.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

View File

@@ -50,9 +50,17 @@ json_dump_file
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
@@ -82,7 +90,10 @@ EOF
SOFILE="../src/.libs/libjansson.so"
nm -D $SOFILE | grep ' T ' | cut -d' ' -f3 | sort >$test_log/output
nm -D $SOFILE >/dev/null >$test_log/symbols 2>/dev/null \
|| exit 77 # Skip if "nm -D" doesn't seem to work
grep ' T ' $test_log/symbols | cut -d' ' -f3 | sort >$test_log/output
if ! cmp -s $test_log/exports $test_log/output; then
diff -u $test_log/exports $test_log/output >&2

View File

@@ -1,6 +1,6 @@
#!/bin/sh
#
# Copyright (c) 2009, 2010 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

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2010 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

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2010 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.
@@ -174,9 +174,9 @@ static void test_copy_array(void)
const char *json_array_text = "[1, \"foo\", 3.141592, {\"foo\": \"bar\"}]";
json_t *array, *copy;
unsigned int i;
size_t i;
array = json_loads(json_array_text, NULL);
array = json_loads(json_array_text, 0, NULL);
if(!array)
fail("unable to parse an array");
@@ -203,9 +203,9 @@ static void test_deep_copy_array(void)
const char *json_array_text = "[1, \"foo\", 3.141592, {\"foo\": \"bar\"}]";
json_t *array, *copy;
unsigned int i;
size_t i;
array = json_loads(json_array_text, NULL);
array = json_loads(json_array_text, 0, NULL);
if(!array)
fail("unable to parse an array");
@@ -235,7 +235,7 @@ static void test_copy_object(void)
json_t *object, *copy;
void *iter;
object = json_loads(json_object_text, NULL);
object = json_loads(json_object_text, 0, NULL);
if(!object)
fail("unable to parse an object");
@@ -275,7 +275,7 @@ static void test_deep_copy_object(void)
json_t *object, *copy;
void *iter;
object = json_loads(json_object_text, NULL);
object = json_loads(json_object_text, 0, NULL);
if(!object)
fail("unable to parse an object");

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2010 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,13 +9,13 @@
#include <string.h>
#include "util.h"
int main()
static void encode_twice()
{
/* Encode an empty object/array, add an item, encode again */
json_t *json;
char *result;
/* Encode an empty object/array, add an item, encode again */
json = json_object();
result = json_dumps(json, 0);
if(!result || strcmp(result, "{}"))
@@ -43,7 +43,10 @@ int main()
free(result);
json_decref(json);
}
static void circular_references()
{
/* Construct a JSON object/array with a circular reference:
object: {"a": {"b": {"c": <circular reference to $.a>}}}
@@ -51,6 +54,10 @@ int main()
Encode it, remove the circular reference and encode again.
*/
json_t *json;
char *result;
json = json_object();
json_object_set_new(json, "a", json_object());
json_object_set_new(json_object_get(json, "a"), "b", json_object());
@@ -86,6 +93,50 @@ int main()
free(result);
json_decref(json);
}
static void encode_other_than_array_or_object()
{
/* Encoding anything other than array or object should only
* succeed if the JSON_ENCODE_ANY flag is used */
json_t *json;
FILE *fp = NULL;
char *result;
json = json_string("foo");
if(json_dumps(json, 0) != NULL)
fail("json_dumps encoded a string!");
if(json_dumpf(json, fp, 0) == 0)
fail("json_dumpf encoded a string!");
result = json_dumps(json, JSON_ENCODE_ANY);
if(!result || strcmp(result, "\"foo\"") != 0)
fail("json_dumps failed to encode a string with JSON_ENCODE_ANY");
free(result);
json_decref(json);
json = json_integer(42);
if(json_dumps(json, 0) != NULL)
fail("json_dumps encoded an integer!");
if(json_dumpf(json, fp, 0) == 0)
fail("json_dumpf encoded an integer!");
result = json_dumps(json, JSON_ENCODE_ANY);
if(!result || strcmp(result, "42") != 0)
fail("json_dumps failed to encode an integer with JSON_ENCODE_ANY");
free(result);
json_decref(json);
}
int main()
{
encode_twice();
circular_references();
encode_other_than_array_or_object();
return 0;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2010 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.
@@ -167,8 +167,8 @@ static void test_equal_complex()
" \"array\": [\"foo\", false, null, 1.234]"
"}";
value1 = json_loads(complex_json, NULL);
value2 = json_loads(complex_json, NULL);
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))

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2010 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,16 +9,52 @@
#include <string.h>
#include "util.h"
int main()
static void file_not_found()
{
json_t *json;
json_error_t error;
json = json_load_file("/path/to/nonexistent/file.json", &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, 2010 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

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2010 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.

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

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2010 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.
@@ -152,33 +152,33 @@ int main()
/* Test reference counting on singletons (true, false, null) */
value = json_true();
if(value->refcount != (unsigned int)-1)
if(value->refcount != (size_t)-1)
fail("refcounting true works incorrectly");
json_decref(value);
if(value->refcount != (unsigned int)-1)
if(value->refcount != (size_t)-1)
fail("refcounting true works incorrectly");
json_incref(value);
if(value->refcount != (unsigned int)-1)
if(value->refcount != (size_t)-1)
fail("refcounting true works incorrectly");
value = json_false();
if(value->refcount != (unsigned int)-1)
if(value->refcount != (size_t)-1)
fail("refcounting false works incorrectly");
json_decref(value);
if(value->refcount != (unsigned int)-1)
if(value->refcount != (size_t)-1)
fail("refcounting false works incorrectly");
json_incref(value);
if(value->refcount != (unsigned int)-1)
if(value->refcount != (size_t)-1)
fail("refcounting false works incorrectly");
value = json_null();
if(value->refcount != (unsigned int)-1)
if(value->refcount != (size_t)-1)
fail("refcounting null works incorrectly");
json_decref(value);
if(value->refcount != (unsigned int)-1)
if(value->refcount != (size_t)-1)
fail("refcounting null works incorrectly");
json_incref(value);
if(value->refcount != (unsigned int)-1)
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;
}

View File

@@ -1,20 +1,55 @@
/*
* Copyright (c) 2009, 2010 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.
*/
#ifndef TESTPROGS_UTIL_H
#define TESTPROGS_UTIL_H
#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 { \
fprintf(stderr, "%s:%s:%d: %s\n", \
__FILE__, __FUNCTION__, __LINE__, msg); \
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

@@ -1,6 +1,6 @@
#!/bin/sh
#
# Copyright (c) 2009, 2010 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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,2 +0,0 @@
1
\u0000 is not allowed

View File

@@ -1 +0,0 @@
["\u0000 (null byte not allowed)"]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,2 +0,0 @@
1
invalid escape near '"\'

View File

@@ -1 +0,0 @@
["\a <-- invalid escape"]

View File

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

View File

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

View File

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

View File

@@ -1,2 +0,0 @@
1
']' expected near 'foo'

View File

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

View File

@@ -1,2 +0,0 @@
1
invalid Unicode '\uD888\u3210'

View File

@@ -1 +0,0 @@
["\uD888\u3210 (first surrogate and invalid second surrogate)"]

View File

@@ -1,2 +0,0 @@
1
invalid Unicode '\uDFAA'

View File

@@ -1 +0,0 @@
["\uDFAA (second surrogate on it's own)"]

View File

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

View File

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

View File

@@ -1,2 +0,0 @@
1
control character 0x0 near '"null byte '

View File

@@ -1,2 +0,0 @@
1
invalid token near end of file

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