Compare commits
293 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86d17a8dc2 | ||
|
|
3988ab2d27 | ||
|
|
f7f7bf5ab2 | ||
|
|
a76ba52f34 | ||
|
|
9febdf333c | ||
|
|
013b8b3f60 | ||
|
|
49fc708d4c | ||
|
|
92f6f0f22c | ||
|
|
e9e34f430e | ||
|
|
ab723c7fb5 | ||
|
|
636d5f60f9 | ||
|
|
c3492973e1 | ||
|
|
e20619e071 | ||
|
|
b44e2be032 | ||
|
|
76d6d700ad | ||
|
|
c96763215d | ||
|
|
4a76900bd7 | ||
|
|
1c0a3b2a55 | ||
|
|
056702e541 | ||
|
|
eab23f05d8 | ||
|
|
0944ac8d91 | ||
|
|
a5a43caa9a | ||
|
|
5456fc59ab | ||
|
|
279d8bf108 | ||
|
|
38e35e0973 | ||
|
|
af18578928 | ||
|
|
c30e92603c | ||
|
|
6ecba84817 | ||
|
|
b90ed1accb | ||
|
|
1111960120 | ||
|
|
7f09f48e7e | ||
|
|
b397711a66 | ||
|
|
cf9b384bcb | ||
|
|
42b651ef56 | ||
|
|
387298d4a6 | ||
|
|
cd854b5bc2 | ||
|
|
dd9b4e530c | ||
|
|
b5dd566c83 | ||
|
|
ff26dc60d1 | ||
|
|
53bc9d8a39 | ||
|
|
a3468c9bd8 | ||
|
|
58f9d65535 | ||
|
|
a33c3628da | ||
|
|
50dc64a7af | ||
|
|
5df7b79397 | ||
|
|
e54ea1f7c9 | ||
|
|
4be9e9e7fe | ||
|
|
dd7dd414f0 | ||
|
|
f25698d08f | ||
|
|
ef13fb9189 | ||
|
|
7706abcbed | ||
|
|
7d49fc75d5 | ||
|
|
908c62f327 | ||
|
|
a1c185a376 | ||
|
|
ac96ac13d4 | ||
|
|
579c291882 | ||
|
|
2770dca2c0 | ||
|
|
6825c2c706 | ||
|
|
6d1f28f050 | ||
|
|
7f3018a4fb | ||
|
|
53383860e8 | ||
|
|
fa7c2ea070 | ||
|
|
46f91797ec | ||
|
|
a242381024 | ||
|
|
3a7512d2b0 | ||
|
|
269e86b725 | ||
|
|
bf32f6cd75 | ||
|
|
c7611e7a0d | ||
|
|
23bc8e468d | ||
|
|
5422a862de | ||
|
|
818baf5fdb | ||
|
|
bb5d4efb2e | ||
|
|
198d537be7 | ||
|
|
1acd1a7b56 | ||
|
|
23dd078c8d | ||
|
|
781bda1404 | ||
|
|
3d5c0f46f1 | ||
|
|
8567816542 | ||
|
|
664c88ca97 | ||
|
|
cbb3855d97 | ||
|
|
3c4cf31a01 | ||
|
|
06eb436008 | ||
|
|
e3654c2245 | ||
|
|
a112563214 | ||
|
|
976fc2279f | ||
|
|
56643d4311 | ||
|
|
cb8fcc7808 | ||
|
|
b76c69de1b | ||
|
|
bfac1972e2 | ||
|
|
f8d0e01e46 | ||
|
|
ffbab6fedd | ||
|
|
145032a57f | ||
|
|
519d52e2bb | ||
|
|
94182a5acc | ||
|
|
f71eb7fe17 | ||
|
|
7ce70533c9 | ||
|
|
014c49c285 | ||
|
|
6e3ca5c45c | ||
|
|
68f2861e92 | ||
|
|
b354f8a35a | ||
|
|
b461c652b4 | ||
|
|
2caac965d4 | ||
|
|
1347686dbf | ||
|
|
8b2b12e05f | ||
|
|
1a090bbcd3 | ||
|
|
dec3ad498e | ||
|
|
978a47e2c5 | ||
|
|
453e4c0aa2 | ||
|
|
2630980f49 | ||
|
|
782acfe378 | ||
|
|
f9475f9577 | ||
|
|
8857aeadfd | ||
|
|
047a1417fb | ||
|
|
ce42e30b8c | ||
|
|
4e63fcd55d | ||
|
|
024106bbfb | ||
|
|
29ee3832cf | ||
|
|
c7c2edae8a | ||
|
|
bb89a5d4d3 | ||
|
|
f76966b438 | ||
|
|
49880cbabe | ||
|
|
f284e3c069 | ||
|
|
66a69f3f10 | ||
|
|
7d5982e6fe | ||
|
|
a2a9107600 | ||
|
|
42621370c3 | ||
|
|
8e61b7c0f0 | ||
|
|
35ddd2de20 | ||
|
|
f18ef5144a | ||
|
|
307167fb66 | ||
|
|
7e8b128740 | ||
|
|
acec2559a5 | ||
|
|
286823227c | ||
|
|
8d75235ff2 | ||
|
|
79e9dae9a0 | ||
|
|
f021ba00a2 | ||
|
|
adb1b58627 | ||
|
|
b8059a1880 | ||
|
|
49d40f020b | ||
|
|
910a2f318b | ||
|
|
08dc8d9baf | ||
|
|
c9fc055351 | ||
|
|
d1a0c3ffc2 | ||
|
|
b07e69c37a | ||
|
|
2b43e7dbda | ||
|
|
5b1a666cf1 | ||
|
|
b495b96547 | ||
|
|
72e3948438 | ||
|
|
f5662a82cd | ||
|
|
ab2e567685 | ||
|
|
d8ea2f8c4b | ||
|
|
aaae37afba | ||
|
|
04f7e27877 | ||
|
|
3dd29366b8 | ||
|
|
8c2ca3fae6 | ||
|
|
2ae279e0d4 | ||
|
|
4c6cb6afd1 | ||
|
|
78594e9bd3 | ||
|
|
e921e63b54 | ||
|
|
38950b081c | ||
|
|
56687e9b56 | ||
|
|
c9b33e3386 | ||
|
|
2ad4634de5 | ||
|
|
e080667729 | ||
|
|
ef6c35ae1b | ||
|
|
95bf762eeb | ||
|
|
f9febb64c5 | ||
|
|
dd36e4e838 | ||
|
|
df35adc438 | ||
|
|
f88a5a0e6b | ||
|
|
cc06bc334a | ||
|
|
2dc2b6bab7 | ||
|
|
49a64a6edf | ||
|
|
f0be52f9f8 | ||
|
|
1bc0225441 | ||
|
|
87df8bb0fe | ||
|
|
b76ee75aad | ||
|
|
69437a7183 | ||
|
|
63f762bc48 | ||
|
|
5a0efe6536 | ||
|
|
01759517aa | ||
|
|
17805e5829 | ||
|
|
492d95329a | ||
|
|
7ba18d3f0a | ||
|
|
7d09af38c1 | ||
|
|
8d3a9e347c | ||
|
|
f79a81dad9 | ||
|
|
b98e9d180c | ||
|
|
b077d7988e | ||
|
|
8d5d2a93d5 | ||
|
|
d77c2e3fb0 | ||
|
|
7ef3202f83 | ||
|
|
36085ab49a | ||
|
|
f743c4ee7f | ||
|
|
c994eddec4 | ||
|
|
5a20e2695b | ||
|
|
cd18aa97f0 | ||
|
|
bd09127859 | ||
|
|
6818c117ee | ||
|
|
39601c183a | ||
|
|
1e3b41e8ea | ||
|
|
7f8684828d | ||
|
|
93ac06c902 | ||
|
|
b21f07b35c | ||
|
|
508873de9b | ||
|
|
aeb5b481c9 | ||
|
|
9db34dc31a | ||
|
|
95a468cebb | ||
|
|
22173c1d8b | ||
|
|
dd2fe1ebe8 | ||
|
|
6637b976ed | ||
|
|
f5202bedef | ||
|
|
2db2f2cfb6 | ||
|
|
e7a5dc58e6 | ||
|
|
3889af476b | ||
|
|
34fb97998c | ||
|
|
ec96cbf016 | ||
|
|
19a606d736 | ||
|
|
3add1cf361 | ||
|
|
50031440a3 | ||
|
|
d67aeb9739 | ||
|
|
7c707a73a2 | ||
|
|
330e892ff6 | ||
|
|
d857fd08a5 | ||
|
|
e0a88d19d1 | ||
|
|
842bc2128b | ||
|
|
ca6d26a1c2 | ||
|
|
17d913307e | ||
|
|
f236c14dc5 | ||
|
|
bf01067a8a | ||
|
|
d3959a8ce7 | ||
|
|
f243930b68 | ||
|
|
d1b07171cc | ||
|
|
15d992cb6a | ||
|
|
59c58ea26c | ||
|
|
f95bb423a3 | ||
|
|
9448ed3ef7 | ||
|
|
7c7a1ed01e | ||
|
|
5ff8ae8052 | ||
|
|
8d53f447bf | ||
|
|
325101e525 | ||
|
|
1b02f3546c | ||
|
|
cd96049d10 | ||
|
|
a83cd30c31 | ||
|
|
a00988f663 | ||
|
|
86dc1d629b | ||
|
|
b67e130fda | ||
|
|
4cd777712b | ||
|
|
79009e62c1 | ||
|
|
76999799ed | ||
|
|
22af193a51 | ||
|
|
951d091f07 | ||
|
|
185e107d24 | ||
|
|
ca7703fbd1 | ||
|
|
12cd4e8c09 | ||
|
|
bad16ea52a | ||
|
|
30015bde90 | ||
|
|
1e00cd58a5 | ||
|
|
40bb7bf437 | ||
|
|
9d648a87cb | ||
|
|
8de850be95 | ||
|
|
19588c2d69 | ||
|
|
9c5a8430db | ||
|
|
afc9c1a23a | ||
|
|
cbacac5975 | ||
|
|
b3e1fe2ec5 | ||
|
|
7728716759 | ||
|
|
b7bf96996f | ||
|
|
6d8c287032 | ||
|
|
ab3764ed0a | ||
|
|
5406c2b3d3 | ||
|
|
743af38e7f | ||
|
|
9d16ec755c | ||
|
|
04d550b02e | ||
|
|
55d2566539 | ||
|
|
9cc6fbe580 | ||
|
|
827a01937f | ||
|
|
0f62dac627 | ||
|
|
7ee974e91c | ||
|
|
c288188a0f | ||
|
|
057ba29a27 | ||
|
|
234ee47281 | ||
|
|
98a8c1aebf | ||
|
|
42e546d065 | ||
|
|
6ffb04f0d4 | ||
|
|
fd259ff68c | ||
|
|
ab2d93b724 | ||
|
|
9611780907 | ||
|
|
93c5892bc3 | ||
|
|
1095ca956b | ||
|
|
05654bfcba | ||
|
|
89d09813c3 | ||
|
|
5ac4914642 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
*~
|
||||
*.o
|
||||
*.a
|
||||
.libs
|
||||
@@ -8,6 +9,7 @@ aclocal.m4
|
||||
autom4te.cache
|
||||
config.guess
|
||||
config.h
|
||||
config.h.in
|
||||
config.log
|
||||
config.status
|
||||
config.sub
|
||||
@@ -21,3 +23,5 @@ missing
|
||||
*.la
|
||||
stamp-h1
|
||||
*.pyc
|
||||
*.pc
|
||||
/src/jansson_config.h
|
||||
|
||||
330
CHANGES
Normal file
330
CHANGES
Normal file
@@ -0,0 +1,330 @@
|
||||
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
|
||||
keys.
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- Fix an error that occured when an array or object was first
|
||||
encoded as empty, then populated with some data, and then
|
||||
re-encoded
|
||||
|
||||
- Fix the situation like above, but when the first encoding resulted
|
||||
in an error
|
||||
|
||||
* Documentation:
|
||||
|
||||
- Clarify the documentation on reference stealing, providing an
|
||||
example usage pattern
|
||||
|
||||
|
||||
Version 1.2.1
|
||||
=============
|
||||
|
||||
Released 2010-04-03
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- Fix reference counting on ``true``, ``false`` and ``null``
|
||||
- Estimate real number underflows in decoder with 0.0 instead of
|
||||
issuing an error
|
||||
|
||||
* Portability:
|
||||
|
||||
- Make ``int32_t`` available on all systems
|
||||
- Support compilers that don't have the ``inline`` keyword
|
||||
- Require Autoconf 2.60 (for ``int32_t``)
|
||||
|
||||
* Tests:
|
||||
|
||||
- Print test names correctly when ``VERBOSE=1``
|
||||
- ``test/suites/api``: Fail when a test fails
|
||||
- Enhance tests for iterators
|
||||
- Enhance tests for decoding texts that contain null bytes
|
||||
|
||||
* Documentation:
|
||||
|
||||
- Don't remove ``changes.rst`` in ``make clean``
|
||||
- Add a chapter on RFC conformance
|
||||
|
||||
|
||||
Version 1.2
|
||||
===========
|
||||
|
||||
Released 2010-01-21
|
||||
|
||||
* New functions:
|
||||
|
||||
- `json_equal()`: Test whether two JSON values are equal
|
||||
- `json_copy()` and `json_deep_copy()`: Make shallow and deep copies
|
||||
of JSON values
|
||||
- Add a version of all functions taking a string argument that
|
||||
doesn't check for valid UTF-8: `json_string_nocheck()`,
|
||||
`json_string_set_nocheck()`, `json_object_set_nocheck()`,
|
||||
`json_object_set_new_nocheck()`
|
||||
|
||||
* New encoding flags:
|
||||
|
||||
- ``JSON_SORT_KEYS``: Sort objects by key
|
||||
- ``JSON_ENSURE_ASCII``: Escape all non-ASCII Unicode characters
|
||||
- ``JSON_COMPACT``: Use a compact representation with all unneeded
|
||||
whitespace stripped
|
||||
|
||||
* Bug fixes:
|
||||
|
||||
- Revise and unify whitespace usage in encoder: Add spaces between
|
||||
array and object items, never append newline to output.
|
||||
- Remove const qualifier from the ``json_t`` parameter in
|
||||
`json_string_set()`, `json_integer_set()` and `json_real_set`.
|
||||
- Use ``int32_t`` internally for representing Unicode code points
|
||||
(int is not enough on all platforms)
|
||||
|
||||
* Other changes:
|
||||
|
||||
- Convert ``CHANGES`` (this file) to reStructured text and add it to
|
||||
HTML documentation
|
||||
- The test system has been refactored. Python is no longer required
|
||||
to run the tests.
|
||||
- Documentation can now be built by invoking ``make html``
|
||||
- Support for pkg-config
|
||||
|
||||
|
||||
Version 1.1.3
|
||||
=============
|
||||
|
||||
Released 2009-12-18
|
||||
|
||||
* Encode reals correctly, so that first encoding and then decoding a
|
||||
real always produces the same value
|
||||
* Don't export private symbols in ``libjansson.so``
|
||||
|
||||
|
||||
Version 1.1.2
|
||||
=============
|
||||
|
||||
Released 2009-11-08
|
||||
|
||||
* Fix a bug where an error message was not produced if the input file
|
||||
could not be opened in `json_load_file()`
|
||||
* Fix an assertion failure in decoder caused by a minus sign without a
|
||||
digit after it
|
||||
* Remove an unneeded include of ``stdint.h`` in ``jansson.h``
|
||||
|
||||
|
||||
Version 1.1.1
|
||||
=============
|
||||
|
||||
Released 2009-10-26
|
||||
|
||||
* All documentation files were not distributed with v1.1; build
|
||||
documentation in make distcheck to prevent this in the future
|
||||
* Fix v1.1 release date in ``CHANGES``
|
||||
|
||||
|
||||
Version 1.1
|
||||
===========
|
||||
|
||||
Released 2009-10-20
|
||||
|
||||
* API additions and improvements:
|
||||
|
||||
- Extend array and object APIs
|
||||
- Add functions to modify integer, real and string values
|
||||
- Improve argument validation
|
||||
- Use unsigned int instead of ``uint32_t`` for encoding flags
|
||||
|
||||
* Enhance documentation
|
||||
|
||||
- Add getting started guide and tutorial
|
||||
- Fix some typos
|
||||
- General clarifications and cleanup
|
||||
|
||||
* Check for integer and real overflows and underflows in decoder
|
||||
* Make singleton values thread-safe (``true``, ``false`` and ``null``)
|
||||
* Enhance circular reference handling
|
||||
* Don't define ``-std=c99`` in ``AM_CFLAGS``
|
||||
* Add C++ guards to ``jansson.h``
|
||||
* Minor performance and portability improvements
|
||||
* Expand test coverage
|
||||
|
||||
|
||||
Version 1.0.4
|
||||
=============
|
||||
|
||||
Released 2009-10-11
|
||||
|
||||
* Relax Autoconf version requirement to 2.59
|
||||
* Make Jansson compile on platforms where plain ``char`` is unsigned
|
||||
* Fix API tests for object
|
||||
|
||||
|
||||
Version 1.0.3
|
||||
=============
|
||||
|
||||
Released 2009-09-14
|
||||
|
||||
* Check for integer and real overflows and underflows in decoder
|
||||
* Use the Python json module for tests, or simplejson if the json
|
||||
module is not found
|
||||
* Distribute changelog (this file)
|
||||
|
||||
|
||||
Version 1.0.2
|
||||
=============
|
||||
|
||||
Released 2009-09-08
|
||||
|
||||
* Handle EOF correctly in decoder
|
||||
|
||||
|
||||
Version 1.0.1
|
||||
=============
|
||||
|
||||
Released 2009-09-04
|
||||
|
||||
* Fixed broken `json_is_boolean()`
|
||||
|
||||
|
||||
Version 1.0
|
||||
===========
|
||||
|
||||
Released 2009-08-25
|
||||
|
||||
* Initial release
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
|
||||
Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
10
Makefile.am
10
Makefile.am
@@ -1,2 +1,10 @@
|
||||
EXTRA_DIST = LICENSE README.rst
|
||||
EXTRA_DIST = CHANGES LICENSE README.rst
|
||||
SUBDIRS = doc src test
|
||||
|
||||
# "make distcheck" builds the dvi target, so use it to check that the
|
||||
# documentation is built correctly.
|
||||
dvi:
|
||||
$(MAKE) SPHINXOPTS_EXTRA=-W html
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = jansson.pc
|
||||
|
||||
38
README.rst
38
README.rst
@@ -6,14 +6,14 @@ data. Its main features and design principles are:
|
||||
|
||||
- Simple and intuitive API and data model
|
||||
|
||||
- Good documentation
|
||||
- Comprehensive documentation
|
||||
|
||||
- No dependencies on other libraries
|
||||
|
||||
- Full Unicode support (UTF-8)
|
||||
|
||||
- Extensive test suite
|
||||
|
||||
- No dependencies on other libraries
|
||||
|
||||
Jansson is licensed under the `MIT license`_; see LICENSE in the
|
||||
source distribution for details.
|
||||
|
||||
@@ -24,7 +24,13 @@ Compilation and Installation
|
||||
If you obtained a source tarball, just use the standard autotools
|
||||
commands::
|
||||
|
||||
$ ./configure && make && make install
|
||||
$ ./configure
|
||||
$ make
|
||||
$ make install
|
||||
|
||||
To run the test suite, invoke::
|
||||
|
||||
$ make check
|
||||
|
||||
If the source has been checked out from a Git repository, the
|
||||
./configure script has to be generated fist. The easiest way is to use
|
||||
@@ -32,32 +38,22 @@ autoreconf::
|
||||
|
||||
$ autoreconf -i
|
||||
|
||||
To run the test suite, invoke::
|
||||
|
||||
$ make check
|
||||
|
||||
Python_ is required to run the tests.
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Documentation is in the ``doc/`` subdirectory. It's written in
|
||||
reStructuredText_ with Sphinx_ annotations, so reading it in plain may
|
||||
be inconvenient. For this reason, prebuilt HTML documentation is
|
||||
available at http://www.digip.org/jansson/doc/.
|
||||
Prebuilt HTML documentation is available at
|
||||
http://www.digip.org/jansson/doc/.
|
||||
|
||||
To generate HTML documentation yourself, invoke::
|
||||
The documentation source is in the ``doc/`` subdirectory. To generate
|
||||
HTML documentation, invoke::
|
||||
|
||||
cd doc/
|
||||
sphinx-build . .build/html
|
||||
$ make html
|
||||
|
||||
... and point your browser to ``.build/html/index.html``. Sphinx_ is
|
||||
required to generate the documentation.
|
||||
Then, point your browser to ``doc/_build/html/index.html``. Sphinx_
|
||||
1.0 or newer is required to generate the documentation.
|
||||
|
||||
|
||||
.. _Jansson: http://www.digip.org/jansson/
|
||||
.. _`MIT license`: http://www.opensource.org/licenses/mit-license.php
|
||||
.. _Python: http://www.python.org/
|
||||
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
|
||||
.. _Sphinx: http://sphinx.pocoo.org/
|
||||
|
||||
59
config.h.in
59
config.h.in
@@ -1,59 +0,0 @@
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#undef HAVE_DLFCN_H
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#undef HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#undef HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#undef HAVE_SYS_STAT_H
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#undef HAVE_SYS_TYPES_H
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#undef HAVE_UNISTD_H
|
||||
|
||||
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||
*/
|
||||
#undef LT_OBJDIR
|
||||
|
||||
/* Name of package */
|
||||
#undef PACKAGE
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
/* Version number of package */
|
||||
#undef VERSION
|
||||
28
configure.ac
28
configure.ac
@@ -1,5 +1,5 @@
|
||||
AC_PREREQ([2.63])
|
||||
AC_INIT([jansson], [1.0.1], [petri@digip.org])
|
||||
AC_PREREQ([2.60])
|
||||
AC_INIT([jansson], [2.1], [petri@digip.org])
|
||||
|
||||
AM_INIT_AUTOMAKE([1.10 foreign])
|
||||
|
||||
@@ -9,21 +9,41 @@ AC_CONFIG_HEADERS([config.h])
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
AC_PROG_LIBTOOL
|
||||
AM_CONDITIONAL([GCC], [test x$GCC = xyes])
|
||||
|
||||
# Checks for libraries.
|
||||
|
||||
# Checks for header files.
|
||||
|
||||
# Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_TYPE_INT32_T
|
||||
|
||||
AC_TYPE_LONG_LONG_INT
|
||||
case $ac_cv_type_long_long_int in
|
||||
yes) json_have_long_long=1;;
|
||||
*) json_have_long_long=0;;
|
||||
esac
|
||||
AC_SUBST([json_have_long_long])
|
||||
|
||||
AC_C_INLINE
|
||||
case $ac_cv_c_inline in
|
||||
yes) json_inline=inline;;
|
||||
no) json_inline=;;
|
||||
*) json_inline=$ac_cv_c_inline;;
|
||||
esac
|
||||
AC_SUBST([json_inline])
|
||||
|
||||
# Checks for library functions.
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
jansson.pc
|
||||
Makefile
|
||||
doc/Makefile
|
||||
src/Makefile
|
||||
src/jansson_config.h
|
||||
test/Makefile
|
||||
test/testdata/Makefile
|
||||
test/testprogs/Makefile
|
||||
test/bin/Makefile
|
||||
test/suites/Makefile
|
||||
test/suites/api/Makefile
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
||||
2
doc/.gitignore
vendored
2
doc/.gitignore
vendored
@@ -1 +1 @@
|
||||
.build/
|
||||
_build/
|
||||
|
||||
@@ -1,4 +1,20 @@
|
||||
EXTRA_DIST = conf.py apiref.rst index.rst ext/refcounting.py
|
||||
EXTRA_DIST = conf.py apiref.rst changes.rst conformance.rst \
|
||||
gettingstarted.rst github_commits.c index.rst tutorial.rst \
|
||||
upgrading.rst ext/refcounting.py
|
||||
|
||||
SPHINXBUILD = sphinx-build
|
||||
SPHINXOPTS = -d _build/doctrees $(SPHINXOPTS_EXTRA)
|
||||
|
||||
html-local:
|
||||
$(SPHINXBUILD) -b html $(SPHINXOPTS) $(srcdir) _build/html
|
||||
|
||||
install-html-local: html
|
||||
mkdir -p $(DESTDIR)$(htmldir)
|
||||
cp -r _build/html $(DESTDIR)$(htmldir)
|
||||
|
||||
uninstall-local:
|
||||
rm -rf $(DESTDIR)$(htmldir)
|
||||
|
||||
clean-local:
|
||||
rm -rf .build
|
||||
rm -rf _build
|
||||
rm -f ext/refcounting.pyc
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
To build the documentation, invoke
|
||||
|
||||
sphinx-build . .build/html
|
||||
make html
|
||||
|
||||
in this directory. Then point your browser to .build/html/index.html.
|
||||
Then point your browser to _build/html/index.html.
|
||||
|
||||
1107
doc/apiref.rst
1107
doc/apiref.rst
File diff suppressed because it is too large
Load Diff
5
doc/changes.rst
Normal file
5
doc/changes.rst
Normal file
@@ -0,0 +1,5 @@
|
||||
******************
|
||||
Changes in Jansson
|
||||
******************
|
||||
|
||||
.. include:: ../CHANGES
|
||||
104
doc/conf.py
104
doc/conf.py
@@ -1,13 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Jansson documentation build configuration file, created by
|
||||
# sphinx-quickstart on Thu Jul 30 11:35:32 2009.
|
||||
# sphinx-quickstart on Sun Sep 5 21:47:20 2010.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# The contents of this file are pickled, so don't put values in the namespace
|
||||
# that aren't pickleable (module imports are okay, they're removed automatically).
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
@@ -15,44 +12,45 @@
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.abspath('ext'))
|
||||
|
||||
# If your extensions (or modules documented by autodoc) are in another directory,
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.append(os.path.abspath('.'))
|
||||
sys.path.insert(0, os.path.abspath('ext'))
|
||||
|
||||
# General configuration
|
||||
# ---------------------
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['refcounting']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = []
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8'
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Jansson'
|
||||
copyright = u'2009, Petri Lehtinen'
|
||||
copyright = u'2009-2011, Petri Lehtinen'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '1.0'
|
||||
version = '2.1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '1.0.1'
|
||||
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.0.1'
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of documents that shouldn't be included in the build.
|
||||
#unused_docs = []
|
||||
|
||||
# List of directories, relative to source directory, that shouldn't be searched
|
||||
# for source files.
|
||||
exclude_trees = ['.build']
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
default_role = 'c:func'
|
||||
primary_domain = 'c'
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
@@ -88,14 +84,23 @@ exclude_trees = ['.build']
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
# Options for HTML output
|
||||
# -----------------------
|
||||
|
||||
# The style sheet to use for HTML and HTML Help pages. A file of that name
|
||||
# must exist either in Sphinx' static/ path, or in one of the custom paths
|
||||
# given in html_static_path.
|
||||
html_style = 'default.css'
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#html_theme = 'default'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
@@ -116,7 +121,7 @@ html_style = 'default.css'
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = []
|
||||
#html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
@@ -134,7 +139,7 @@ html_static_path = []
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_use_modindex = True
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
@@ -142,23 +147,28 @@ html_static_path = []
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, the reST sources are included in the HTML build as _sources/<name>.
|
||||
#html_copy_source = True
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = ''
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'Janssondoc'
|
||||
|
||||
|
||||
# Options for LaTeX output
|
||||
# ------------------------
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
# The paper size ('letter' or 'a4').
|
||||
#latex_paper_size = 'letter'
|
||||
@@ -167,10 +177,10 @@ htmlhelp_basename = 'Janssondoc'
|
||||
#latex_font_size = '10pt'
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, document class [howto/manual]).
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'Jansson.tex', ur'Jansson Documentation',
|
||||
ur'Petri Lehtinen', 'manual'),
|
||||
('index', 'Jansson.tex', u'Jansson Documentation',
|
||||
u'Petri Lehtinen', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
@@ -181,6 +191,12 @@ latex_documents = [
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#latex_preamble = ''
|
||||
|
||||
@@ -188,4 +204,14 @@ latex_documents = [
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_use_modindex = True
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output --------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'jansson', u'Jansson Documentation',
|
||||
[u'Petri Lehtinen'], 1)
|
||||
]
|
||||
|
||||
112
doc/conformance.rst
Normal file
112
doc/conformance.rst
Normal file
@@ -0,0 +1,112 @@
|
||||
.. _rfc-conformance:
|
||||
|
||||
***************
|
||||
RFC Conformance
|
||||
***************
|
||||
|
||||
JSON is specified in :rfc:`4627`, *"The application/json Media Type
|
||||
for JavaScript Object Notation (JSON)"*. This chapter discusses
|
||||
Jansson's conformance to this specification.
|
||||
|
||||
Character Encoding
|
||||
==================
|
||||
|
||||
Jansson only supports UTF-8 encoded JSON texts. It does not support or
|
||||
auto-detect any of the other encodings mentioned in the RFC, namely
|
||||
UTF-16LE, UTF-16BE, UTF-32LE or UTF-32BE. Pure ASCII is supported, as
|
||||
it's a subset of UTF-8.
|
||||
|
||||
Strings
|
||||
=======
|
||||
|
||||
JSON strings are mapped to C-style null-terminated character arrays,
|
||||
and UTF-8 encoding is used internally. Strings may not contain
|
||||
embedded null characters, not even escaped ones.
|
||||
|
||||
For example, trying to decode the following JSON text leads to a parse
|
||||
error::
|
||||
|
||||
["this string contains the null character: \u0000"]
|
||||
|
||||
All other Unicode codepoints U+0001 through U+10FFFF are allowed.
|
||||
|
||||
Unicode normalization or any other transformation is never performed
|
||||
on any strings (string values or object keys). When checking for
|
||||
equivalence of strings or object keys, the comparison is performed
|
||||
byte by byte between the original UTF-8 representations of the
|
||||
strings.
|
||||
|
||||
Numbers
|
||||
=======
|
||||
|
||||
Real vs. Integer
|
||||
----------------
|
||||
|
||||
JSON makes no distinction between real and integer numbers; Jansson
|
||||
does. Real numbers are mapped to the ``double`` type and integers to
|
||||
the ``json_int_t`` type, which is a typedef of ``long long`` or
|
||||
``long``, depending on whether ``long long`` is supported by your
|
||||
compiler or not.
|
||||
|
||||
A JSON number is considered to be a real number if its lexical
|
||||
representation includes one of ``e``, ``E``, or ``.``; regardless if
|
||||
its actual numeric value is a true integer (e.g., all of ``1E6``,
|
||||
``3.0``, ``400E-2``, and ``3.14E3`` are mathematical integers, but
|
||||
will be treated as real values).
|
||||
|
||||
All other JSON numbers are considered integers.
|
||||
|
||||
When encoding to JSON, real values are always represented
|
||||
with a fractional part; e.g., the ``double`` value 3.0 will be
|
||||
represented in JSON as ``3.0``, not ``3``.
|
||||
|
||||
Overflow, Underflow & Precision
|
||||
-------------------------------
|
||||
|
||||
Real numbers whose absolute values are too small to be represented in
|
||||
a C ``double`` will be silently estimated with 0.0. Thus, depending on
|
||||
platform, JSON numbers very close to zero such as 1E-999 may result in
|
||||
0.0.
|
||||
|
||||
Real numbers whose absolute values are too large to be represented in
|
||||
a C ``double`` will result in an overflow error (a JSON decoding
|
||||
error). Thus, depending on platform, JSON numbers like 1E+999 or
|
||||
-1E+999 may result in a parsing error.
|
||||
|
||||
Likewise, integer numbers whose absolute values are too large to be
|
||||
represented in the ``json_int_t`` type (see above) will result in an
|
||||
overflow error (a JSON decoding error). Thus, depending on platform,
|
||||
JSON numbers like 1000000000000000 may result in parsing error.
|
||||
|
||||
Parsing JSON real numbers may result in a loss of precision. As long
|
||||
as overflow does not occur (i.e. a total loss of precision), the
|
||||
rounded approximate value is silently used. Thus the JSON number
|
||||
1.000000000000000005 may, depending on platform, result in the
|
||||
``double`` value 1.0.
|
||||
|
||||
Signed zeros
|
||||
------------
|
||||
|
||||
JSON makes no statement about what a number means; however Javascript
|
||||
(ECMAscript) does state that +0.0 and -0.0 must be treated as being
|
||||
distinct values, i.e. -0.0 |not-equal| 0.0. Jansson relies on the
|
||||
underlying floating point library in the C environment in which it is
|
||||
compiled. Therefore it is platform-dependent whether 0.0 and -0.0 will
|
||||
be distinct values. Most platforms that use the IEEE 754
|
||||
floating-point standard will support signed zeros.
|
||||
|
||||
Note that this only applies to floating-point; neither JSON, C, or
|
||||
IEEE support the concept of signed integer zeros.
|
||||
|
||||
.. |not-equal| unicode:: U+2260
|
||||
|
||||
Types
|
||||
-----
|
||||
|
||||
No support is provided in Jansson for any C numeric types other than
|
||||
``json_int_t`` and ``double``. This excludes things such as unsigned
|
||||
types, ``long double``, etc. Obviously, shorter types like ``short``,
|
||||
``int``, ``long`` (if ``json_int_t`` is ``long long``) and ``float``
|
||||
are implicitly handled via the ordinary C type coercion rules (subject
|
||||
to overflow semantics). Also, no support or hooks are provided for any
|
||||
supplemental "bignum" type add-on packages.
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
<description of the json_object function>
|
||||
|
||||
:copyright: Copyright 2009 Petri Lehtinen <petri@digip.org>
|
||||
:copyright: Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
:license: MIT, see LICENSE for details.
|
||||
"""
|
||||
|
||||
|
||||
123
doc/gettingstarted.rst
Normal file
123
doc/gettingstarted.rst
Normal file
@@ -0,0 +1,123 @@
|
||||
***************
|
||||
Getting Started
|
||||
***************
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
Compiling and Installing Jansson
|
||||
================================
|
||||
|
||||
The Jansson source is available at
|
||||
http://www.digip.org/jansson/releases/.
|
||||
|
||||
Unix-like systems
|
||||
-----------------
|
||||
|
||||
Unpack the source tarball and change to the source directory:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
bunzip2 -c jansson-|release|.tar.bz2 | tar xf -
|
||||
cd jansson-|release|
|
||||
|
||||
The source uses GNU Autotools (autoconf_, automake_, libtool_), so
|
||||
compiling and installing is extremely simple::
|
||||
|
||||
./configure
|
||||
make
|
||||
make check
|
||||
make install
|
||||
|
||||
To change the destination directory (``/usr/local`` by default), use
|
||||
the ``--prefix=DIR`` argument to ``./configure``. See ``./configure
|
||||
--help`` for the list of all possible installation options. (There are
|
||||
no options to customize the resulting Jansson binary.)
|
||||
|
||||
The command ``make check`` runs the test suite distributed with
|
||||
Jansson. This step is not strictly necessary, but it may find possible
|
||||
problems that Jansson has on your platform. If any problems are found,
|
||||
please report them.
|
||||
|
||||
If you obtained the source from a Git repository (or any other source
|
||||
control system), there's no ``./configure`` script as it's not kept in
|
||||
version control. To create the script, the build system needs to be
|
||||
bootstrapped. There are many ways to do this, but the easiest one is
|
||||
to use ``autoreconf``::
|
||||
|
||||
autoreconf -vi
|
||||
|
||||
This command creates the ``./configure`` script, which can then be
|
||||
used as described above.
|
||||
|
||||
.. _autoconf: http://www.gnu.org/software/autoconf/
|
||||
.. _automake: http://www.gnu.org/software/automake/
|
||||
.. _libtool: http://www.gnu.org/software/libtool/
|
||||
|
||||
|
||||
Other Systems
|
||||
-------------
|
||||
|
||||
On Windows and other non Unix-like systems, you may be unable to run
|
||||
the ``./configure`` script. In this case, follow these steps. All the
|
||||
files mentioned can be found in the ``src/`` directory.
|
||||
|
||||
1. Create ``jansson_config.h``. This file has some platform-specific
|
||||
parameters that are normally filled in by the ``./configure``
|
||||
script:
|
||||
|
||||
- On Windows, rename ``jansson_config.h.win32`` to ``jansson_config.h``.
|
||||
|
||||
- On other systems, edit ``jansson_config.h.in``, replacing all
|
||||
``@variable@`` placeholders, and rename the file to
|
||||
``jansson_config.h``.
|
||||
|
||||
2. Make ``jansson.h`` and ``jansson_config.h`` available to the
|
||||
compiler, so that they can be found when compiling programs that
|
||||
use Jansson.
|
||||
|
||||
3. Compile all the ``.c`` files (in the ``src/`` directory) into a
|
||||
library file. Make the library available to the compiler, as in
|
||||
step 2.
|
||||
|
||||
|
||||
Building the Documentation
|
||||
--------------------------
|
||||
|
||||
(This subsection describes how to build the HTML documentation you are
|
||||
currently reading, so it can be safely skipped.)
|
||||
|
||||
Documentation is in the ``doc/`` subdirectory. It's written in
|
||||
reStructuredText_ with Sphinx_ annotations. To generate the HTML
|
||||
documentation, invoke::
|
||||
|
||||
make html
|
||||
|
||||
and point your browser to ``doc/_build/html/index.html``. Sphinx_ 1.0
|
||||
or newer is required to generate the documentation.
|
||||
|
||||
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
|
||||
.. _Sphinx: http://sphinx.pocoo.org/
|
||||
|
||||
|
||||
Compiling Programs that Use Jansson
|
||||
===================================
|
||||
|
||||
Jansson involves one C header file, :file:`jansson.h`, so it's enough
|
||||
to put the line
|
||||
|
||||
::
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
in the beginning of every source file that uses Jansson.
|
||||
|
||||
There's also just one library to link with, ``libjansson``. Compile and
|
||||
link the program as follows::
|
||||
|
||||
cc -o prog prog.c -ljansson
|
||||
|
||||
Starting from version 1.2, there's also support for pkg-config_::
|
||||
|
||||
cc -o prog prog.c `pkg-config --cflags --libs jansson`
|
||||
|
||||
.. _pkg-config: http://pkg-config.freedesktop.org/
|
||||
171
doc/github_commits.c
Normal file
171
doc/github_commits.c
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <jansson.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#define BUFFER_SIZE (256 * 1024) /* 256 KB */
|
||||
|
||||
#define URL_FORMAT "http://github.com/api/v2/json/commits/list/%s/%s/master"
|
||||
#define URL_SIZE 256
|
||||
|
||||
/* Return the offset of the first newline in text or the length of
|
||||
text if there's no newline */
|
||||
static int newline_offset(const char *text)
|
||||
{
|
||||
const char *newline = strchr(text, '\n');
|
||||
if(!newline)
|
||||
return strlen(text);
|
||||
else
|
||||
return (int)(newline - text);
|
||||
}
|
||||
|
||||
struct write_result
|
||||
{
|
||||
char *data;
|
||||
int pos;
|
||||
};
|
||||
|
||||
static size_t write_response(void *ptr, size_t size, size_t nmemb, void *stream)
|
||||
{
|
||||
struct write_result *result = (struct write_result *)stream;
|
||||
|
||||
if(result->pos + size * nmemb >= BUFFER_SIZE - 1)
|
||||
{
|
||||
fprintf(stderr, "error: too small buffer\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(result->data + result->pos, ptr, size * nmemb);
|
||||
result->pos += size * nmemb;
|
||||
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
static char *request(const char *url)
|
||||
{
|
||||
CURL *curl;
|
||||
CURLcode status;
|
||||
char *data;
|
||||
long code;
|
||||
|
||||
curl = curl_easy_init();
|
||||
data = malloc(BUFFER_SIZE);
|
||||
if(!curl || !data)
|
||||
return NULL;
|
||||
|
||||
struct write_result write_result = {
|
||||
.data = data,
|
||||
.pos = 0
|
||||
};
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_response);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_result);
|
||||
|
||||
status = curl_easy_perform(curl);
|
||||
if(status != 0)
|
||||
{
|
||||
fprintf(stderr, "error: unable to request data from %s:\n", url);
|
||||
fprintf(stderr, "%s\n", curl_easy_strerror(status));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
|
||||
if(code != 200)
|
||||
{
|
||||
fprintf(stderr, "error: server responded with code %ld\n", code);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
curl_global_cleanup();
|
||||
|
||||
/* zero-terminate the result */
|
||||
data[write_result.pos] = '\0';
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
size_t i;
|
||||
char *text;
|
||||
char url[URL_SIZE];
|
||||
|
||||
json_t *root;
|
||||
json_error_t error;
|
||||
json_t *commits;
|
||||
|
||||
if(argc != 3)
|
||||
{
|
||||
fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]);
|
||||
fprintf(stderr, "List commits at USER's REPOSITORY.\n\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
snprintf(url, URL_SIZE, URL_FORMAT, argv[1], argv[2]);
|
||||
|
||||
text = request(url);
|
||||
if(!text)
|
||||
return 1;
|
||||
|
||||
root = json_loads(text, 0, &error);
|
||||
free(text);
|
||||
|
||||
if(!root)
|
||||
{
|
||||
fprintf(stderr, "error: on line %d: %s\n", error.line, error.text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
commits = json_object_get(root, "commits");
|
||||
if(!json_is_array(commits))
|
||||
{
|
||||
fprintf(stderr, "error: commits is not an array\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for(i = 0; i < json_array_size(commits); i++)
|
||||
{
|
||||
json_t *commit, *id, *message;
|
||||
const char *message_text;
|
||||
|
||||
commit = json_array_get(commits, i);
|
||||
if(!json_is_object(commit))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d is not an object\n", i + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
id = json_object_get(commit, "id");
|
||||
if(!json_is_string(id))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: id is not a string\n", i + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
message = json_object_get(commit, "message");
|
||||
if(!json_is_string(message))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: message is not a string\n", i + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
message_text = json_string_value(message);
|
||||
printf("%.8s %.*s\n",
|
||||
json_string_value(id),
|
||||
newline_offset(message_text),
|
||||
message_text);
|
||||
}
|
||||
|
||||
json_decref(root);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,16 +1,43 @@
|
||||
Overview
|
||||
========
|
||||
Jansson Documentation
|
||||
=====================
|
||||
|
||||
This is the documentation for Jansson_ |release|, last updated |today|.
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Jansson_ is a C library for encoding, decoding and manipulating JSON
|
||||
data. Its main features and design principles are:
|
||||
|
||||
- Simple and intuitive API and data model
|
||||
|
||||
- Comprehensive documentation
|
||||
|
||||
- No dependencies on other libraries
|
||||
|
||||
- Full Unicode support (UTF-8)
|
||||
|
||||
- Extensive test suite
|
||||
|
||||
Jansson is licensed under the `MIT license`_; see LICENSE in the
|
||||
source distribution for details.
|
||||
|
||||
|
||||
.. _`MIT license`: http://www.opensource.org/licenses/mit-license.php
|
||||
.. _Jansson: http://www.digip.org/jansson/
|
||||
|
||||
**Contents:**
|
||||
Contents
|
||||
--------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
gettingstarted
|
||||
upgrading
|
||||
tutorial
|
||||
conformance
|
||||
apiref
|
||||
changes
|
||||
|
||||
|
||||
Indices and Tables
|
||||
|
||||
275
doc/tutorial.rst
Normal file
275
doc/tutorial.rst
Normal file
@@ -0,0 +1,275 @@
|
||||
.. _tutorial:
|
||||
|
||||
********
|
||||
Tutorial
|
||||
********
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
In this tutorial, we create a program that fetches the latest commits
|
||||
of a repository in GitHub_ over the web. One of the response formats
|
||||
supported by `GitHub API`_ is JSON, so the result can be parsed using
|
||||
Jansson.
|
||||
|
||||
To stick to the the scope of this tutorial, we will only cover the the
|
||||
parts of the program related to handling JSON data. For the best user
|
||||
experience, the full source code is available:
|
||||
:download:`github_commits.c`. To compile it (on Unix-like systems with
|
||||
gcc), use the following command::
|
||||
|
||||
gcc -o github_commits github_commits.c -ljansson -lcurl
|
||||
|
||||
libcurl_ is used to communicate over the web, so it is required to
|
||||
compile the program.
|
||||
|
||||
The command line syntax is::
|
||||
|
||||
github_commits USER REPOSITORY
|
||||
|
||||
``USER`` is a GitHub user ID and ``REPOSITORY`` is the repository
|
||||
name. Please note that the GitHub API is rate limited, so if you run
|
||||
the program too many times within a short period of time, the sever
|
||||
starts to respond with an error.
|
||||
|
||||
.. _GitHub: http://github.com/
|
||||
.. _GitHub API: http://develop.github.com/
|
||||
.. _libcurl: http://curl.haxx.se/
|
||||
|
||||
|
||||
.. _tutorial-github-commits-api:
|
||||
|
||||
The GitHub Commits API
|
||||
======================
|
||||
|
||||
The `GitHub commits API`_ is used by sending HTTP requests to URLs
|
||||
starting with ``http://github.com/api/v2/json/commits/``. Our program
|
||||
only lists the latest commits, so the rest of the URL is
|
||||
``list/USER/REPOSITORY/BRANCH``, where ``USER``, ``REPOSITORY`` and
|
||||
``BRANCH`` are the GitHub user ID, the name of the repository, and the
|
||||
name of the branch whose commits are to be listed, respectively.
|
||||
|
||||
GitHub responds with a JSON object of the following form:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
{
|
||||
"commits": [
|
||||
{
|
||||
"id": "<the commit ID>",
|
||||
"message": "<the commit message>",
|
||||
<more fields, not important to this tutorial>
|
||||
},
|
||||
{
|
||||
"id": "<the commit ID>",
|
||||
"message": "<the commit message>",
|
||||
<more fields, not important to this tutorial>
|
||||
},
|
||||
<more commits...>
|
||||
]
|
||||
}
|
||||
|
||||
In our program, the HTTP request is sent using the following
|
||||
function::
|
||||
|
||||
static char *request(const char *url);
|
||||
|
||||
It takes the URL as a parameter, preforms a HTTP GET request, and
|
||||
returns a newly allocated string that contains the response body. If
|
||||
the request fails, an error message is printed to stderr and the
|
||||
return value is *NULL*. For full details, refer to :download:`the code
|
||||
<github_commits.c>`, as the actual implementation is not important
|
||||
here.
|
||||
|
||||
.. _GitHub commits API: http://develop.github.com/p/commits.html
|
||||
|
||||
.. _tutorial-the-program:
|
||||
|
||||
The Program
|
||||
===========
|
||||
|
||||
First the includes::
|
||||
|
||||
#include <string.h>
|
||||
#include <jansson.h>
|
||||
|
||||
Like all the programs using Jansson, we need to include
|
||||
:file:`jansson.h`.
|
||||
|
||||
The following definitions are used to build the GitHub commits API
|
||||
request URL::
|
||||
|
||||
#define URL_FORMAT "http://github.com/api/v2/json/commits/list/%s/%s/master"
|
||||
#define URL_SIZE 256
|
||||
|
||||
The following function is used when formatting the result to find the
|
||||
first newline in the commit message::
|
||||
|
||||
/* Return the offset of the first newline in text or the length of
|
||||
text if there's no newline */
|
||||
static int newline_offset(const char *text)
|
||||
{
|
||||
const char *newline = strchr(text, '\n');
|
||||
if(!newline)
|
||||
return strlen(text);
|
||||
else
|
||||
return (int)(newline - text);
|
||||
}
|
||||
|
||||
The main function follows. In the beginning, we first declare a bunch
|
||||
of variables and check the command line parameters::
|
||||
|
||||
size_t i;
|
||||
char *text;
|
||||
char url[URL_SIZE];
|
||||
|
||||
json_t *root;
|
||||
json_error_t error;
|
||||
json_t *commits;
|
||||
|
||||
if(argc != 3)
|
||||
{
|
||||
fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]);
|
||||
fprintf(stderr, "List commits at USER's REPOSITORY.\n\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
Then we build the request URL using the user and repository names
|
||||
given as command line parameters::
|
||||
|
||||
snprintf(url, URL_SIZE, URL_FORMAT, argv[1], argv[2]);
|
||||
|
||||
This uses the ``URL_SIZE`` and ``URL_FORMAT`` constants defined above.
|
||||
Now we're ready to actually request the JSON data over the web::
|
||||
|
||||
text = request(url);
|
||||
if(!text)
|
||||
return 1;
|
||||
|
||||
If an error occurs, our function ``request`` prints the error and
|
||||
returns *NULL*, so it's enough to just return 1 from the main
|
||||
function.
|
||||
|
||||
Next we'll call :func:`json_loads()` to decode the JSON text we got
|
||||
as a response::
|
||||
|
||||
root = json_loads(text, 0, &error);
|
||||
free(text);
|
||||
|
||||
if(!root)
|
||||
{
|
||||
fprintf(stderr, "error: on line %d: %s\n", error.line, error.text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
We don't need the JSON text anymore, so we can free the ``text``
|
||||
variable right after decoding it. If :func:`json_loads()` fails, it
|
||||
returns *NULL* and sets error information to the :type:`json_error_t`
|
||||
structure given as the second parameter. In this case, our program
|
||||
prints the error information out and returns 1 from the main function.
|
||||
|
||||
Now we're ready to extract the data out of the decoded JSON response.
|
||||
The structure of the response JSON was explained in section
|
||||
:ref:`tutorial-github-commits-api`.
|
||||
|
||||
First, we'll extract the ``commits`` array from the JSON response::
|
||||
|
||||
commits = json_object_get(root, "commits");
|
||||
if(!json_is_array(commits))
|
||||
{
|
||||
fprintf(stderr, "error: commits is not an array\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
This is the array that contains objects describing latest commits in
|
||||
the repository. We check that the returned value really is an array.
|
||||
If the key ``commits`` doesn't exist, :func:`json_object_get()`
|
||||
returns *NULL*, but :func:`json_is_array()` handles this case, too.
|
||||
|
||||
Then we proceed to loop over all the commits in the array::
|
||||
|
||||
for(i = 0; i < json_array_size(commits); i++)
|
||||
{
|
||||
json_t *commit, *id, *message;
|
||||
const char *message_text;
|
||||
|
||||
commit = json_array_get(commits, i);
|
||||
if(!json_is_object(commit))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d is not an object\n", i + 1);
|
||||
return 1;
|
||||
}
|
||||
...
|
||||
|
||||
The function :func:`json_array_size()` returns the size of a JSON
|
||||
array. First, we again declare some variables and then extract the
|
||||
i'th element of the ``commits`` array using :func:`json_array_get()`.
|
||||
We also check that the resulting value is a JSON object.
|
||||
|
||||
Next we'll extract the commit ID and commit message, and check that
|
||||
they both are JSON strings::
|
||||
|
||||
id = json_object_get(commit, "id");
|
||||
if(!json_is_string(id))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: id is not a string\n", i + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
message = json_object_get(commit, "message");
|
||||
if(!json_is_string(message))
|
||||
{
|
||||
fprintf(stderr, "error: commit %d: message is not a string\n", i + 1);
|
||||
return 1;
|
||||
}
|
||||
...
|
||||
|
||||
And finally, we'll print the first 8 characters of the commit ID and
|
||||
the first line of the commit message. A C-style string is extracted
|
||||
from a JSON string using :func:`json_string_value()`::
|
||||
|
||||
message_text = json_string_value(message);
|
||||
printf("%.8s %.*s\n",
|
||||
json_string_value(id),
|
||||
newline_offset(message_text),
|
||||
message_text);
|
||||
}
|
||||
|
||||
After sending the HTTP request, we decoded the JSON text using
|
||||
:func:`json_loads()`, remember? It returns a *new reference* to the
|
||||
JSON value it decodes. When we're finished with the value, we'll need
|
||||
to decrease the reference count using :func:`json_decref()`. This way
|
||||
Jansson can release the resources::
|
||||
|
||||
json_decref(root);
|
||||
return 0;
|
||||
|
||||
For a detailed explanation of reference counting in Jansson, see
|
||||
:ref:`apiref-reference-count` in :ref:`apiref`.
|
||||
|
||||
The program's ready, let's test it and view the latest commits in
|
||||
Jansson's repository::
|
||||
|
||||
$ ./github_commits akheron jansson
|
||||
86dc1d62 Fix indentation
|
||||
b67e130f json_dumpf: Document the output shortage on error
|
||||
4cd77771 Enhance handling of circular references
|
||||
79009e62 json_dumps: Close the strbuffer if dumping fails
|
||||
76999799 doc: Fix a small typo in apiref
|
||||
22af193a doc/Makefile.am: Remove *.pyc in clean
|
||||
951d091f Make integer, real and string mutable
|
||||
185e107d Don't use non-portable asprintf()
|
||||
ca7703fb Merge branch '1.0'
|
||||
12cd4e8c jansson 1.0.4
|
||||
<etc...>
|
||||
|
||||
|
||||
Conclusion
|
||||
==========
|
||||
|
||||
In this tutorial, we implemented a program that fetches the latest
|
||||
commits of a GitHub repository using the GitHub commits API. Jansson
|
||||
was used to decode the JSON response and to extract the commit data.
|
||||
|
||||
This tutorial only covered a small part of Jansson. For example, we
|
||||
did not create or manipulate JSON values at all. Proceed to
|
||||
:ref:`apiref` to explore all features of Jansson.
|
||||
76
doc/upgrading.rst
Normal file
76
doc/upgrading.rst
Normal file
@@ -0,0 +1,76 @@
|
||||
.. highlight:: c
|
||||
|
||||
******************
|
||||
Upgrading from 1.x
|
||||
******************
|
||||
|
||||
This chapter lists the backwards incompatible changes introduced in
|
||||
Jansson 2.0, and the steps that are needed for upgrading your code.
|
||||
|
||||
**The incompatibilities are not dramatic.** The biggest change is that
|
||||
all decoding functions now require and extra parameter. Most programs
|
||||
can be modified to work with 2.0 by adding a ``0`` as the second
|
||||
parameter to all calls of :func:`json_loads()`, :func:`json_loadf()`
|
||||
and :func:`json_load_file()`.
|
||||
|
||||
|
||||
Compatibility
|
||||
=============
|
||||
|
||||
Jansson 2.0 is backwards incompatible with the Jansson 1.x releases.
|
||||
It is ABI incompatible, i.e. all programs dynamically linking to the
|
||||
Jansson library need to be recompiled. It's also API incompatible,
|
||||
i.e. the source code of programs using Jansson 1.x may need
|
||||
modifications to make them compile against Jansson 2.0.
|
||||
|
||||
All the 2.x releases are guaranteed to be backwards compatible for
|
||||
both ABI and API, so no recompilation or source changes are needed
|
||||
when upgrading from 2.x to 2.y.
|
||||
|
||||
|
||||
List of Incompatible Changes
|
||||
============================
|
||||
|
||||
**Decoding flags**
|
||||
For future needs, a ``flags`` parameter was added as the second
|
||||
parameter to all decoding functions, i.e. :func:`json_loads()`,
|
||||
:func:`json_loadf()` and :func:`json_load_file()`. All calls to
|
||||
these functions need to be changed by adding a ``0`` as the second
|
||||
argument. For example::
|
||||
|
||||
/* old code */
|
||||
json_loads(input, &error);
|
||||
|
||||
/* new code */
|
||||
json_loads(input, 0, &error);
|
||||
|
||||
|
||||
**Underlying type of JSON integers**
|
||||
The underlying C type of JSON integers has been changed from
|
||||
:type:`int` to the widest available signed integer type, i.e.
|
||||
:type:`long long` or :type:`long`, depending on whether
|
||||
:type:`long long` is supported on your system or not. This makes
|
||||
the whole 64-bit integer range available on most modern systems.
|
||||
|
||||
``jansson.h`` has a typedef :type:`json_int_t` to the underlying
|
||||
integer type. :type:`int` should still be used in most cases when
|
||||
dealing with smallish JSON integers, as the compiler handles
|
||||
implicit type coercion. Only when the full 64-bit range is needed,
|
||||
:type:`json_int_t` should be explicitly used.
|
||||
|
||||
|
||||
**Maximum encoder indentation depth**
|
||||
The maximum argument of the ``JSON_INDENT()`` macro has been
|
||||
changed from 255 to 31, to free up bits from the ``flags``
|
||||
parameter of :func:`json_dumps()`, :func:`json_dumpf()` and
|
||||
:func:`json_dump_file()`. If your code uses a bigger indentation
|
||||
than 31, it needs to be changed.
|
||||
|
||||
|
||||
**Unsigned integers in API functions**
|
||||
Version 2.0 unifies unsigned integer usage in the API. All uses of
|
||||
:type:`unsigned int` and :type:`unsigned long` have been replaced
|
||||
with :type:`size_t`. This includes flags, container sizes, etc.
|
||||
This should not require source code changes, as both
|
||||
:type:`unsigned int` and :type:`unsigned long` are usually
|
||||
compatible with :type:`size_t`.
|
||||
10
jansson.pc.in
Normal file
10
jansson.pc.in
Normal file
@@ -0,0 +1,10 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=${prefix}/include
|
||||
|
||||
Name: Jansson
|
||||
Description: Library for encoding, decoding and manipulating JSON data
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -ljansson
|
||||
Cflags: -I${includedir}
|
||||
@@ -1,18 +1,25 @@
|
||||
include_HEADERS = jansson.h
|
||||
include_HEADERS = jansson.h jansson_config.h
|
||||
|
||||
lib_LTLIBRARIES = libjansson.la
|
||||
libjansson_la_SOURCES = \
|
||||
dump.c \
|
||||
error.c \
|
||||
hashtable.c \
|
||||
hashtable.h \
|
||||
jansson_private.h \
|
||||
load.c \
|
||||
memory.c \
|
||||
pack_unpack.c \
|
||||
strbuffer.c \
|
||||
strbuffer.h \
|
||||
utf.c \
|
||||
utf.h \
|
||||
util.h \
|
||||
value.c
|
||||
libjansson_la_LDFLAGS = -version-info 0:1:0
|
||||
libjansson_la_LDFLAGS = \
|
||||
-export-symbols-regex '^json_' \
|
||||
-version-info 5:0:1
|
||||
|
||||
AM_CFLAGS = -Wall -Wextra -Werror -std=c99
|
||||
if GCC
|
||||
# These flags are gcc specific
|
||||
AM_CFLAGS = -Wall -Wextra -Wdeclaration-after-statement -Werror
|
||||
endif
|
||||
|
||||
375
src/dump.c
375
src/dump.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -9,10 +9,15 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <jansson.h>
|
||||
#include "jansson_private.h"
|
||||
#include "strbuffer.h"
|
||||
#include "utf.h"
|
||||
|
||||
#define MAX_INTEGER_STR_LENGTH 100
|
||||
#define MAX_REAL_STR_LENGTH 100
|
||||
|
||||
typedef int (*dump_func)(const char *buffer, int size, void *data);
|
||||
|
||||
@@ -36,54 +41,74 @@ static int dump_to_file(const char *buffer, int size, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_indent(uint32_t flags, int depth, dump_func dump, void *data)
|
||||
/* 32 spaces (the maximum indentation size) */
|
||||
static char whitespace[] = " ";
|
||||
|
||||
static int dump_indent(size_t flags, int depth, int space, dump_func dump, void *data)
|
||||
{
|
||||
if(JSON_INDENT(flags) > 0)
|
||||
{
|
||||
char *ws_buffer;
|
||||
int ws_count = JSON_INDENT(flags) * depth;
|
||||
int i, ws_count = JSON_INDENT(flags);
|
||||
|
||||
if(dump("\n", 1, data))
|
||||
return -1;
|
||||
|
||||
if(ws_count == 0)
|
||||
return 0;
|
||||
|
||||
ws_buffer = alloca(ws_count);
|
||||
memset(ws_buffer, ' ', ws_count);
|
||||
return dump(ws_buffer, ws_count, data);
|
||||
for(i = 0; i < depth; i++)
|
||||
{
|
||||
if(dump(whitespace, ws_count, data))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if(space && !(flags & JSON_COMPACT))
|
||||
{
|
||||
return dump(" ", 1, data);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_string(const char *str, dump_func dump, void *data)
|
||||
static int dump_string(const char *str, int ascii, dump_func dump, void *data)
|
||||
{
|
||||
const char *end;
|
||||
const char *pos, *end;
|
||||
int32_t codepoint;
|
||||
|
||||
if(dump("\"", 1, data))
|
||||
return -1;
|
||||
|
||||
end = str;
|
||||
end = pos = str;
|
||||
while(1)
|
||||
{
|
||||
const char *text;
|
||||
char seq[7];
|
||||
char seq[13];
|
||||
int length;
|
||||
|
||||
while(*end && *end != '\\' && *end != '"' && (*end < 0 || *end > 0x1F))
|
||||
end++;
|
||||
while(*end)
|
||||
{
|
||||
end = utf8_iterate(pos, &codepoint);
|
||||
if(!end)
|
||||
return -1;
|
||||
|
||||
if(end != str) {
|
||||
if(dump(str, end - str, data))
|
||||
/* mandatory escape or control char */
|
||||
if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
|
||||
break;
|
||||
|
||||
/* non-ASCII */
|
||||
if(ascii && codepoint > 0x7F)
|
||||
break;
|
||||
|
||||
pos = end;
|
||||
}
|
||||
|
||||
if(pos != str) {
|
||||
if(dump(str, pos - str, data))
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!*end)
|
||||
if(end == pos)
|
||||
break;
|
||||
|
||||
/* handle \, ", and control codes */
|
||||
length = 2;
|
||||
switch(*end)
|
||||
switch(codepoint)
|
||||
{
|
||||
case '\\': text = "\\\\"; break;
|
||||
case '\"': text = "\\\""; break;
|
||||
@@ -94,9 +119,27 @@ static int dump_string(const char *str, dump_func dump, void *data)
|
||||
case '\t': text = "\\t"; break;
|
||||
default:
|
||||
{
|
||||
sprintf(seq, "\\u00%02x", *end);
|
||||
/* codepoint is in BMP */
|
||||
if(codepoint < 0x10000)
|
||||
{
|
||||
sprintf(seq, "\\u%04x", codepoint);
|
||||
length = 6;
|
||||
}
|
||||
|
||||
/* not in BMP -> construct a UTF-16 surrogate pair */
|
||||
else
|
||||
{
|
||||
int32_t first, last;
|
||||
|
||||
codepoint -= 0x10000;
|
||||
first = 0xD800 | ((codepoint & 0xffc00) >> 10);
|
||||
last = 0xDC00 | (codepoint & 0x003ff);
|
||||
|
||||
sprintf(seq, "\\u%04x\\u%04x", first, last);
|
||||
length = 12;
|
||||
}
|
||||
|
||||
text = seq;
|
||||
length = 6;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -104,16 +147,29 @@ static int dump_string(const char *str, dump_func dump, void *data)
|
||||
if(dump(text, length, data))
|
||||
return -1;
|
||||
|
||||
end++;
|
||||
str = end;
|
||||
str = pos = end;
|
||||
}
|
||||
|
||||
return dump("\"", 1, data);
|
||||
}
|
||||
|
||||
static int do_dump(const json_t *json, uint32_t flags, int depth,
|
||||
static int object_key_compare_keys(const void *key1, const void *key2)
|
||||
{
|
||||
return strcmp((*(const object_key_t **)key1)->key,
|
||||
(*(const object_key_t **)key2)->key);
|
||||
}
|
||||
|
||||
static int object_key_compare_serials(const void *key1, const void *key2)
|
||||
{
|
||||
return (*(const object_key_t **)key1)->serial -
|
||||
(*(const object_key_t **)key2)->serial;
|
||||
}
|
||||
|
||||
static int do_dump(const json_t *json, size_t flags, int depth,
|
||||
dump_func dump, void *data)
|
||||
{
|
||||
int ascii = flags & JSON_ENSURE_ASCII ? 1 : 0;
|
||||
|
||||
switch(json_typeof(json)) {
|
||||
case JSON_NULL:
|
||||
return dump("null", 4, data);
|
||||
@@ -126,103 +182,231 @@ static int do_dump(const json_t *json, uint32_t flags, int depth,
|
||||
|
||||
case JSON_INTEGER:
|
||||
{
|
||||
char *buffer;
|
||||
int size, ret;
|
||||
char buffer[MAX_INTEGER_STR_LENGTH];
|
||||
int size;
|
||||
|
||||
size = asprintf(&buffer, "%d", json_integer_value(json));
|
||||
if(size == -1)
|
||||
size = snprintf(buffer, MAX_INTEGER_STR_LENGTH,
|
||||
"%" JSON_INTEGER_FORMAT,
|
||||
json_integer_value(json));
|
||||
if(size >= MAX_INTEGER_STR_LENGTH)
|
||||
return -1;
|
||||
|
||||
ret = dump(buffer, size, data);
|
||||
free(buffer);
|
||||
return ret;
|
||||
return dump(buffer, size, data);
|
||||
}
|
||||
|
||||
case JSON_REAL:
|
||||
{
|
||||
char *buffer;
|
||||
int size, ret;
|
||||
char buffer[MAX_REAL_STR_LENGTH];
|
||||
int size;
|
||||
|
||||
size = asprintf(&buffer, "%.17f", json_real_value(json));
|
||||
if(size == -1)
|
||||
size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%.17g",
|
||||
json_real_value(json));
|
||||
if(size >= MAX_REAL_STR_LENGTH)
|
||||
return -1;
|
||||
|
||||
ret = dump(buffer, size, data);
|
||||
free(buffer);
|
||||
return ret;
|
||||
/* Make sure there's a dot or 'e' in the output. Otherwise
|
||||
a real is converted to an integer when decoding */
|
||||
if(strchr(buffer, '.') == NULL &&
|
||||
strchr(buffer, 'e') == NULL)
|
||||
{
|
||||
if(size + 2 >= MAX_REAL_STR_LENGTH) {
|
||||
/* No space to append ".0" */
|
||||
return -1;
|
||||
}
|
||||
buffer[size] = '.';
|
||||
buffer[size + 1] = '0';
|
||||
size += 2;
|
||||
}
|
||||
|
||||
return dump(buffer, size, data);
|
||||
}
|
||||
|
||||
case JSON_STRING:
|
||||
return dump_string(json_string_value(json), dump, data);
|
||||
return dump_string(json_string_value(json), ascii, dump, data);
|
||||
|
||||
case JSON_ARRAY:
|
||||
{
|
||||
int i;
|
||||
int n = json_array_size(json);
|
||||
int n;
|
||||
json_array_t *array;
|
||||
|
||||
/* detect circular references */
|
||||
array = json_to_array(json);
|
||||
if(array->visited)
|
||||
goto array_error;
|
||||
array->visited = 1;
|
||||
|
||||
n = json_array_size(json);
|
||||
|
||||
if(dump("[", 1, data))
|
||||
return -1;
|
||||
if(n == 0)
|
||||
goto array_error;
|
||||
if(n == 0) {
|
||||
array->visited = 0;
|
||||
return dump("]", 1, data);
|
||||
if(dump_indent(flags, depth + 1, dump, data))
|
||||
return -1;
|
||||
}
|
||||
if(dump_indent(flags, depth + 1, 0, dump, data))
|
||||
goto array_error;
|
||||
|
||||
for(i = 0; i < n; ++i) {
|
||||
if(do_dump(json_array_get(json, i), flags, depth + 1,
|
||||
dump, data))
|
||||
return -1;
|
||||
goto array_error;
|
||||
|
||||
if(i < n - 1)
|
||||
{
|
||||
if(dump(",", 1, data) ||
|
||||
dump_indent(flags, depth + 1, dump, data))
|
||||
return -1;
|
||||
dump_indent(flags, depth + 1, 1, dump, data))
|
||||
goto array_error;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(dump_indent(flags, depth, dump, data))
|
||||
return -1;
|
||||
if(dump_indent(flags, depth, 0, dump, data))
|
||||
goto array_error;
|
||||
}
|
||||
}
|
||||
|
||||
array->visited = 0;
|
||||
return dump("]", 1, data);
|
||||
|
||||
array_error:
|
||||
array->visited = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
case JSON_OBJECT:
|
||||
{
|
||||
void *iter = json_object_iter((json_t *)json);
|
||||
json_object_t *object;
|
||||
void *iter;
|
||||
const char *separator;
|
||||
int separator_length;
|
||||
|
||||
if(flags & JSON_COMPACT) {
|
||||
separator = ":";
|
||||
separator_length = 1;
|
||||
}
|
||||
else {
|
||||
separator = ": ";
|
||||
separator_length = 2;
|
||||
}
|
||||
|
||||
/* detect circular references */
|
||||
object = json_to_object(json);
|
||||
if(object->visited)
|
||||
goto object_error;
|
||||
object->visited = 1;
|
||||
|
||||
iter = json_object_iter((json_t *)json);
|
||||
|
||||
if(dump("{", 1, data))
|
||||
return -1;
|
||||
if(!iter)
|
||||
goto object_error;
|
||||
if(!iter) {
|
||||
object->visited = 0;
|
||||
return dump("}", 1, data);
|
||||
if(dump_indent(flags, depth + 1, dump, data))
|
||||
return -1;
|
||||
|
||||
while(iter)
|
||||
{
|
||||
void *next = json_object_iter_next((json_t *)json, iter);
|
||||
|
||||
dump_string(json_object_iter_key(iter), dump, data);
|
||||
if(dump(": ", 2, data) ||
|
||||
do_dump(json_object_iter_value(iter), flags, depth + 1,
|
||||
dump, data))
|
||||
return -1;
|
||||
|
||||
if(next)
|
||||
{
|
||||
if(dump(",", 1, data) ||
|
||||
dump_indent(flags, depth + 1, dump, data))
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(dump_indent(flags, depth, dump, data))
|
||||
return -1;
|
||||
}
|
||||
|
||||
iter = next;
|
||||
}
|
||||
if(dump_indent(flags, depth + 1, 0, dump, data))
|
||||
goto object_error;
|
||||
|
||||
if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER)
|
||||
{
|
||||
const object_key_t **keys;
|
||||
size_t size, i;
|
||||
int (*cmp_func)(const void *, const void *);
|
||||
|
||||
size = json_object_size(json);
|
||||
keys = jsonp_malloc(size * sizeof(object_key_t *));
|
||||
if(!keys)
|
||||
goto object_error;
|
||||
|
||||
i = 0;
|
||||
while(iter)
|
||||
{
|
||||
keys[i] = jsonp_object_iter_fullkey(iter);
|
||||
iter = json_object_iter_next((json_t *)json, iter);
|
||||
i++;
|
||||
}
|
||||
assert(i == size);
|
||||
|
||||
if(flags & JSON_SORT_KEYS)
|
||||
cmp_func = object_key_compare_keys;
|
||||
else
|
||||
cmp_func = object_key_compare_serials;
|
||||
|
||||
qsort(keys, size, sizeof(object_key_t *), cmp_func);
|
||||
|
||||
for(i = 0; i < size; i++)
|
||||
{
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
key = keys[i]->key;
|
||||
value = json_object_get(json, key);
|
||||
assert(value);
|
||||
|
||||
dump_string(key, ascii, dump, data);
|
||||
if(dump(separator, separator_length, data) ||
|
||||
do_dump(value, flags, depth + 1, dump, data))
|
||||
{
|
||||
jsonp_free(keys);
|
||||
goto object_error;
|
||||
}
|
||||
|
||||
if(i < size - 1)
|
||||
{
|
||||
if(dump(",", 1, data) ||
|
||||
dump_indent(flags, depth + 1, 1, dump, data))
|
||||
{
|
||||
jsonp_free(keys);
|
||||
goto object_error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(dump_indent(flags, depth, 0, dump, data))
|
||||
{
|
||||
jsonp_free(keys);
|
||||
goto object_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jsonp_free(keys);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Don't sort keys */
|
||||
|
||||
while(iter)
|
||||
{
|
||||
void *next = json_object_iter_next((json_t *)json, iter);
|
||||
|
||||
dump_string(json_object_iter_key(iter), ascii, dump, data);
|
||||
if(dump(separator, separator_length, data) ||
|
||||
do_dump(json_object_iter_value(iter), flags, depth + 1,
|
||||
dump, data))
|
||||
goto object_error;
|
||||
|
||||
if(next)
|
||||
{
|
||||
if(dump(",", 1, data) ||
|
||||
dump_indent(flags, depth + 1, 1, dump, data))
|
||||
goto object_error;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(dump_indent(flags, depth, 0, dump, data))
|
||||
goto object_error;
|
||||
}
|
||||
|
||||
iter = next;
|
||||
}
|
||||
}
|
||||
|
||||
object->visited = 0;
|
||||
return dump("}", 1, data);
|
||||
|
||||
object_error:
|
||||
object->visited = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -232,40 +416,41 @@ static int do_dump(const json_t *json, uint32_t flags, int depth,
|
||||
}
|
||||
|
||||
|
||||
char *json_dumps(const json_t *json, uint32_t flags)
|
||||
char *json_dumps(const json_t *json, size_t flags)
|
||||
{
|
||||
strbuffer_t strbuff;
|
||||
char *result;
|
||||
|
||||
if(!json_is_array(json) && !json_is_object(json))
|
||||
return NULL;
|
||||
if(!(flags & JSON_ENCODE_ANY)) {
|
||||
if(!json_is_array(json) && !json_is_object(json))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(strbuffer_init(&strbuff))
|
||||
return NULL;
|
||||
|
||||
if(do_dump(json, flags, 0, dump_to_strbuffer, (void *)&strbuff))
|
||||
return NULL;
|
||||
|
||||
if(dump_to_strbuffer("\n", 1, (void *)&strbuff))
|
||||
if(do_dump(json, flags, 0, dump_to_strbuffer, (void *)&strbuff)) {
|
||||
strbuffer_close(&strbuff);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = strdup(strbuffer_value(&strbuff));
|
||||
result = jsonp_strdup(strbuffer_value(&strbuff));
|
||||
strbuffer_close(&strbuff);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int json_dumpf(const json_t *json, FILE *output, uint32_t flags)
|
||||
int json_dumpf(const json_t *json, FILE *output, size_t flags)
|
||||
{
|
||||
if(!json_is_array(json) && !json_is_object(json))
|
||||
return -1;
|
||||
if(!(flags & JSON_ENCODE_ANY)) {
|
||||
if(!json_is_array(json) && !json_is_object(json))
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(do_dump(json, flags, 0, dump_to_file, (void *)output))
|
||||
return -1;
|
||||
return dump_to_file("\n", 1, (void *)output);
|
||||
return do_dump(json, flags, 0, dump_to_file, (void *)output);
|
||||
}
|
||||
|
||||
int json_dump_file(const json_t *json, const char *path, uint32_t flags)
|
||||
int json_dump_file(const json_t *json, const char *path, size_t flags)
|
||||
{
|
||||
int result;
|
||||
|
||||
|
||||
62
src/error.c
Normal file
62
src/error.c
Normal 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);
|
||||
}
|
||||
166
src/hashtable.c
166
src/hashtable.c
@@ -1,29 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <jansson_config.h> /* for JSON_INLINE */
|
||||
#include "jansson_private.h" /* for container_of() */
|
||||
#include "hashtable.h"
|
||||
|
||||
typedef struct hashtable_list list_t;
|
||||
typedef struct hashtable_pair pair_t;
|
||||
typedef struct hashtable_bucket bucket_t;
|
||||
|
||||
#define container_of(ptr_, type_, member_) \
|
||||
((type_ *)((char *)ptr_ - (size_t)&((type_ *)0)->member_))
|
||||
|
||||
#define list_to_pair(list_) container_of(list_, pair_t, list)
|
||||
|
||||
static inline void list_init(list_t *list)
|
||||
static JSON_INLINE void list_init(list_t *list)
|
||||
{
|
||||
list->next = list;
|
||||
list->prev = list;
|
||||
}
|
||||
|
||||
static inline void list_insert(list_t *list, list_t *node)
|
||||
static JSON_INLINE void list_insert(list_t *list, list_t *node)
|
||||
{
|
||||
node->next = list;
|
||||
node->prev = list->prev;
|
||||
@@ -31,13 +30,13 @@ static inline void list_insert(list_t *list, list_t *node)
|
||||
list->prev = node;
|
||||
}
|
||||
|
||||
static inline void list_remove(list_t *list)
|
||||
static JSON_INLINE void list_remove(list_t *list)
|
||||
{
|
||||
list->prev->next = list->next;
|
||||
list->next->prev = list->prev;
|
||||
}
|
||||
|
||||
static inline int bucket_is_empty(hashtable_t *hashtable, bucket_t *bucket)
|
||||
static JSON_INLINE int bucket_is_empty(hashtable_t *hashtable, bucket_t *bucket)
|
||||
{
|
||||
return bucket->first == &hashtable->list && bucket->first == bucket->last;
|
||||
}
|
||||
@@ -57,22 +56,22 @@ static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket,
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int primes[] = {
|
||||
static size_t primes[] = {
|
||||
5, 13, 23, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593,
|
||||
49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
|
||||
12582917, 25165843, 50331653, 100663319, 201326611, 402653189,
|
||||
805306457, 1610612741
|
||||
};
|
||||
static const unsigned int num_primes = sizeof(primes) / sizeof(unsigned int);
|
||||
static const size_t num_primes = sizeof(primes) / sizeof(size_t);
|
||||
|
||||
static inline unsigned int num_buckets(hashtable_t *hashtable)
|
||||
static JSON_INLINE size_t num_buckets(hashtable_t *hashtable)
|
||||
{
|
||||
return primes[hashtable->num_buckets];
|
||||
}
|
||||
|
||||
|
||||
static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
|
||||
const void *key, unsigned int hash)
|
||||
const void *key, size_t hash)
|
||||
{
|
||||
list_t *list;
|
||||
pair_t *pair;
|
||||
@@ -98,11 +97,11 @@ static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
|
||||
|
||||
/* returns 0 on success, -1 if key was not found */
|
||||
static int hashtable_do_del(hashtable_t *hashtable,
|
||||
const void *key, unsigned int hash)
|
||||
const void *key, size_t hash)
|
||||
{
|
||||
pair_t *pair;
|
||||
bucket_t *bucket;
|
||||
unsigned int index;
|
||||
size_t index;
|
||||
|
||||
index = hash % num_buckets(hashtable);
|
||||
bucket = &hashtable->buckets[index];
|
||||
@@ -127,24 +126,41 @@ static int hashtable_do_del(hashtable_t *hashtable,
|
||||
if(hashtable->free_value)
|
||||
hashtable->free_value(pair->value);
|
||||
|
||||
free(pair);
|
||||
jsonp_free(pair);
|
||||
hashtable->size--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hashtable_do_clear(hashtable_t *hashtable)
|
||||
{
|
||||
list_t *list, *next;
|
||||
pair_t *pair;
|
||||
|
||||
for(list = hashtable->list.next; list != &hashtable->list; list = next)
|
||||
{
|
||||
next = list->next;
|
||||
pair = list_to_pair(list);
|
||||
if(hashtable->free_key)
|
||||
hashtable->free_key(pair->key);
|
||||
if(hashtable->free_value)
|
||||
hashtable->free_value(pair->value);
|
||||
jsonp_free(pair);
|
||||
}
|
||||
}
|
||||
|
||||
static int hashtable_do_rehash(hashtable_t *hashtable)
|
||||
{
|
||||
list_t *list, *next;
|
||||
pair_t *pair;
|
||||
unsigned int i, index, new_size;
|
||||
size_t i, index, new_size;
|
||||
|
||||
free(hashtable->buckets);
|
||||
jsonp_free(hashtable->buckets);
|
||||
|
||||
hashtable->num_buckets++;
|
||||
new_size = num_buckets(hashtable);
|
||||
|
||||
hashtable->buckets = malloc(new_size * sizeof(bucket_t));
|
||||
hashtable->buckets = jsonp_malloc(new_size * sizeof(bucket_t));
|
||||
if(!hashtable->buckets)
|
||||
return -1;
|
||||
|
||||
@@ -171,13 +187,13 @@ static int hashtable_do_rehash(hashtable_t *hashtable)
|
||||
hashtable_t *hashtable_create(key_hash_fn hash_key, key_cmp_fn cmp_keys,
|
||||
free_fn free_key, free_fn free_value)
|
||||
{
|
||||
hashtable_t *hashtable = malloc(sizeof(hashtable_t));
|
||||
hashtable_t *hashtable = jsonp_malloc(sizeof(hashtable_t));
|
||||
if(!hashtable)
|
||||
return NULL;
|
||||
|
||||
if(hashtable_init(hashtable, hash_key, cmp_keys, free_key, free_value))
|
||||
{
|
||||
free(hashtable);
|
||||
jsonp_free(hashtable);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -187,18 +203,18 @@ hashtable_t *hashtable_create(key_hash_fn hash_key, key_cmp_fn cmp_keys,
|
||||
void hashtable_destroy(hashtable_t *hashtable)
|
||||
{
|
||||
hashtable_close(hashtable);
|
||||
free(hashtable);
|
||||
jsonp_free(hashtable);
|
||||
}
|
||||
|
||||
int hashtable_init(hashtable_t *hashtable,
|
||||
key_hash_fn hash_key, key_cmp_fn cmp_keys,
|
||||
free_fn free_key, free_fn free_value)
|
||||
{
|
||||
unsigned int i;
|
||||
size_t i;
|
||||
|
||||
hashtable->size = 0;
|
||||
hashtable->num_buckets = 0; /* index to primes[] */
|
||||
hashtable->buckets = malloc(num_buckets(hashtable) * sizeof(bucket_t));
|
||||
hashtable->buckets = jsonp_malloc(num_buckets(hashtable) * sizeof(bucket_t));
|
||||
if(!hashtable->buckets)
|
||||
return -1;
|
||||
|
||||
@@ -220,60 +236,56 @@ int hashtable_init(hashtable_t *hashtable,
|
||||
|
||||
void hashtable_close(hashtable_t *hashtable)
|
||||
{
|
||||
list_t *list, *next;
|
||||
pair_t *pair;
|
||||
for(list = hashtable->list.next; list != &hashtable->list; list = next)
|
||||
{
|
||||
next = list->next;
|
||||
pair = list_to_pair(list);
|
||||
if(hashtable->free_key)
|
||||
hashtable->free_key(pair->key);
|
||||
if(hashtable->free_value)
|
||||
hashtable->free_value(pair->value);
|
||||
free(pair);
|
||||
}
|
||||
|
||||
free(hashtable->buckets);
|
||||
hashtable_do_clear(hashtable);
|
||||
jsonp_free(hashtable->buckets);
|
||||
}
|
||||
|
||||
int hashtable_set(hashtable_t *hashtable, void *key, void *value)
|
||||
{
|
||||
pair_t *pair;
|
||||
bucket_t *bucket;
|
||||
unsigned int hash, index;
|
||||
|
||||
hash = hashtable->hash_key(key);
|
||||
|
||||
/* if the key already exists, delete it */
|
||||
hashtable_do_del(hashtable, key, hash);
|
||||
size_t hash, index;
|
||||
|
||||
/* rehash if the load ratio exceeds 1 */
|
||||
if(hashtable->size >= num_buckets(hashtable))
|
||||
if(hashtable_do_rehash(hashtable))
|
||||
return -1;
|
||||
|
||||
pair = malloc(sizeof(pair_t));
|
||||
if(!pair)
|
||||
return -1;
|
||||
|
||||
pair->key = key;
|
||||
pair->value = value;
|
||||
pair->hash = hash;
|
||||
list_init(&pair->list);
|
||||
|
||||
hash = hashtable->hash_key(key);
|
||||
index = hash % num_buckets(hashtable);
|
||||
bucket = &hashtable->buckets[index];
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
|
||||
insert_to_bucket(hashtable, bucket, &pair->list);
|
||||
if(pair)
|
||||
{
|
||||
if(hashtable->free_key)
|
||||
hashtable->free_key(key);
|
||||
if(hashtable->free_value)
|
||||
hashtable->free_value(pair->value);
|
||||
pair->value = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
pair = jsonp_malloc(sizeof(pair_t));
|
||||
if(!pair)
|
||||
return -1;
|
||||
|
||||
hashtable->size++;
|
||||
pair->key = key;
|
||||
pair->value = value;
|
||||
pair->hash = hash;
|
||||
list_init(&pair->list);
|
||||
|
||||
insert_to_bucket(hashtable, bucket, &pair->list);
|
||||
|
||||
hashtable->size++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *hashtable_get(hashtable_t *hashtable, const void *key)
|
||||
{
|
||||
pair_t *pair;
|
||||
unsigned int hash;
|
||||
size_t hash;
|
||||
bucket_t *bucket;
|
||||
|
||||
hash = hashtable->hash_key(key);
|
||||
@@ -288,15 +300,47 @@ void *hashtable_get(hashtable_t *hashtable, const void *key)
|
||||
|
||||
int hashtable_del(hashtable_t *hashtable, const void *key)
|
||||
{
|
||||
unsigned int hash = hashtable->hash_key(key);
|
||||
size_t hash = hashtable->hash_key(key);
|
||||
return hashtable_do_del(hashtable, key, hash);
|
||||
}
|
||||
|
||||
void hashtable_clear(hashtable_t *hashtable)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
hashtable_do_clear(hashtable);
|
||||
|
||||
for(i = 0; i < num_buckets(hashtable); i++)
|
||||
{
|
||||
hashtable->buckets[i].first = hashtable->buckets[i].last =
|
||||
&hashtable->list;
|
||||
}
|
||||
|
||||
list_init(&hashtable->list);
|
||||
hashtable->size = 0;
|
||||
}
|
||||
|
||||
void *hashtable_iter(hashtable_t *hashtable)
|
||||
{
|
||||
return hashtable_iter_next(hashtable, &hashtable->list);
|
||||
}
|
||||
|
||||
void *hashtable_iter_at(hashtable_t *hashtable, const void *key)
|
||||
{
|
||||
pair_t *pair;
|
||||
size_t hash;
|
||||
bucket_t *bucket;
|
||||
|
||||
hash = hashtable->hash_key(key);
|
||||
bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
|
||||
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
if(!pair)
|
||||
return NULL;
|
||||
|
||||
return &pair->list;
|
||||
}
|
||||
|
||||
void *hashtable_iter_next(hashtable_t *hashtable, void *iter)
|
||||
{
|
||||
list_t *list = (list_t *)iter;
|
||||
@@ -316,3 +360,13 @@ void *hashtable_iter_value(void *iter)
|
||||
pair_t *pair = list_to_pair((list_t *)iter);
|
||||
return pair->value;
|
||||
}
|
||||
|
||||
void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value)
|
||||
{
|
||||
pair_t *pair = list_to_pair((list_t *)iter);
|
||||
|
||||
if(hashtable->free_value)
|
||||
hashtable->free_value(pair->value);
|
||||
|
||||
pair->value = value;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -8,19 +8,19 @@
|
||||
#ifndef HASHTABLE_H
|
||||
#define HASHTABLE_H
|
||||
|
||||
typedef unsigned int (*key_hash_fn)(const void *key);
|
||||
typedef size_t (*key_hash_fn)(const void *key);
|
||||
typedef int (*key_cmp_fn)(const void *key1, const void *key2);
|
||||
typedef void (*free_fn)(void *key);
|
||||
|
||||
struct hashtable_list {
|
||||
struct hashtable_list *prev;
|
||||
struct hashtable_list *next;
|
||||
struct hashtable_list *prev;
|
||||
struct hashtable_list *next;
|
||||
};
|
||||
|
||||
struct hashtable_pair {
|
||||
void *key;
|
||||
void *value;
|
||||
unsigned int hash;
|
||||
size_t hash;
|
||||
struct hashtable_list list;
|
||||
};
|
||||
|
||||
@@ -30,9 +30,9 @@ struct hashtable_bucket {
|
||||
};
|
||||
|
||||
typedef struct hashtable {
|
||||
unsigned int size;
|
||||
size_t size;
|
||||
struct hashtable_bucket *buckets;
|
||||
unsigned int num_buckets; /* index to primes[] */
|
||||
size_t num_buckets; /* index to primes[] */
|
||||
struct hashtable_list list;
|
||||
|
||||
key_hash_fn hash_key;
|
||||
@@ -134,6 +134,15 @@ void *hashtable_get(hashtable_t *hashtable, const void *key);
|
||||
*/
|
||||
int hashtable_del(hashtable_t *hashtable, const void *key);
|
||||
|
||||
/**
|
||||
* hashtable_clear - Clear hashtable
|
||||
*
|
||||
* @hashtable: The hashtable object
|
||||
*
|
||||
* Removes all items from the hashtable.
|
||||
*/
|
||||
void hashtable_clear(hashtable_t *hashtable);
|
||||
|
||||
/**
|
||||
* hashtable_iter - Iterate over hashtable
|
||||
*
|
||||
@@ -151,6 +160,17 @@ int hashtable_del(hashtable_t *hashtable, const void *key);
|
||||
*/
|
||||
void *hashtable_iter(hashtable_t *hashtable);
|
||||
|
||||
/**
|
||||
* hashtable_iter_at - Return an iterator at a specific key
|
||||
*
|
||||
* @hashtable: The hashtable object
|
||||
* @key: The key that the iterator should point to
|
||||
*
|
||||
* Like hashtable_iter() but returns an iterator pointing to a
|
||||
* specific key.
|
||||
*/
|
||||
void *hashtable_iter_at(hashtable_t *hashtable, const void *key);
|
||||
|
||||
/**
|
||||
* hashtable_iter_next - Advance an iterator
|
||||
*
|
||||
@@ -176,4 +196,12 @@ void *hashtable_iter_key(void *iter);
|
||||
*/
|
||||
void *hashtable_iter_value(void *iter);
|
||||
|
||||
/**
|
||||
* hashtable_iter_set - Set the value pointed by an iterator
|
||||
*
|
||||
* @iter: The iterator
|
||||
* @value: The value to set
|
||||
*/
|
||||
void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value);
|
||||
|
||||
#endif
|
||||
|
||||
199
src/jansson.h
199
src/jansson.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -9,7 +9,30 @@
|
||||
#define JANSSON_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h> /* for size_t */
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <jansson_config.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* version */
|
||||
|
||||
#define JANSSON_MAJOR_VERSION 2
|
||||
#define JANSSON_MINOR_VERSION 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 */
|
||||
|
||||
@@ -26,9 +49,17 @@ typedef enum {
|
||||
|
||||
typedef struct {
|
||||
json_type type;
|
||||
unsigned long refcount;
|
||||
size_t refcount;
|
||||
} json_t;
|
||||
|
||||
#if JSON_INTEGER_IS_LONG_LONG
|
||||
#define JSON_INTEGER_FORMAT "lld"
|
||||
typedef long long json_int_t;
|
||||
#else
|
||||
#define JSON_INTEGER_FORMAT "ld"
|
||||
typedef long json_int_t;
|
||||
#endif /* JSON_INTEGER_IS_LONG_LONG */
|
||||
|
||||
#define json_typeof(json) ((json)->type)
|
||||
#define json_is_object(json) (json && json_typeof(json) == JSON_OBJECT)
|
||||
#define json_is_array(json) (json && json_typeof(json) == JSON_ARRAY)
|
||||
@@ -46,15 +77,17 @@ typedef struct {
|
||||
json_t *json_object(void);
|
||||
json_t *json_array(void);
|
||||
json_t *json_string(const char *value);
|
||||
json_t *json_integer(int value);
|
||||
json_t *json_string_nocheck(const char *value);
|
||||
json_t *json_integer(json_int_t value);
|
||||
json_t *json_real(double value);
|
||||
json_t *json_true(void);
|
||||
json_t *json_false(void);
|
||||
json_t *json_null(void);
|
||||
|
||||
static inline json_t *json_incref(json_t *json)
|
||||
static JSON_INLINE
|
||||
json_t *json_incref(json_t *json)
|
||||
{
|
||||
if(json)
|
||||
if(json && json->refcount != (size_t)-1)
|
||||
++json->refcount;
|
||||
return json;
|
||||
}
|
||||
@@ -62,51 +95,159 @@ static inline json_t *json_incref(json_t *json)
|
||||
/* do not call json_delete directly */
|
||||
void json_delete(json_t *json);
|
||||
|
||||
static inline void json_decref(json_t *json)
|
||||
static JSON_INLINE
|
||||
void json_decref(json_t *json)
|
||||
{
|
||||
if(json && --json->refcount == 0)
|
||||
if(json && json->refcount != (size_t)-1 && --json->refcount == 0)
|
||||
json_delete(json);
|
||||
}
|
||||
|
||||
|
||||
/* error reporting */
|
||||
|
||||
#define JSON_ERROR_TEXT_LENGTH 160
|
||||
#define JSON_ERROR_SOURCE_LENGTH 80
|
||||
|
||||
typedef struct {
|
||||
int line;
|
||||
int column;
|
||||
int position;
|
||||
char source[JSON_ERROR_SOURCE_LENGTH];
|
||||
char text[JSON_ERROR_TEXT_LENGTH];
|
||||
} json_error_t;
|
||||
|
||||
|
||||
/* getters, setters, manipulation */
|
||||
|
||||
size_t json_object_size(const json_t *object);
|
||||
json_t *json_object_get(const json_t *object, const char *key);
|
||||
int json_object_set(json_t *object, const char *key, json_t *value);
|
||||
int json_object_set_new(json_t *object, const char *key, json_t *value);
|
||||
int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value);
|
||||
int json_object_del(json_t *object, const char *key);
|
||||
int json_object_clear(json_t *object);
|
||||
int json_object_update(json_t *object, json_t *other);
|
||||
void *json_object_iter(json_t *object);
|
||||
void *json_object_iter_at(json_t *object, const char *key);
|
||||
void *json_object_iter_next(json_t *object, void *iter);
|
||||
const char *json_object_iter_key(void *iter);
|
||||
json_t *json_object_iter_value(void *iter);
|
||||
int json_object_iter_set_new(json_t *object, void *iter, json_t *value);
|
||||
|
||||
unsigned int json_array_size(const json_t *array);
|
||||
json_t *json_array_get(const json_t *array, unsigned int index);
|
||||
int json_array_set(json_t *array, unsigned int index, json_t *value);
|
||||
int json_array_append(json_t *array, json_t *value);
|
||||
static JSON_INLINE
|
||||
int json_object_set(json_t *object, const char *key, json_t *value)
|
||||
{
|
||||
return json_object_set_new(object, key, json_incref(value));
|
||||
}
|
||||
|
||||
const char *json_string_value(const json_t *json);
|
||||
int json_integer_value(const json_t *json);
|
||||
double json_real_value(const json_t *json);
|
||||
static JSON_INLINE
|
||||
int json_object_set_nocheck(json_t *object, const char *key, json_t *value)
|
||||
{
|
||||
return json_object_set_new_nocheck(object, key, json_incref(value));
|
||||
}
|
||||
|
||||
static JSON_INLINE
|
||||
int json_object_iter_set(json_t *object, void *iter, json_t *value)
|
||||
{
|
||||
return json_object_iter_set_new(object, iter, json_incref(value));
|
||||
}
|
||||
|
||||
size_t json_array_size(const json_t *array);
|
||||
json_t *json_array_get(const json_t *array, size_t index);
|
||||
int json_array_set_new(json_t *array, size_t index, json_t *value);
|
||||
int json_array_append_new(json_t *array, json_t *value);
|
||||
int json_array_insert_new(json_t *array, size_t index, json_t *value);
|
||||
int json_array_remove(json_t *array, size_t index);
|
||||
int json_array_clear(json_t *array);
|
||||
int json_array_extend(json_t *array, json_t *other);
|
||||
|
||||
static JSON_INLINE
|
||||
int json_array_set(json_t *array, size_t index, json_t *value)
|
||||
{
|
||||
return json_array_set_new(array, index, json_incref(value));
|
||||
}
|
||||
|
||||
static JSON_INLINE
|
||||
int json_array_append(json_t *array, json_t *value)
|
||||
{
|
||||
return json_array_append_new(array, json_incref(value));
|
||||
}
|
||||
|
||||
static JSON_INLINE
|
||||
int json_array_insert(json_t *array, size_t index, json_t *value)
|
||||
{
|
||||
return json_array_insert_new(array, index, json_incref(value));
|
||||
}
|
||||
|
||||
const char *json_string_value(const json_t *string);
|
||||
json_int_t json_integer_value(const json_t *integer);
|
||||
double json_real_value(const json_t *real);
|
||||
double json_number_value(const json_t *json);
|
||||
|
||||
int json_string_set(json_t *string, const char *value);
|
||||
int json_string_set_nocheck(json_t *string, const char *value);
|
||||
int json_integer_set(json_t *integer, json_int_t value);
|
||||
int json_real_set(json_t *real, double value);
|
||||
|
||||
/* loading, printing */
|
||||
|
||||
#define JSON_ERROR_TEXT_LENGTH 160
|
||||
/* pack, unpack */
|
||||
|
||||
typedef struct {
|
||||
char text[JSON_ERROR_TEXT_LENGTH];
|
||||
int line;
|
||||
} json_error_t;
|
||||
json_t *json_pack(const char *fmt, ...);
|
||||
json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...);
|
||||
json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap);
|
||||
|
||||
json_t *json_loads(const char *input, json_error_t *error);
|
||||
json_t *json_loadf(FILE *input, json_error_t *error);
|
||||
json_t *json_load_file(const char *path, json_error_t *error);
|
||||
#define JSON_VALIDATE_ONLY 0x1
|
||||
#define JSON_STRICT 0x2
|
||||
|
||||
#define JSON_INDENT(n) (n & 0xFF)
|
||||
int json_unpack(json_t *root, const char *fmt, ...);
|
||||
int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...);
|
||||
int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, va_list ap);
|
||||
|
||||
char *json_dumps(const json_t *json, uint32_t flags);
|
||||
int json_dumpf(const json_t *json, FILE *output, uint32_t flags);
|
||||
int json_dump_file(const json_t *json, const char *path, uint32_t flags);
|
||||
|
||||
/* equality */
|
||||
|
||||
int json_equal(json_t *value1, json_t *value2);
|
||||
|
||||
|
||||
/* copying */
|
||||
|
||||
json_t *json_copy(json_t *value);
|
||||
json_t *json_deep_copy(json_t *value);
|
||||
|
||||
|
||||
/* decoding */
|
||||
|
||||
#define JSON_REJECT_DUPLICATES 0x1
|
||||
#define JSON_DISABLE_EOF_CHECK 0x2
|
||||
|
||||
json_t *json_loads(const char *input, size_t flags, json_error_t *error);
|
||||
json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error);
|
||||
json_t *json_loadf(FILE *input, size_t flags, json_error_t *error);
|
||||
json_t *json_load_file(const char *path, size_t flags, json_error_t *error);
|
||||
|
||||
|
||||
/* encoding */
|
||||
|
||||
#define JSON_INDENT(n) (n & 0x1F)
|
||||
#define JSON_COMPACT 0x20
|
||||
#define JSON_ENSURE_ASCII 0x40
|
||||
#define JSON_SORT_KEYS 0x80
|
||||
#define JSON_PRESERVE_ORDER 0x100
|
||||
#define JSON_ENCODE_ANY 0x200
|
||||
|
||||
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
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
34
src/jansson_config.h.in
Normal file
34
src/jansson_config.h.in
Normal 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
|
||||
34
src/jansson_config.h.win32
Normal file
34
src/jansson_config.h.win32
Normal 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
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -8,8 +8,84 @@
|
||||
#ifndef JANSSON_PRIVATE_H
|
||||
#define JANSSON_PRIVATE_H
|
||||
|
||||
int json_object_set_nocheck(json_t *json, const char *key, json_t *value);
|
||||
json_t *json_string_nocheck(const char *value);
|
||||
#include <stddef.h>
|
||||
#include "jansson.h"
|
||||
#include "hashtable.h"
|
||||
|
||||
#define container_of(ptr_, type_, member_) \
|
||||
((type_ *)((char *)ptr_ - offsetof(type_, member_)))
|
||||
|
||||
/* On some platforms, max() may already be defined */
|
||||
#ifndef max
|
||||
#define max(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/* va_copy is a C99 feature. In C89 implementations, it's sometimes
|
||||
available as __va_copy. If not, memcpy() should do the trick. */
|
||||
#ifndef va_copy
|
||||
#ifdef __va_copy
|
||||
#define va_copy __va_copy
|
||||
#else
|
||||
#define va_copy(a, b) memcpy(&(a), &(b), sizeof(va_list))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
hashtable_t hashtable;
|
||||
size_t serial;
|
||||
int visited;
|
||||
} json_object_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
size_t size;
|
||||
size_t entries;
|
||||
json_t **table;
|
||||
int visited;
|
||||
} json_array_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
char *value;
|
||||
} json_string_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
double value;
|
||||
} json_real_t;
|
||||
|
||||
typedef struct {
|
||||
json_t json;
|
||||
json_int_t value;
|
||||
} json_integer_t;
|
||||
|
||||
#define json_to_object(json_) container_of(json_, json_object_t, json)
|
||||
#define json_to_array(json_) container_of(json_, json_array_t, json)
|
||||
#define json_to_string(json_) container_of(json_, json_string_t, json)
|
||||
#define json_to_real(json_) container_of(json_, json_real_t, json)
|
||||
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
|
||||
|
||||
size_t jsonp_hash_str(const void *ptr);
|
||||
int jsonp_str_equal(const void *ptr1, const void *ptr2);
|
||||
|
||||
typedef struct {
|
||||
size_t serial;
|
||||
char key[1];
|
||||
} object_key_t;
|
||||
|
||||
const object_key_t *jsonp_object_iter_fullkey(void *iter);
|
||||
|
||||
void jsonp_error_init(json_error_t *error, const char *source);
|
||||
void jsonp_error_set_source(json_error_t *error, const char *source);
|
||||
void jsonp_error_set(json_error_t *error, int line, int column,
|
||||
size_t position, const char *msg, ...);
|
||||
void jsonp_error_vset(json_error_t *error, int line, int column,
|
||||
size_t position, const char *msg, va_list ap);
|
||||
|
||||
/* Wrappers for custom memory functions */
|
||||
void* jsonp_malloc(size_t size);
|
||||
void jsonp_free(void *ptr);
|
||||
char *jsonp_strdup(const char *str);
|
||||
|
||||
#endif
|
||||
|
||||
472
src/load.c
472
src/load.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -8,11 +8,10 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <jansson.h>
|
||||
@@ -20,6 +19,10 @@
|
||||
#include "strbuffer.h"
|
||||
#include "utf.h"
|
||||
|
||||
#define STREAM_STATE_OK 0
|
||||
#define STREAM_STATE_EOF -1
|
||||
#define STREAM_STATE_ERROR -2
|
||||
|
||||
#define TOKEN_INVALID -1
|
||||
#define TOKEN_EOF 0
|
||||
#define TOKEN_STRING 256
|
||||
@@ -29,115 +32,125 @@
|
||||
#define TOKEN_FALSE 260
|
||||
#define TOKEN_NULL 261
|
||||
|
||||
/* read one byte from stream, return EOF on end of file */
|
||||
/* Read one byte from stream, convert to unsigned char, then int, and
|
||||
return. return EOF on end of file. This corresponds to the
|
||||
behaviour of fgetc(). */
|
||||
typedef int (*get_func)(void *data);
|
||||
|
||||
/* return non-zero if end of file has been reached */
|
||||
typedef int (*eof_func)(void *data);
|
||||
|
||||
typedef struct {
|
||||
get_func get;
|
||||
eof_func eof;
|
||||
void *data;
|
||||
int stream_pos;
|
||||
char buffer[5];
|
||||
int buffer_pos;
|
||||
int state;
|
||||
int line;
|
||||
int column, last_column;
|
||||
size_t position;
|
||||
} stream_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
stream_t stream;
|
||||
strbuffer_t saved_text;
|
||||
int token;
|
||||
int line, column;
|
||||
union {
|
||||
char *string;
|
||||
int integer;
|
||||
json_int_t integer;
|
||||
double real;
|
||||
} value;
|
||||
} lex_t;
|
||||
|
||||
#define stream_to_lex(stream) container_of(stream, lex_t, stream)
|
||||
|
||||
|
||||
/*** error reporting ***/
|
||||
|
||||
static void error_init(json_error_t *error)
|
||||
{
|
||||
if(error)
|
||||
{
|
||||
error->text[0] = '\0';
|
||||
error->line = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void error_set(json_error_t *error, const lex_t *lex,
|
||||
const char *msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char text[JSON_ERROR_TEXT_LENGTH];
|
||||
char msg_text[JSON_ERROR_TEXT_LENGTH];
|
||||
|
||||
if(!error || error->text[0] != '\0') {
|
||||
/* error already set */
|
||||
int line = -1, col = -1;
|
||||
size_t pos = 0;
|
||||
const char *result = msg_text;
|
||||
|
||||
if(!error)
|
||||
return;
|
||||
}
|
||||
|
||||
va_start(ap, msg);
|
||||
vsnprintf(text, JSON_ERROR_TEXT_LENGTH, msg, ap);
|
||||
vsnprintf(msg_text, JSON_ERROR_TEXT_LENGTH, msg, ap);
|
||||
va_end(ap);
|
||||
|
||||
if(lex)
|
||||
{
|
||||
const char *saved_text = strbuffer_value(&lex->saved_text);
|
||||
error->line = lex->line;
|
||||
char msg_with_context[JSON_ERROR_TEXT_LENGTH];
|
||||
|
||||
line = lex->stream.line;
|
||||
col = lex->stream.column;
|
||||
pos = lex->stream.position;
|
||||
|
||||
if(saved_text && saved_text[0])
|
||||
{
|
||||
if(lex->saved_text.length <= 20) {
|
||||
snprintf(error->text, JSON_ERROR_TEXT_LENGTH,
|
||||
"%s near '%s'", text, saved_text);
|
||||
snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
|
||||
"%s near '%s'", msg_text, saved_text);
|
||||
result = msg_with_context;
|
||||
}
|
||||
else
|
||||
snprintf(error->text, JSON_ERROR_TEXT_LENGTH, "%s", text);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(error->text, JSON_ERROR_TEXT_LENGTH,
|
||||
"%s near end of file", text);
|
||||
if(lex->stream.state == STREAM_STATE_ERROR) {
|
||||
/* No context for UTF-8 decoding errors */
|
||||
result = msg_text;
|
||||
}
|
||||
else {
|
||||
snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
|
||||
"%s near end of file", msg_text);
|
||||
result = msg_with_context;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error->line = -1;
|
||||
snprintf(error->text, JSON_ERROR_TEXT_LENGTH, "%s", text);
|
||||
}
|
||||
|
||||
jsonp_error_set(error, line, col, pos, "%s", result);
|
||||
}
|
||||
|
||||
|
||||
/*** lexical analyzer ***/
|
||||
|
||||
void stream_init(stream_t *stream, get_func get, eof_func eof, void *data)
|
||||
static void
|
||||
stream_init(stream_t *stream, get_func get, void *data)
|
||||
{
|
||||
stream->get = get;
|
||||
stream->eof = eof;
|
||||
stream->data = data;
|
||||
stream->stream_pos = 0;
|
||||
stream->buffer[0] = '\0';
|
||||
stream->buffer_pos = 0;
|
||||
|
||||
stream->state = STREAM_STATE_OK;
|
||||
stream->line = 1;
|
||||
stream->column = 0;
|
||||
stream->position = 0;
|
||||
}
|
||||
|
||||
static char stream_get(stream_t *stream, json_error_t *error)
|
||||
static int stream_get(stream_t *stream, json_error_t *error)
|
||||
{
|
||||
char c;
|
||||
int c;
|
||||
|
||||
if(stream->state != STREAM_STATE_OK)
|
||||
return stream->state;
|
||||
|
||||
if(!stream->buffer[stream->buffer_pos])
|
||||
{
|
||||
stream->buffer[0] = stream->get(stream->data);
|
||||
c = stream->get(stream->data);
|
||||
if(c == EOF) {
|
||||
stream->state = STREAM_STATE_EOF;
|
||||
return STREAM_STATE_EOF;
|
||||
}
|
||||
|
||||
stream->buffer[0] = c;
|
||||
stream->buffer_pos = 0;
|
||||
|
||||
c = stream->buffer[0];
|
||||
|
||||
if(c == EOF && stream->eof(stream->data))
|
||||
return EOF;
|
||||
|
||||
if(c < 0)
|
||||
if(0x80 <= c && c <= 0xFF)
|
||||
{
|
||||
/* multi-byte UTF-8 sequence */
|
||||
int i, count;
|
||||
@@ -151,33 +164,50 @@ static char stream_get(stream_t *stream, json_error_t *error)
|
||||
for(i = 1; i < count; i++)
|
||||
stream->buffer[i] = stream->get(stream->data);
|
||||
|
||||
if(!utf8_check_full(stream->buffer, count))
|
||||
if(!utf8_check_full(stream->buffer, count, NULL))
|
||||
goto out;
|
||||
|
||||
stream->stream_pos += count;
|
||||
stream->buffer[count] = '\0';
|
||||
}
|
||||
else {
|
||||
else
|
||||
stream->buffer[1] = '\0';
|
||||
stream->stream_pos++;
|
||||
}
|
||||
}
|
||||
|
||||
return stream->buffer[stream->buffer_pos++];
|
||||
c = stream->buffer[stream->buffer_pos++];
|
||||
|
||||
stream->position++;
|
||||
if(c == '\n') {
|
||||
stream->line++;
|
||||
stream->last_column = stream->column;
|
||||
stream->column = 0;
|
||||
}
|
||||
else if(utf8_check_first(c)) {
|
||||
/* track the Unicode character column, so increment only if
|
||||
this is the first character of a UTF-8 sequence */
|
||||
stream->column++;
|
||||
}
|
||||
|
||||
return c;
|
||||
|
||||
out:
|
||||
error_set(error, NULL, "unable to decode byte 0x%x at position %d",
|
||||
(unsigned char)c, stream->stream_pos);
|
||||
|
||||
stream->buffer[0] = EOF;
|
||||
stream->buffer[1] = '\0';
|
||||
stream->buffer_pos = 1;
|
||||
|
||||
return EOF;
|
||||
stream->state = STREAM_STATE_ERROR;
|
||||
error_set(error, stream_to_lex(stream), "unable to decode byte 0x%x", c);
|
||||
return STREAM_STATE_ERROR;
|
||||
}
|
||||
|
||||
static void stream_unget(stream_t *stream, char c)
|
||||
static void stream_unget(stream_t *stream, int c)
|
||||
{
|
||||
if(c == STREAM_STATE_EOF || c == STREAM_STATE_ERROR)
|
||||
return;
|
||||
|
||||
stream->position--;
|
||||
if(c == '\n') {
|
||||
stream->line--;
|
||||
stream->column = stream->last_column;
|
||||
}
|
||||
else if(utf8_check_first(c))
|
||||
stream->column--;
|
||||
|
||||
assert(stream->buffer_pos > 0);
|
||||
stream->buffer_pos--;
|
||||
assert(stream->buffer[stream->buffer_pos] == c);
|
||||
@@ -189,29 +219,32 @@ static int lex_get(lex_t *lex, json_error_t *error)
|
||||
return stream_get(&lex->stream, error);
|
||||
}
|
||||
|
||||
static int lex_eof(lex_t *lex)
|
||||
{
|
||||
return lex->stream.eof(lex->stream.data);
|
||||
}
|
||||
|
||||
static void lex_save(lex_t *lex, char c)
|
||||
static void lex_save(lex_t *lex, int c)
|
||||
{
|
||||
strbuffer_append_byte(&lex->saved_text, c);
|
||||
}
|
||||
|
||||
static int lex_get_save(lex_t *lex, json_error_t *error)
|
||||
{
|
||||
char c = stream_get(&lex->stream, error);
|
||||
lex_save(lex, c);
|
||||
int c = stream_get(&lex->stream, error);
|
||||
if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR)
|
||||
lex_save(lex, c);
|
||||
return c;
|
||||
}
|
||||
|
||||
static void lex_unget_unsave(lex_t *lex, char c)
|
||||
static void lex_unget(lex_t *lex, int c)
|
||||
{
|
||||
char d;
|
||||
stream_unget(&lex->stream, c);
|
||||
d = strbuffer_pop(&lex->saved_text);
|
||||
assert(c == d);
|
||||
}
|
||||
|
||||
static void lex_unget_unsave(lex_t *lex, int c)
|
||||
{
|
||||
if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR) {
|
||||
char d;
|
||||
stream_unget(&lex->stream, c);
|
||||
d = strbuffer_pop(&lex->saved_text);
|
||||
assert(c == d);
|
||||
}
|
||||
}
|
||||
|
||||
static void lex_save_cached(lex_t *lex)
|
||||
@@ -220,14 +253,15 @@ static void lex_save_cached(lex_t *lex)
|
||||
{
|
||||
lex_save(lex, lex->stream.buffer[lex->stream.buffer_pos]);
|
||||
lex->stream.buffer_pos++;
|
||||
lex->stream.position++;
|
||||
}
|
||||
}
|
||||
|
||||
/* assumes that str points to 'u' plus at least 4 valid hex digits */
|
||||
static int decode_unicode_escape(const char *str)
|
||||
static int32_t decode_unicode_escape(const char *str)
|
||||
{
|
||||
int i;
|
||||
int value = 0;
|
||||
int32_t value = 0;
|
||||
|
||||
assert(str[0] == 'u');
|
||||
|
||||
@@ -249,7 +283,7 @@ static int decode_unicode_escape(const char *str)
|
||||
|
||||
static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
{
|
||||
char c;
|
||||
int c;
|
||||
const char *p;
|
||||
char *t;
|
||||
int i;
|
||||
@@ -257,13 +291,14 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
lex->value.string = NULL;
|
||||
lex->token = TOKEN_INVALID;
|
||||
|
||||
/* skip the " */
|
||||
c = lex_get_save(lex, error);
|
||||
|
||||
while(c != '"') {
|
||||
if(c == EOF) {
|
||||
if(lex_eof(lex))
|
||||
error_set(error, lex, "premature end of input");
|
||||
if(c == STREAM_STATE_ERROR)
|
||||
goto out;
|
||||
|
||||
else if(c == STREAM_STATE_EOF) {
|
||||
error_set(error, lex, "premature end of input");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -283,7 +318,6 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
c = lex_get_save(lex, error);
|
||||
for(i = 0; i < 4; i++) {
|
||||
if(!isxdigit(c)) {
|
||||
lex_unget_unsave(lex, c);
|
||||
error_set(error, lex, "invalid escape");
|
||||
goto out;
|
||||
}
|
||||
@@ -294,7 +328,6 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
c == 'f' || c == 'n' || c == 'r' || c == 't')
|
||||
c = lex_get_save(lex, error);
|
||||
else {
|
||||
lex_unget_unsave(lex, c);
|
||||
error_set(error, lex, "invalid escape");
|
||||
goto out;
|
||||
}
|
||||
@@ -310,7 +343,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
- two \uXXXX escapes (length 12) forming an UTF-16 surrogate pair
|
||||
are converted to 4 bytes
|
||||
*/
|
||||
lex->value.string = malloc(lex->saved_text.length + 1);
|
||||
lex->value.string = jsonp_malloc(lex->saved_text.length + 1);
|
||||
if(!lex->value.string) {
|
||||
/* this is not very nice, since TOKEN_INVALID is returned */
|
||||
goto out;
|
||||
@@ -328,7 +361,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
if(*p == 'u') {
|
||||
char buffer[4];
|
||||
int length;
|
||||
int value;
|
||||
int32_t value;
|
||||
|
||||
value = decode_unicode_escape(p);
|
||||
p += 5;
|
||||
@@ -336,14 +369,15 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
if(0xD800 <= value && value <= 0xDBFF) {
|
||||
/* surrogate pair */
|
||||
if(*p == '\\' && *(p + 1) == 'u') {
|
||||
int value2 = decode_unicode_escape(++p);
|
||||
int32_t value2 = decode_unicode_escape(++p);
|
||||
p += 5;
|
||||
|
||||
if(0xDC00 <= value2 && value2 <= 0xDFFF) {
|
||||
/* valid second surrogate */
|
||||
value = ((value - 0xD800) << 10) +
|
||||
(value2 - 0xDC00) +
|
||||
0x10000;
|
||||
value =
|
||||
((value - 0xD800) << 10) +
|
||||
(value2 - 0xDC00) +
|
||||
0x10000;
|
||||
}
|
||||
else {
|
||||
/* invalid second surrogate */
|
||||
@@ -399,13 +433,20 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
|
||||
return;
|
||||
|
||||
out:
|
||||
free(lex->value.string);
|
||||
jsonp_free(lex->value.string);
|
||||
}
|
||||
|
||||
static void lex_scan_number(lex_t *lex, char c, json_error_t *error)
|
||||
#if JSON_INTEGER_IS_LONG_LONG
|
||||
#define json_strtoint strtoll
|
||||
#else
|
||||
#define json_strtoint strtol
|
||||
#endif
|
||||
|
||||
static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
|
||||
{
|
||||
const char *saved_text;
|
||||
char *end;
|
||||
double value;
|
||||
|
||||
lex->token = TOKEN_INVALID;
|
||||
|
||||
@@ -419,27 +460,46 @@ static void lex_scan_number(lex_t *lex, char c, json_error_t *error)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else /* c != '0' */ {
|
||||
else if(isdigit(c)) {
|
||||
c = lex_get_save(lex, error);
|
||||
while(isdigit(c))
|
||||
c = lex_get_save(lex, error);
|
||||
}
|
||||
else {
|
||||
lex_unget_unsave(lex, c);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(c != '.' && c != 'E' && c != 'e') {
|
||||
json_int_t value;
|
||||
|
||||
lex_unget_unsave(lex, c);
|
||||
lex->token = TOKEN_INTEGER;
|
||||
|
||||
saved_text = strbuffer_value(&lex->saved_text);
|
||||
lex->value.integer = strtol(saved_text, &end, 10);
|
||||
|
||||
errno = 0;
|
||||
value = json_strtoint(saved_text, &end, 10);
|
||||
if(errno == ERANGE) {
|
||||
if(value < 0)
|
||||
error_set(error, lex, "too big negative integer");
|
||||
else
|
||||
error_set(error, lex, "too big integer");
|
||||
goto out;
|
||||
}
|
||||
|
||||
assert(end == saved_text + lex->saved_text.length);
|
||||
|
||||
return;
|
||||
lex->token = TOKEN_INTEGER;
|
||||
lex->value.integer = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(c == '.') {
|
||||
c = lex_get(lex, error);
|
||||
if(!isdigit(c))
|
||||
if(!isdigit(c)) {
|
||||
lex_unget(lex, c);
|
||||
goto out;
|
||||
}
|
||||
lex_save(lex, c);
|
||||
|
||||
c = lex_get_save(lex, error);
|
||||
@@ -463,41 +523,47 @@ static void lex_scan_number(lex_t *lex, char c, json_error_t *error)
|
||||
}
|
||||
|
||||
lex_unget_unsave(lex, c);
|
||||
lex->token = TOKEN_REAL;
|
||||
|
||||
saved_text = strbuffer_value(&lex->saved_text);
|
||||
lex->value.real = strtod(saved_text, &end);
|
||||
errno = 0;
|
||||
value = strtod(saved_text, &end);
|
||||
assert(end == saved_text + lex->saved_text.length);
|
||||
|
||||
if(errno == ERANGE && value != 0) {
|
||||
error_set(error, lex, "real number overflow");
|
||||
goto out;
|
||||
}
|
||||
|
||||
lex->token = TOKEN_REAL;
|
||||
lex->value.real = value;
|
||||
return 0;
|
||||
|
||||
out:
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int lex_scan(lex_t *lex, json_error_t *error)
|
||||
{
|
||||
char c;
|
||||
int c;
|
||||
|
||||
strbuffer_clear(&lex->saved_text);
|
||||
|
||||
if(lex->token == TOKEN_STRING) {
|
||||
free(lex->value.string);
|
||||
lex->value.string = NULL;
|
||||
jsonp_free(lex->value.string);
|
||||
lex->value.string = NULL;
|
||||
}
|
||||
|
||||
c = lex_get(lex, error);
|
||||
while(c == ' ' || c == '\t' || c == '\n' || c == '\r')
|
||||
{
|
||||
if(c == '\n')
|
||||
lex->line++;
|
||||
|
||||
c = lex_get(lex, error);
|
||||
|
||||
if(c == STREAM_STATE_EOF) {
|
||||
lex->token = TOKEN_EOF;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(c == EOF) {
|
||||
if(lex_eof(lex))
|
||||
lex->token = TOKEN_EOF;
|
||||
else
|
||||
lex->token = TOKEN_INVALID;
|
||||
if(c == STREAM_STATE_ERROR) {
|
||||
lex->token = TOKEN_INVALID;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -509,8 +575,10 @@ static int lex_scan(lex_t *lex, json_error_t *error)
|
||||
else if(c == '"')
|
||||
lex_scan_string(lex, error);
|
||||
|
||||
else if(isdigit(c) || c == '-')
|
||||
lex_scan_number(lex, c, error);
|
||||
else if(isdigit(c) || c == '-') {
|
||||
if(lex_scan_number(lex, c, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
else if(isupper(c) || islower(c)) {
|
||||
/* eat up the whole identifier for clearer error messages */
|
||||
@@ -544,31 +612,40 @@ out:
|
||||
return lex->token;
|
||||
}
|
||||
|
||||
static int lex_init(lex_t *lex, get_func get, eof_func eof, void *data)
|
||||
static char *lex_steal_string(lex_t *lex)
|
||||
{
|
||||
stream_init(&lex->stream, get, eof, data);
|
||||
char *result = NULL;
|
||||
if(lex->token == TOKEN_STRING)
|
||||
{
|
||||
result = lex->value.string;
|
||||
lex->value.string = NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int lex_init(lex_t *lex, get_func get, void *data)
|
||||
{
|
||||
stream_init(&lex->stream, get, data);
|
||||
if(strbuffer_init(&lex->saved_text))
|
||||
return -1;
|
||||
|
||||
lex->token = TOKEN_INVALID;
|
||||
lex->line = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lex_close(lex_t *lex)
|
||||
{
|
||||
if(lex->token == TOKEN_STRING)
|
||||
free(lex->value.string);
|
||||
jsonp_free(lex->value.string);
|
||||
strbuffer_close(&lex->saved_text);
|
||||
}
|
||||
|
||||
|
||||
/*** parser ***/
|
||||
|
||||
static json_t *parse_value(lex_t *lex, json_error_t *error);
|
||||
static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error);
|
||||
|
||||
static json_t *parse_object(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *object = json_object();
|
||||
if(!object)
|
||||
@@ -587,32 +664,40 @@ static json_t *parse_object(lex_t *lex, json_error_t *error)
|
||||
goto error;
|
||||
}
|
||||
|
||||
key = strdup(lex->value.string);
|
||||
key = lex_steal_string(lex);
|
||||
if(!key)
|
||||
return NULL;
|
||||
|
||||
if(flags & JSON_REJECT_DUPLICATES) {
|
||||
if(json_object_get(object, key)) {
|
||||
jsonp_free(key);
|
||||
error_set(error, lex, "duplicate object key");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
lex_scan(lex, error);
|
||||
if(lex->token != ':') {
|
||||
free(key);
|
||||
jsonp_free(key);
|
||||
error_set(error, lex, "':' expected");
|
||||
goto error;
|
||||
}
|
||||
|
||||
lex_scan(lex, error);
|
||||
value = parse_value(lex, error);
|
||||
value = parse_value(lex, flags, error);
|
||||
if(!value) {
|
||||
free(key);
|
||||
jsonp_free(key);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(json_object_set_nocheck(object, key, value)) {
|
||||
free(key);
|
||||
jsonp_free(key);
|
||||
json_decref(value);
|
||||
goto error;
|
||||
}
|
||||
|
||||
json_decref(value);
|
||||
free(key);
|
||||
jsonp_free(key);
|
||||
|
||||
lex_scan(lex, error);
|
||||
if(lex->token != ',')
|
||||
@@ -633,7 +718,7 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static json_t *parse_array(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *array = json_array();
|
||||
if(!array)
|
||||
@@ -644,7 +729,7 @@ static json_t *parse_array(lex_t *lex, json_error_t *error)
|
||||
return array;
|
||||
|
||||
while(lex->token) {
|
||||
json_t *elem = parse_value(lex, error);
|
||||
json_t *elem = parse_value(lex, flags, error);
|
||||
if(!elem)
|
||||
goto error;
|
||||
|
||||
@@ -673,7 +758,7 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static json_t *parse_value(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *json;
|
||||
|
||||
@@ -706,11 +791,11 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
|
||||
break;
|
||||
|
||||
case '{':
|
||||
json = parse_object(lex, error);
|
||||
json = parse_object(lex, flags, error);
|
||||
break;
|
||||
|
||||
case '[':
|
||||
json = parse_array(lex, error);
|
||||
json = parse_array(lex, flags, error);
|
||||
break;
|
||||
|
||||
case TOKEN_INVALID:
|
||||
@@ -728,9 +813,9 @@ static json_t *parse_value(lex_t *lex, json_error_t *error)
|
||||
return json;
|
||||
}
|
||||
|
||||
json_t *parse_json(lex_t *lex, json_error_t *error)
|
||||
static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
|
||||
{
|
||||
error_init(error);
|
||||
json_t *result;
|
||||
|
||||
lex_scan(lex, error);
|
||||
if(lex->token != '[' && lex->token != '{') {
|
||||
@@ -738,7 +823,20 @@ json_t *parse_json(lex_t *lex, json_error_t *error)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return parse_value(lex, error);
|
||||
result = parse_value(lex, flags, error);
|
||||
if(!result)
|
||||
return NULL;
|
||||
|
||||
if(!(flags & JSON_DISABLE_EOF_CHECK)) {
|
||||
lex_scan(lex, error);
|
||||
if(lex->token != TOKEN_EOF) {
|
||||
error_set(error, lex, "end of file expected");
|
||||
json_decref(result);
|
||||
result = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
@@ -757,74 +855,96 @@ static int string_get(void *data)
|
||||
else
|
||||
{
|
||||
stream->pos++;
|
||||
return c;
|
||||
return (unsigned char)c;
|
||||
}
|
||||
}
|
||||
|
||||
static int string_eof(void *data)
|
||||
{
|
||||
string_data_t *stream = (string_data_t *)data;
|
||||
return (stream->data[stream->pos] == '\0');
|
||||
}
|
||||
|
||||
json_t *json_loads(const char *string, json_error_t *error)
|
||||
json_t *json_loads(const char *string, size_t flags, json_error_t *error)
|
||||
{
|
||||
lex_t lex;
|
||||
json_t *result;
|
||||
string_data_t stream_data;
|
||||
|
||||
string_data_t stream_data = {
|
||||
.data = string,
|
||||
.pos = 0
|
||||
};
|
||||
stream_data.data = string;
|
||||
stream_data.pos = 0;
|
||||
|
||||
if(lex_init(&lex, string_get, string_eof, (void *)&stream_data))
|
||||
if(lex_init(&lex, string_get, (void *)&stream_data))
|
||||
return NULL;
|
||||
|
||||
result = parse_json(&lex, error);
|
||||
if(!result)
|
||||
goto out;
|
||||
jsonp_error_init(error, "<string>");
|
||||
result = parse_json(&lex, flags, error);
|
||||
|
||||
lex_scan(&lex, error);
|
||||
if(lex.token != TOKEN_EOF) {
|
||||
error_set(error, &lex, "end of file expected");
|
||||
json_decref(result);
|
||||
result = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
lex_close(&lex);
|
||||
return result;
|
||||
}
|
||||
|
||||
json_t *json_loadf(FILE *input, json_error_t *error)
|
||||
typedef struct
|
||||
{
|
||||
const char *data;
|
||||
size_t len;
|
||||
size_t pos;
|
||||
} buffer_data_t;
|
||||
|
||||
static int buffer_get(void *data)
|
||||
{
|
||||
char c;
|
||||
buffer_data_t *stream = data;
|
||||
if(stream->pos >= stream->len)
|
||||
return EOF;
|
||||
|
||||
c = stream->data[stream->pos];
|
||||
stream->pos++;
|
||||
return (unsigned char)c;
|
||||
}
|
||||
|
||||
json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error)
|
||||
{
|
||||
lex_t lex;
|
||||
json_t *result;
|
||||
buffer_data_t stream_data;
|
||||
|
||||
if(lex_init(&lex, (get_func)fgetc, (eof_func)feof, input))
|
||||
stream_data.data = buffer;
|
||||
stream_data.pos = 0;
|
||||
stream_data.len = buflen;
|
||||
|
||||
if(lex_init(&lex, buffer_get, (void *)&stream_data))
|
||||
return NULL;
|
||||
|
||||
result = parse_json(&lex, error);
|
||||
if(!result)
|
||||
goto out;
|
||||
jsonp_error_init(error, "<buffer>");
|
||||
result = parse_json(&lex, flags, error);
|
||||
|
||||
lex_scan(&lex, error);
|
||||
if(lex.token != TOKEN_EOF) {
|
||||
error_set(error, &lex, "end of file expected");
|
||||
json_decref(result);
|
||||
result = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
lex_close(&lex);
|
||||
return result;
|
||||
}
|
||||
|
||||
json_t *json_load_file(const char *path, json_error_t *error)
|
||||
json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
|
||||
{
|
||||
lex_t lex;
|
||||
const char *source;
|
||||
json_t *result;
|
||||
|
||||
if(lex_init(&lex, (get_func)fgetc, input))
|
||||
return NULL;
|
||||
|
||||
if(input == stdin)
|
||||
source = "<stdin>";
|
||||
else
|
||||
source = "<stream>";
|
||||
|
||||
jsonp_error_init(error, source);
|
||||
result = parse_json(&lex, flags, error);
|
||||
|
||||
lex_close(&lex);
|
||||
return result;
|
||||
}
|
||||
|
||||
json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
|
||||
{
|
||||
json_t *result;
|
||||
FILE *fp;
|
||||
|
||||
jsonp_error_init(error, path);
|
||||
|
||||
fp = fopen(path, "r");
|
||||
if(!fp)
|
||||
{
|
||||
@@ -833,7 +953,7 @@ json_t *json_load_file(const char *path, json_error_t *error)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = json_loadf(fp, error);
|
||||
result = json_loadf(fp, flags, error);
|
||||
|
||||
fclose(fp);
|
||||
return result;
|
||||
|
||||
51
src/memory.c
Normal file
51
src/memory.c
Normal 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
610
src/pack_unpack.c
Normal 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;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -8,8 +8,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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
41
src/utf.c
41
src/utf.c
@@ -1,13 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "utf.h"
|
||||
|
||||
int utf8_encode(int codepoint, char *buffer, int *size)
|
||||
int utf8_encode(int32_t codepoint, char *buffer, int *size)
|
||||
{
|
||||
if(codepoint < 0)
|
||||
return -1;
|
||||
@@ -79,9 +80,10 @@ int utf8_check_first(char byte)
|
||||
}
|
||||
}
|
||||
|
||||
int utf8_check_full(const char *buffer, int size)
|
||||
int utf8_check_full(const char *buffer, int size, int32_t *codepoint)
|
||||
{
|
||||
int i, value = 0;
|
||||
int i;
|
||||
int32_t value = 0;
|
||||
unsigned char u = (unsigned char)buffer[0];
|
||||
|
||||
if(size == 2)
|
||||
@@ -128,9 +130,38 @@ int utf8_check_full(const char *buffer, int size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(codepoint)
|
||||
*codepoint = value;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *utf8_iterate(const char *buffer, int32_t *codepoint)
|
||||
{
|
||||
int count;
|
||||
int32_t value;
|
||||
|
||||
if(!*buffer)
|
||||
return buffer;
|
||||
|
||||
count = utf8_check_first(buffer[0]);
|
||||
if(count <= 0)
|
||||
return NULL;
|
||||
|
||||
if(count == 1)
|
||||
value = (unsigned char)buffer[0];
|
||||
else
|
||||
{
|
||||
if(!utf8_check_full(buffer, count, &value))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(codepoint)
|
||||
*codepoint = value;
|
||||
|
||||
return buffer + count;
|
||||
}
|
||||
|
||||
int utf8_check_string(const char *string, int length)
|
||||
{
|
||||
int i;
|
||||
@@ -148,7 +179,7 @@ int utf8_check_string(const char *string, int length)
|
||||
if(i + count > length)
|
||||
return 0;
|
||||
|
||||
if(!utf8_check_full(&string[i], count))
|
||||
if(!utf8_check_full(&string[i], count, NULL))
|
||||
return 0;
|
||||
|
||||
i += count - 1;
|
||||
|
||||
25
src/utf.h
25
src/utf.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -8,10 +8,31 @@
|
||||
#ifndef UTF_H
|
||||
#define UTF_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
|
||||
#ifdef HAVE_INTTYPES_H
|
||||
/* inttypes.h includes stdint.h in a standard environment, so there's
|
||||
no need to include stdint.h separately. If inttypes.h doesn't define
|
||||
int32_t, it's defined in config.h. */
|
||||
#include <inttypes.h>
|
||||
#endif /* HAVE_INTTYPES_H */
|
||||
|
||||
#else /* !HAVE_CONFIG_H */
|
||||
#ifdef _WIN32
|
||||
typedef int int32_t;
|
||||
#else /* !_WIN32 */
|
||||
/* Assume a standard environment */
|
||||
#include <inttypes.h>
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
int utf8_encode(int codepoint, char *buffer, int *size);
|
||||
|
||||
int utf8_check_first(char byte);
|
||||
int utf8_check_full(const char *buffer, int size);
|
||||
int utf8_check_full(const char *buffer, int size, int32_t *codepoint);
|
||||
const char *utf8_iterate(const char *buffer, int32_t *codepoint);
|
||||
|
||||
int utf8_check_string(const char *string, int length);
|
||||
|
||||
|
||||
13
src/util.h
13
src/util.h
@@ -1,13 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#define max(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
#endif
|
||||
773
src/value.c
773
src/value.c
File diff suppressed because it is too large
Load Diff
22
test/.gitignore
vendored
22
test/.gitignore
vendored
@@ -1,7 +1,15 @@
|
||||
loadf_dumpf
|
||||
loads_dumps
|
||||
load_file_dump_file
|
||||
testlogs
|
||||
testprogs/test_array
|
||||
testprogs/test_number
|
||||
testprogs/test_object
|
||||
logs
|
||||
bin/json_process
|
||||
suites/api/test_array
|
||||
suites/api/test_copy
|
||||
suites/api/test_cpp
|
||||
suites/api/test_dump
|
||||
suites/api/test_equal
|
||||
suites/api/test_load
|
||||
suites/api/test_loadb
|
||||
suites/api/test_memory_funcs
|
||||
suites/api/test_number
|
||||
suites/api/test_object
|
||||
suites/api/test_pack
|
||||
suites/api/test_simple
|
||||
suites/api/test_unpack
|
||||
|
||||
@@ -1,22 +1,10 @@
|
||||
DIST_SUBDIRS = testprogs testdata
|
||||
SUBDIRS = testprogs
|
||||
SUBDIRS = bin suites
|
||||
EXTRA_DIST = scripts run-suites
|
||||
|
||||
check_PROGRAMS = loadf_dumpf loads_dumps load_file_dump_file
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src
|
||||
AM_CFLAGS = -Wall -Werror
|
||||
LDFLAGS = -static # for speed and Valgrind
|
||||
LDADD = ../src/libjansson.la
|
||||
|
||||
TESTS = test-api test-invalid test-valid
|
||||
|
||||
EXTRA_DIST = \
|
||||
test-api \
|
||||
test-invalid \
|
||||
test-valid \
|
||||
run-test \
|
||||
json-compare.py \
|
||||
split-testfile.py
|
||||
TESTS = run-suites
|
||||
TESTS_ENVIRONMENT = \
|
||||
top_srcdir=$(top_srcdir) \
|
||||
top_builddir=$(top_builddir)
|
||||
|
||||
clean-local:
|
||||
rm -rf testlogs
|
||||
rm -rf logs
|
||||
|
||||
6
test/bin/Makefile.am
Normal file
6
test/bin/Makefile.am
Normal file
@@ -0,0 +1,6 @@
|
||||
check_PROGRAMS = json_process
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src
|
||||
AM_CFLAGS = -Wall -Werror
|
||||
LDFLAGS = -static # for speed and Valgrind
|
||||
LDADD = $(top_builddir)/src/libjansson.la
|
||||
124
test/bin/json_process.c
Normal file
124
test/bin/json_process.c
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <jansson.h>
|
||||
|
||||
static int getenv_int(const char *name)
|
||||
{
|
||||
char *value, *end;
|
||||
long result;
|
||||
|
||||
value = getenv(name);
|
||||
if(!value)
|
||||
return 0;
|
||||
|
||||
result = strtol(value, &end, 10);
|
||||
if(*end != '\0')
|
||||
return 0;
|
||||
|
||||
return (int)result;
|
||||
}
|
||||
|
||||
/* Return a pointer to the first non-whitespace character of str.
|
||||
Modifies str so that all trailing whitespace characters are
|
||||
replaced by '\0'. */
|
||||
static const char *strip(char *str)
|
||||
{
|
||||
size_t length;
|
||||
char *result = str;
|
||||
while(*result && isspace(*result))
|
||||
result++;
|
||||
|
||||
length = strlen(result);
|
||||
if(length == 0)
|
||||
return result;
|
||||
|
||||
while(isspace(result[length - 1]))
|
||||
result[--length] = '\0';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int indent = 0;
|
||||
size_t flags = 0;
|
||||
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
|
||||
if(argc != 1) {
|
||||
fprintf(stderr, "usage: %s\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
indent = getenv_int("JSON_INDENT");
|
||||
if(indent < 0 || indent > 255) {
|
||||
fprintf(stderr, "invalid value for JSON_INDENT: %d\n", indent);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if(indent > 0)
|
||||
flags |= JSON_INDENT(indent);
|
||||
|
||||
if(getenv_int("JSON_COMPACT") > 0)
|
||||
flags |= JSON_COMPACT;
|
||||
|
||||
if(getenv_int("JSON_ENSURE_ASCII"))
|
||||
flags |= JSON_ENSURE_ASCII;
|
||||
|
||||
if(getenv_int("JSON_PRESERVE_ORDER"))
|
||||
flags |= JSON_PRESERVE_ORDER;
|
||||
|
||||
if(getenv_int("JSON_SORT_KEYS"))
|
||||
flags |= JSON_SORT_KEYS;
|
||||
|
||||
if(getenv_int("STRIP")) {
|
||||
/* Load to memory, strip leading and trailing whitespace */
|
||||
size_t size = 0, used = 0;
|
||||
char *buffer = NULL;
|
||||
|
||||
while(1) {
|
||||
int count;
|
||||
|
||||
size = (size == 0 ? 128 : size * 2);
|
||||
buffer = realloc(buffer, size);
|
||||
if(!buffer) {
|
||||
fprintf(stderr, "Unable to allocate %d bytes\n", (int)size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
count = fread(buffer + used, 1, size - used, stdin);
|
||||
if(count < size - used) {
|
||||
buffer[used + count] = '\0';
|
||||
break;
|
||||
}
|
||||
used += count;
|
||||
}
|
||||
|
||||
json = json_loads(strip(buffer), 0, &error);
|
||||
free(buffer);
|
||||
}
|
||||
else
|
||||
json = json_loadf(stdin, 0, &error);
|
||||
|
||||
if(!json) {
|
||||
fprintf(stderr, "%d %d %d\n%s\n",
|
||||
error.line, error.column, error.position,
|
||||
error.text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
json_dumpf(json, stdout, flags);
|
||||
json_decref(json);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
|
||||
#
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
import simplejson
|
||||
import sys
|
||||
|
||||
def load(filename):
|
||||
try:
|
||||
jsonfile = open(filename)
|
||||
except IOError, err:
|
||||
print >>sys.stderr, "unable to load %s: %s" % \
|
||||
(filename, err.strerror)
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
json = simplejson.load(jsonfile)
|
||||
except ValueError, err:
|
||||
print "%s is malformed: %s" % (filename, err)
|
||||
sys.exit(1)
|
||||
finally:
|
||||
jsonfile.close()
|
||||
|
||||
return json
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
print >>sys.stderr, "usage: %s json1 json2" % sys.argv[0]
|
||||
return 2
|
||||
|
||||
json1 = load(sys.argv[1])
|
||||
json2 = load(sys.argv[2])
|
||||
if json1 == json2:
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main() or 0)
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <jansson.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
|
||||
if(argc != 3) {
|
||||
fprintf(stderr, "usage: %s infile outfile\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
json = json_load_file(argv[1], &error);
|
||||
if(!json) {
|
||||
fprintf(stderr, "%d\n%s\n", error.line, error.text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
json_dump_file(json, argv[2], 0);
|
||||
json_decref(json);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <jansson.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
|
||||
if(argc != 1) {
|
||||
fprintf(stderr, "usage: %s\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
json = json_loadf(stdin, &error);
|
||||
if(!json) {
|
||||
fprintf(stderr, "%d\n%s\n", error.line, error.text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* loadf_dumpf indents, others don't, so dumping with and without
|
||||
indenting is tested */
|
||||
json_dumpf(json, stdout, JSON_INDENT(4));
|
||||
json_decref(json);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <jansson.h>
|
||||
|
||||
#define BUFFER_SIZE (256 * 1024)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
int count;
|
||||
char buffer[BUFFER_SIZE];
|
||||
char *result;
|
||||
|
||||
if(argc != 1) {
|
||||
fprintf(stderr, "usage: %s\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
count = fread(buffer, 1, BUFFER_SIZE, stdin);
|
||||
if(count < 0 || count >= BUFFER_SIZE) {
|
||||
fprintf(stderr, "unable to read input\n");
|
||||
return 1;
|
||||
}
|
||||
buffer[count] = '\0';
|
||||
|
||||
json = json_loads(buffer, &error);
|
||||
if(!json) {
|
||||
fprintf(stderr, "%d\n%s\n", error.line, error.text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
result = json_dumps(json, 0);
|
||||
json_decref(json);
|
||||
|
||||
puts(result);
|
||||
free(result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
49
test/run-suites
Executable file
49
test/run-suites
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/bin/sh
|
||||
|
||||
while [ -n "$1" ]; do
|
||||
suite=$1
|
||||
if [ -x $top_srcdir/test/suites/$suite/run ]; then
|
||||
SUITES="$SUITES $suite"
|
||||
else
|
||||
echo "No such suite: $suite"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
done
|
||||
|
||||
if [ -z "$SUITES" ]; then
|
||||
suitedirs=$top_srcdir/test/suites/*
|
||||
for suitedir in $suitedirs; do
|
||||
if [ -d $suitedir ]; then
|
||||
SUITES="$SUITES `basename $suitedir`"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
[ -z "$STOP" ] && STOP=0
|
||||
|
||||
export suites_srcdir=$top_srcdir/test/suites
|
||||
export suites_builddir=suites
|
||||
export scriptdir=$top_srcdir/test/scripts
|
||||
export logdir=logs
|
||||
export bindir=bin
|
||||
|
||||
passed=0
|
||||
failed=0
|
||||
for suite in $SUITES; do
|
||||
echo "Suite: $suite"
|
||||
if $suites_srcdir/$suite/run $suite; then
|
||||
passed=$(($passed+1))
|
||||
else
|
||||
failed=$(($failed+1))
|
||||
[ $STOP -eq 1 ] && break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $failed -gt 0 ]; then
|
||||
echo "$failed of $((passed+failed)) test suites failed"
|
||||
exit 1
|
||||
else
|
||||
echo "$passed test suites passed"
|
||||
rm -rf $logdir
|
||||
fi
|
||||
@@ -1,54 +0,0 @@
|
||||
# Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
|
||||
#
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
VALGRIND_CMDLINE="valgrind --leak-check=full --show-reachable=yes --track-origins=yes -q"
|
||||
|
||||
run_testprog() {
|
||||
local prog=$1
|
||||
local prefix=$2
|
||||
if [ -n "$VALGRIND" ]; then
|
||||
local runner="$VALGRIND_CMDLINE "
|
||||
fi
|
||||
|
||||
case "$prog" in
|
||||
load_file_dump_file)
|
||||
$runner./$prog \
|
||||
$prefix.in \
|
||||
$prefix.$prog.stdout \
|
||||
2>$prefix.$prog.stderr
|
||||
;;
|
||||
*)
|
||||
$runner./$prog \
|
||||
<$prefix.in \
|
||||
>$prefix.$prog.stdout \
|
||||
2>$prefix.$prog.stderr
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -n "$VALGRIND" ]; then
|
||||
# Check for Valgrind error output. The valgrind option
|
||||
# --error-exitcode is not enough because Valgrind doesn't
|
||||
# think unfreed allocs are errors.
|
||||
if grep -E -q '^==[0-9]+== ' $prefix.$prog.stderr; then
|
||||
echo "### $prefix ($prog) failed:" >&2
|
||||
echo "valgrind detected an error" >&2
|
||||
echo "for details, see test/$prefix.$prog.stderr" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
for testfile in $TESTFILES; do
|
||||
tmpdir="testlogs/`basename $testfile`"
|
||||
rm -rf $tmpdir
|
||||
mkdir -p $tmpdir
|
||||
${srcdir}/split-testfile.py $testfile $tmpdir | while read name; do
|
||||
run_test loadf_dumpf $tmpdir/$name
|
||||
run_test loads_dumps $tmpdir/$name
|
||||
run_test load_file_dump_file $tmpdir/$name
|
||||
echo -n '.'
|
||||
done || exit 1
|
||||
echo
|
||||
done
|
||||
89
test/scripts/run-tests.sh
Normal file
89
test/scripts/run-tests.sh
Normal file
@@ -0,0 +1,89 @@
|
||||
# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
#
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
json_process=$bindir/json_process
|
||||
|
||||
suite_name=$1
|
||||
suite_srcdir=$suites_srcdir/$suite_name
|
||||
suite_builddir=$suites_builddir/$suite_name
|
||||
suite_log=$logdir/$suite_name
|
||||
|
||||
|
||||
[ -z "$VERBOSE" ] && VERBOSE=0
|
||||
[ -z "$STOP" ] && STOP=0
|
||||
|
||||
. $scriptdir/valgrind.sh
|
||||
|
||||
rm -rf $suite_log
|
||||
mkdir -p $suite_log
|
||||
|
||||
for test_path in $suite_srcdir/*; do
|
||||
test_name=$(basename $test_path)
|
||||
test_builddir=$suite_builddir/$test_name
|
||||
test_log=$suite_log/$test_name
|
||||
|
||||
[ "$test_name" = "run" ] && continue
|
||||
is_test || continue
|
||||
|
||||
rm -rf $test_log
|
||||
mkdir -p $test_log
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
printf '%s... ' "$test_name"
|
||||
fi
|
||||
|
||||
run_test
|
||||
case $? in
|
||||
0)
|
||||
# Success
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
printf 'ok\n'
|
||||
else
|
||||
printf '.'
|
||||
fi
|
||||
rm -rf $test_log
|
||||
;;
|
||||
|
||||
77)
|
||||
# Skip
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
printf 'skipped\n'
|
||||
else
|
||||
printf 'S'
|
||||
fi
|
||||
rm -rf $test_log
|
||||
;;
|
||||
|
||||
*)
|
||||
# Failure
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
printf 'FAILED\n'
|
||||
else
|
||||
printf 'F'
|
||||
fi
|
||||
|
||||
[ $STOP -eq 1 ] && break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ $VERBOSE -eq 0 ]; then
|
||||
printf '\n'
|
||||
fi
|
||||
|
||||
if [ -n "$(ls -A $suite_log)" ]; then
|
||||
for test_log in $suite_log/*; do
|
||||
test_name=$(basename $test_log)
|
||||
test_path=$suite_srcdir/$test_name
|
||||
echo "================================================================="
|
||||
echo "$suite_name/$test_name"
|
||||
echo "================================================================="
|
||||
show_error
|
||||
echo
|
||||
done
|
||||
echo "================================================================="
|
||||
exit 1
|
||||
else
|
||||
rm -rf $suite_log
|
||||
fi
|
||||
35
test/scripts/valgrind.sh
Normal file
35
test/scripts/valgrind.sh
Normal file
@@ -0,0 +1,35 @@
|
||||
# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
#
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
[ -z "$VALGRIND" ] && VALGRIND=0
|
||||
|
||||
VALGRIND_CMDLINE="valgrind --leak-check=full --show-reachable=yes --track-origins=yes -q"
|
||||
|
||||
if [ $VALGRIND -eq 1 ]; then
|
||||
test_runner="$VALGRIND_CMDLINE"
|
||||
json_process="$VALGRIND_CMDLINE $json_process"
|
||||
else
|
||||
test_runner=""
|
||||
fi
|
||||
|
||||
valgrind_check() {
|
||||
if [ $VALGRIND -eq 1 ]; then
|
||||
# Check for Valgrind error output. The valgrind option
|
||||
# --error-exitcode is not enough because Valgrind doesn't
|
||||
# think unfreed allocs are errors.
|
||||
if grep -E -q '^==[0-9]+== ' $1; then
|
||||
touch $test_log/valgrind_error
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
valgrind_show_error() {
|
||||
if [ $VALGRIND -eq 1 -a -f $test_log/valgrind_error ]; then
|
||||
echo "valgrind detected an error"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
|
||||
#
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
def open_files(outdir, i, name):
|
||||
basename = '%02d_%s' % (i, name)
|
||||
print basename
|
||||
input_path = os.path.join(outdir, basename + '.in')
|
||||
output_path = os.path.join(outdir, basename + '.out')
|
||||
return open(input_path, 'w'), open(output_path, 'w')
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
print 'usage: %s input-file output-directory' % sys.argv[0]
|
||||
return 2
|
||||
|
||||
infile = os.path.normpath(sys.argv[1])
|
||||
outdir = os.path.normpath(sys.argv[2])
|
||||
|
||||
if not os.path.exists(outdir):
|
||||
print >>sys.stderr, 'output directory %r does not exist!' % outdir
|
||||
return 1
|
||||
|
||||
n = 0
|
||||
current = None
|
||||
input, output = None, None
|
||||
|
||||
for line in open(infile):
|
||||
if line.startswith('==== '):
|
||||
n += 1
|
||||
if input is not None and output is not None:
|
||||
input.close()
|
||||
output.close()
|
||||
input, output = open_files(outdir, n, line[5:line.find(' ====\n')])
|
||||
current = input
|
||||
elif line == '====\n':
|
||||
current = output
|
||||
else:
|
||||
current.write(line)
|
||||
|
||||
if input is not None and output is not None:
|
||||
input.close()
|
||||
output.close()
|
||||
|
||||
print >>sys.stderr, "%s: %d test cases" % (infile, n)
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main() or 0)
|
||||
@@ -1 +1,2 @@
|
||||
SUBDIRS = api
|
||||
EXTRA_DIST = invalid invalid-unicode valid
|
||||
32
test/suites/api/Makefile.am
Normal file
32
test/suites/api/Makefile.am
Normal file
@@ -0,0 +1,32 @@
|
||||
EXTRA_DIST = run
|
||||
|
||||
check_PROGRAMS = \
|
||||
test_array \
|
||||
test_copy \
|
||||
test_dump \
|
||||
test_equal \
|
||||
test_load \
|
||||
test_loadb \
|
||||
test_memory_funcs \
|
||||
test_number \
|
||||
test_object \
|
||||
test_pack \
|
||||
test_simple \
|
||||
test_unpack
|
||||
|
||||
test_array_SOURCES = test_array.c util.h
|
||||
test_copy_SOURCES = test_copy.c util.h
|
||||
test_dump_SOURCES = test_dump.c util.h
|
||||
test_load_SOURCES = test_load.c util.h
|
||||
test_loadb_SOURCES = test_loadb.c util.h
|
||||
test_memory_funcs_SOURCES = test_memory_funcs.c util.h
|
||||
test_number_SOURCES = test_number.c util.h
|
||||
test_object_SOURCES = test_object.c util.h
|
||||
test_pack_SOURCES = test_pack.c util.h
|
||||
test_simple_SOURCES = test_simple.c util.h
|
||||
test_unpack_SOURCES = test_unpack.c util.h
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src
|
||||
AM_CFLAGS = -Wall -Werror
|
||||
LDFLAGS = -static # for speed and Valgrind
|
||||
LDADD = $(top_builddir)/src/libjansson.la
|
||||
101
test/suites/api/check-exports
Executable file
101
test/suites/api/check-exports
Executable file
@@ -0,0 +1,101 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This tests checks that the libjansson.so exports the correct
|
||||
# symbols.
|
||||
|
||||
# The list of symbols that the shared object should export
|
||||
sort >$test_log/exports <<EOF
|
||||
json_delete
|
||||
json_true
|
||||
json_false
|
||||
json_null
|
||||
json_string
|
||||
json_string_nocheck
|
||||
json_string_value
|
||||
json_string_set
|
||||
json_string_set_nocheck
|
||||
json_integer
|
||||
json_integer_value
|
||||
json_integer_set
|
||||
json_real
|
||||
json_real_value
|
||||
json_real_set
|
||||
json_number_value
|
||||
json_array
|
||||
json_array_size
|
||||
json_array_get
|
||||
json_array_set_new
|
||||
json_array_append_new
|
||||
json_array_insert_new
|
||||
json_array_remove
|
||||
json_array_clear
|
||||
json_array_extend
|
||||
json_object
|
||||
json_object_size
|
||||
json_object_get
|
||||
json_object_set_new
|
||||
json_object_set_new_nocheck
|
||||
json_object_del
|
||||
json_object_clear
|
||||
json_object_update
|
||||
json_object_iter
|
||||
json_object_iter_at
|
||||
json_object_iter_next
|
||||
json_object_iter_key
|
||||
json_object_iter_value
|
||||
json_object_iter_set_new
|
||||
json_dumps
|
||||
json_dumpf
|
||||
json_dump_file
|
||||
json_loads
|
||||
json_loadf
|
||||
json_load_file
|
||||
json_loadb
|
||||
json_equal
|
||||
json_copy
|
||||
json_deep_copy
|
||||
json_pack
|
||||
json_pack_ex
|
||||
json_vpack_ex
|
||||
json_unpack
|
||||
json_unpack_ex
|
||||
json_vunpack_ex
|
||||
json_set_alloc_funcs
|
||||
EOF
|
||||
|
||||
# The list of functions are not exported in the library because they
|
||||
# are macros or static inline functions. This is only the make the
|
||||
# list complete, there are not used by the test.
|
||||
sort >$test_log/macros_or_inline <<EOF
|
||||
json_typeof
|
||||
json_incref
|
||||
json_decref
|
||||
json_is_object
|
||||
json_is_object
|
||||
json_is_array
|
||||
json_is_string
|
||||
json_is_integer
|
||||
json_is_real
|
||||
json_is_true
|
||||
json_is_false
|
||||
json_is_null
|
||||
json_is_number
|
||||
json_is_boolean
|
||||
json_array_set
|
||||
json_array_append
|
||||
json_array_insert
|
||||
json_object_set
|
||||
json_object_set_nocheck
|
||||
EOF
|
||||
|
||||
SOFILE="../src/.libs/libjansson.so"
|
||||
|
||||
nm -D $SOFILE >/dev/null >$test_log/symbols 2>/dev/null \
|
||||
|| exit 77 # Skip if "nm -D" doesn't seem to work
|
||||
|
||||
grep ' T ' $test_log/symbols | cut -d' ' -f3 | sort >$test_log/output
|
||||
|
||||
if ! cmp -s $test_log/exports $test_log/output; then
|
||||
diff -u $test_log/exports $test_log/output >&2
|
||||
exit 1
|
||||
fi
|
||||
36
test/suites/api/run
Executable file
36
test/suites/api/run
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
#
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
is_test() {
|
||||
case "$test_name" in
|
||||
*.c|check-exports)
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
run_test() {
|
||||
if [ "$test_name" = "check-exports" ]; then
|
||||
test_log=$test_log $test_path >$test_log/stdout 2>$test_log/stderr
|
||||
else
|
||||
$test_runner $suite_builddir/${test_name%.c} \
|
||||
>$test_log/stdout \
|
||||
2>$test_log/stderr \
|
||||
|| return 1
|
||||
valgrind_check $test_log/stderr || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
show_error() {
|
||||
valgrind_show_error && return
|
||||
cat $test_log/stderr
|
||||
}
|
||||
|
||||
. $top_srcdir/test/scripts/run-tests.sh
|
||||
400
test/suites/api/test_array.c
Normal file
400
test/suites/api/test_array.c
Normal file
@@ -0,0 +1,400 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <jansson.h>
|
||||
#include "util.h"
|
||||
|
||||
static void test_misc(void)
|
||||
{
|
||||
json_t *array, *five, *seven, *value;
|
||||
int i;
|
||||
|
||||
array = json_array();
|
||||
five = json_integer(5);
|
||||
seven = json_integer(7);
|
||||
|
||||
if(!array)
|
||||
fail("unable to create array");
|
||||
if(!five || !seven)
|
||||
fail("unable to create integer");
|
||||
|
||||
if(json_array_size(array) != 0)
|
||||
fail("empty array has nonzero size");
|
||||
|
||||
if(!json_array_append(array, NULL))
|
||||
fail("able to append NULL");
|
||||
|
||||
if(json_array_append(array, five))
|
||||
fail("unable to append");
|
||||
|
||||
if(json_array_size(array) != 1)
|
||||
fail("wrong array size");
|
||||
|
||||
value = json_array_get(array, 0);
|
||||
if(!value)
|
||||
fail("unable to get item");
|
||||
if(value != five)
|
||||
fail("got wrong value");
|
||||
|
||||
if(json_array_append(array, seven))
|
||||
fail("unable to append value");
|
||||
|
||||
if(json_array_size(array) != 2)
|
||||
fail("wrong array size");
|
||||
|
||||
value = json_array_get(array, 1);
|
||||
if(!value)
|
||||
fail("unable to get item");
|
||||
if(value != seven)
|
||||
fail("got wrong value");
|
||||
|
||||
if(json_array_set(array, 0, seven))
|
||||
fail("unable to set value");
|
||||
|
||||
if(!json_array_set(array, 0, NULL))
|
||||
fail("able to set NULL");
|
||||
|
||||
if(json_array_size(array) != 2)
|
||||
fail("wrong array size");
|
||||
|
||||
value = json_array_get(array, 0);
|
||||
if(!value)
|
||||
fail("unable to get item");
|
||||
if(value != seven)
|
||||
fail("got wrong value");
|
||||
|
||||
if(json_array_get(array, 2) != NULL)
|
||||
fail("able to get value out of bounds");
|
||||
|
||||
if(!json_array_set(array, 2, seven))
|
||||
fail("able to set value out of bounds");
|
||||
|
||||
for(i = 2; i < 30; i++) {
|
||||
if(json_array_append(array, seven))
|
||||
fail("unable to append value");
|
||||
|
||||
if(json_array_size(array) != i + 1)
|
||||
fail("wrong array size");
|
||||
}
|
||||
|
||||
for(i = 0; i < 30; i++) {
|
||||
value = json_array_get(array, i);
|
||||
if(!value)
|
||||
fail("unable to get item");
|
||||
if(value != seven)
|
||||
fail("got wrong value");
|
||||
}
|
||||
|
||||
if(json_array_set_new(array, 15, json_integer(123)))
|
||||
fail("unable to set new value");
|
||||
|
||||
value = json_array_get(array, 15);
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 123)
|
||||
fail("json_array_set_new works incorrectly");
|
||||
|
||||
if(!json_array_set_new(array, 15, NULL))
|
||||
fail("able to set_new NULL value");
|
||||
|
||||
if(json_array_append_new(array, json_integer(321)))
|
||||
fail("unable to append new value");
|
||||
|
||||
value = json_array_get(array, json_array_size(array) - 1);
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 321)
|
||||
fail("json_array_append_new works incorrectly");
|
||||
|
||||
if(!json_array_append_new(array, NULL))
|
||||
fail("able to append_new NULL value");
|
||||
|
||||
json_decref(five);
|
||||
json_decref(seven);
|
||||
json_decref(array);
|
||||
}
|
||||
|
||||
static void test_insert(void)
|
||||
{
|
||||
json_t *array, *five, *seven, *eleven, *value;
|
||||
int i;
|
||||
|
||||
array = json_array();
|
||||
five = json_integer(5);
|
||||
seven = json_integer(7);
|
||||
eleven = json_integer(11);
|
||||
|
||||
if(!array)
|
||||
fail("unable to create array");
|
||||
if(!five || !seven || !eleven)
|
||||
fail("unable to create integer");
|
||||
|
||||
|
||||
if(!json_array_insert(array, 1, five))
|
||||
fail("able to insert value out of bounds");
|
||||
|
||||
|
||||
if(json_array_insert(array, 0, five))
|
||||
fail("unable to insert value in an empty array");
|
||||
|
||||
if(json_array_get(array, 0) != five)
|
||||
fail("json_array_insert works incorrectly");
|
||||
|
||||
if(json_array_size(array) != 1)
|
||||
fail("array size is invalid after insertion");
|
||||
|
||||
|
||||
if(json_array_insert(array, 1, seven))
|
||||
fail("unable to insert value at the end of an array");
|
||||
|
||||
if(json_array_get(array, 0) != five)
|
||||
fail("json_array_insert works incorrectly");
|
||||
|
||||
if(json_array_get(array, 1) != seven)
|
||||
fail("json_array_insert works incorrectly");
|
||||
|
||||
if(json_array_size(array) != 2)
|
||||
fail("array size is invalid after insertion");
|
||||
|
||||
|
||||
if(json_array_insert(array, 1, eleven))
|
||||
fail("unable to insert value in the middle of an array");
|
||||
|
||||
if(json_array_get(array, 0) != five)
|
||||
fail("json_array_insert works incorrectly");
|
||||
|
||||
if(json_array_get(array, 1) != eleven)
|
||||
fail("json_array_insert works incorrectly");
|
||||
|
||||
if(json_array_get(array, 2) != seven)
|
||||
fail("json_array_insert works incorrectly");
|
||||
|
||||
if(json_array_size(array) != 3)
|
||||
fail("array size is invalid after insertion");
|
||||
|
||||
|
||||
if(json_array_insert_new(array, 2, json_integer(123)))
|
||||
fail("unable to insert value in the middle of an array");
|
||||
|
||||
value = json_array_get(array, 2);
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 123)
|
||||
fail("json_array_insert_new works incorrectly");
|
||||
|
||||
if(json_array_size(array) != 4)
|
||||
fail("array size is invalid after insertion");
|
||||
|
||||
|
||||
for(i = 0; i < 20; i++) {
|
||||
if(json_array_insert(array, 0, seven))
|
||||
fail("unable to insert value at the begining of an array");
|
||||
}
|
||||
|
||||
for(i = 0; i < 20; i++) {
|
||||
if(json_array_get(array, i) != seven)
|
||||
fail("json_aray_insert works incorrectly");
|
||||
}
|
||||
|
||||
if(json_array_size(array) != 24)
|
||||
fail("array size is invalid after loop insertion");
|
||||
|
||||
json_decref(five);
|
||||
json_decref(seven);
|
||||
json_decref(eleven);
|
||||
json_decref(array);
|
||||
}
|
||||
|
||||
static void test_remove(void)
|
||||
{
|
||||
json_t *array, *five, *seven;
|
||||
|
||||
array = json_array();
|
||||
five = json_integer(5);
|
||||
seven = json_integer(7);
|
||||
|
||||
if(!array)
|
||||
fail("unable to create array");
|
||||
if(!five)
|
||||
fail("unable to create integer");
|
||||
if(!seven)
|
||||
fail("unable to create integer");
|
||||
|
||||
|
||||
if(!json_array_remove(array, 0))
|
||||
fail("able to remove an unexisting index");
|
||||
|
||||
|
||||
if(json_array_append(array, five))
|
||||
fail("unable to append");
|
||||
|
||||
if(!json_array_remove(array, 1))
|
||||
fail("able to remove an unexisting index");
|
||||
|
||||
if(json_array_remove(array, 0))
|
||||
fail("unable to remove");
|
||||
|
||||
if(json_array_size(array) != 0)
|
||||
fail("array size is invalid after removing");
|
||||
|
||||
|
||||
if(json_array_append(array, five) ||
|
||||
json_array_append(array, seven) ||
|
||||
json_array_append(array, five) ||
|
||||
json_array_append(array, seven))
|
||||
fail("unable to append");
|
||||
|
||||
if(json_array_remove(array, 2))
|
||||
fail("unable to remove");
|
||||
|
||||
if(json_array_size(array) != 3)
|
||||
fail("array size is invalid after removing");
|
||||
|
||||
if(json_array_get(array, 0) != five ||
|
||||
json_array_get(array, 1) != seven ||
|
||||
json_array_get(array, 2) != seven)
|
||||
fail("remove works incorrectly");
|
||||
|
||||
json_decref(five);
|
||||
json_decref(seven);
|
||||
json_decref(array);
|
||||
}
|
||||
|
||||
static void test_clear(void)
|
||||
{
|
||||
json_t *array, *five, *seven;
|
||||
int i;
|
||||
|
||||
array = json_array();
|
||||
five = json_integer(5);
|
||||
seven = json_integer(7);
|
||||
|
||||
if(!array)
|
||||
fail("unable to create array");
|
||||
if(!five || !seven)
|
||||
fail("unable to create integer");
|
||||
|
||||
for(i = 0; i < 10; i++) {
|
||||
if(json_array_append(array, five))
|
||||
fail("unable to append");
|
||||
}
|
||||
for(i = 0; i < 10; i++) {
|
||||
if(json_array_append(array, seven))
|
||||
fail("unable to append");
|
||||
}
|
||||
|
||||
if(json_array_size(array) != 20)
|
||||
fail("array size is invalid after appending");
|
||||
|
||||
if(json_array_clear(array))
|
||||
fail("unable to clear");
|
||||
|
||||
if(json_array_size(array) != 0)
|
||||
fail("array size is invalid after clearing");
|
||||
|
||||
json_decref(five);
|
||||
json_decref(seven);
|
||||
json_decref(array);
|
||||
}
|
||||
|
||||
static void test_extend(void)
|
||||
{
|
||||
json_t *array1, *array2, *five, *seven;
|
||||
int i;
|
||||
|
||||
array1 = json_array();
|
||||
array2 = json_array();
|
||||
five = json_integer(5);
|
||||
seven = json_integer(7);
|
||||
|
||||
if(!array1 || !array2)
|
||||
fail("unable to create array");
|
||||
if(!five || !seven)
|
||||
fail("unable to create integer");
|
||||
|
||||
for(i = 0; i < 10; i++) {
|
||||
if(json_array_append(array1, five))
|
||||
fail("unable to append");
|
||||
}
|
||||
for(i = 0; i < 10; i++) {
|
||||
if(json_array_append(array2, seven))
|
||||
fail("unable to append");
|
||||
}
|
||||
|
||||
if(json_array_size(array1) != 10 || json_array_size(array2) != 10)
|
||||
fail("array size is invalid after appending");
|
||||
|
||||
if(json_array_extend(array1, array2))
|
||||
fail("unable to extend");
|
||||
|
||||
for(i = 0; i < 10; i++) {
|
||||
if(json_array_get(array1, i) != five)
|
||||
fail("invalid array contents after extending");
|
||||
}
|
||||
for(i = 10; i < 20; i++) {
|
||||
if(json_array_get(array1, i) != seven)
|
||||
fail("invalid array contents after extending");
|
||||
}
|
||||
|
||||
json_decref(five);
|
||||
json_decref(seven);
|
||||
json_decref(array1);
|
||||
json_decref(array2);
|
||||
}
|
||||
|
||||
static void test_circular()
|
||||
{
|
||||
json_t *array1, *array2;
|
||||
|
||||
/* the simple cases are checked */
|
||||
|
||||
array1 = json_array();
|
||||
if(!array1)
|
||||
fail("unable to create array");
|
||||
|
||||
if(json_array_append(array1, array1) == 0)
|
||||
fail("able to append self");
|
||||
|
||||
if(json_array_insert(array1, 0, array1) == 0)
|
||||
fail("able to insert self");
|
||||
|
||||
if(json_array_append_new(array1, json_true()))
|
||||
fail("failed to append true");
|
||||
|
||||
if(json_array_set(array1, 0, array1) == 0)
|
||||
fail("able to set self");
|
||||
|
||||
json_decref(array1);
|
||||
|
||||
|
||||
/* create circular references */
|
||||
|
||||
array1 = json_array();
|
||||
array2 = json_array();
|
||||
if(!array1 || !array2)
|
||||
fail("unable to create array");
|
||||
|
||||
if(json_array_append(array1, array2) ||
|
||||
json_array_append(array2, array1))
|
||||
fail("unable to append");
|
||||
|
||||
/* circularity is detected when dumping */
|
||||
if(json_dumps(array1, 0) != NULL)
|
||||
fail("able to dump circulars");
|
||||
|
||||
/* decref twice to deal with the circular references */
|
||||
json_decref(array1);
|
||||
json_decref(array2);
|
||||
json_decref(array1);
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
test_misc();
|
||||
test_insert();
|
||||
test_remove();
|
||||
test_clear();
|
||||
test_extend();
|
||||
test_circular();
|
||||
|
||||
return 0;
|
||||
}
|
||||
319
test/suites/api/test_copy.c
Normal file
319
test/suites/api/test_copy.c
Normal file
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <jansson.h>
|
||||
#include "util.h"
|
||||
|
||||
static void test_copy_simple(void)
|
||||
{
|
||||
json_t *value, *copy;
|
||||
|
||||
if(json_copy(NULL))
|
||||
fail("copying NULL doesn't return NULL");
|
||||
|
||||
/* true */
|
||||
value = json_true();
|
||||
copy = json_copy(value);
|
||||
if(value != copy)
|
||||
fail("copying true failed");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
|
||||
/* false */
|
||||
value = json_false();
|
||||
copy = json_copy(value);
|
||||
if(value != copy)
|
||||
fail("copying false failed");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
|
||||
/* null */
|
||||
value = json_null();
|
||||
copy = json_copy(value);
|
||||
if(value != copy)
|
||||
fail("copying null failed");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
|
||||
/* string */
|
||||
value = json_string("foo");
|
||||
if(!value)
|
||||
fail("unable to create a string");
|
||||
copy = json_copy(value);
|
||||
if(!copy)
|
||||
fail("unable to copy a string");
|
||||
if(copy == value)
|
||||
fail("copying a string doesn't copy");
|
||||
if(!json_equal(copy, value))
|
||||
fail("copying a string produces an inequal copy");
|
||||
if(value->refcount != 1 || copy->refcount != 1)
|
||||
fail("invalid refcounts");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
|
||||
/* integer */
|
||||
value = json_integer(543);
|
||||
if(!value)
|
||||
fail("unable to create an integer");
|
||||
copy = json_copy(value);
|
||||
if(!copy)
|
||||
fail("unable to copy an integer");
|
||||
if(copy == value)
|
||||
fail("copying an integer doesn't copy");
|
||||
if(!json_equal(copy, value))
|
||||
fail("copying an integer produces an inequal copy");
|
||||
if(value->refcount != 1 || copy->refcount != 1)
|
||||
fail("invalid refcounts");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
|
||||
/* real */
|
||||
value = json_real(123e9);
|
||||
if(!value)
|
||||
fail("unable to create a real");
|
||||
copy = json_copy(value);
|
||||
if(!copy)
|
||||
fail("unable to copy a real");
|
||||
if(copy == value)
|
||||
fail("copying a real doesn't copy");
|
||||
if(!json_equal(copy, value))
|
||||
fail("copying a real produces an inequal copy");
|
||||
if(value->refcount != 1 || copy->refcount != 1)
|
||||
fail("invalid refcounts");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
}
|
||||
|
||||
static void test_deep_copy_simple(void)
|
||||
{
|
||||
json_t *value, *copy;
|
||||
|
||||
if(json_deep_copy(NULL))
|
||||
fail("deep copying NULL doesn't return NULL");
|
||||
|
||||
/* true */
|
||||
value = json_true();
|
||||
copy = json_deep_copy(value);
|
||||
if(value != copy)
|
||||
fail("deep copying true failed");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
|
||||
/* false */
|
||||
value = json_false();
|
||||
copy = json_deep_copy(value);
|
||||
if(value != copy)
|
||||
fail("deep copying false failed");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
|
||||
/* null */
|
||||
value = json_null();
|
||||
copy = json_deep_copy(value);
|
||||
if(value != copy)
|
||||
fail("deep copying null failed");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
|
||||
/* string */
|
||||
value = json_string("foo");
|
||||
if(!value)
|
||||
fail("unable to create a string");
|
||||
copy = json_deep_copy(value);
|
||||
if(!copy)
|
||||
fail("unable to deep copy a string");
|
||||
if(copy == value)
|
||||
fail("deep copying a string doesn't copy");
|
||||
if(!json_equal(copy, value))
|
||||
fail("deep copying a string produces an inequal copy");
|
||||
if(value->refcount != 1 || copy->refcount != 1)
|
||||
fail("invalid refcounts");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
|
||||
/* integer */
|
||||
value = json_integer(543);
|
||||
if(!value)
|
||||
fail("unable to create an integer");
|
||||
copy = json_deep_copy(value);
|
||||
if(!copy)
|
||||
fail("unable to deep copy an integer");
|
||||
if(copy == value)
|
||||
fail("deep copying an integer doesn't copy");
|
||||
if(!json_equal(copy, value))
|
||||
fail("deep copying an integer produces an inequal copy");
|
||||
if(value->refcount != 1 || copy->refcount != 1)
|
||||
fail("invalid refcounts");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
|
||||
/* real */
|
||||
value = json_real(123e9);
|
||||
if(!value)
|
||||
fail("unable to create a real");
|
||||
copy = json_deep_copy(value);
|
||||
if(!copy)
|
||||
fail("unable to deep copy a real");
|
||||
if(copy == value)
|
||||
fail("deep copying a real doesn't copy");
|
||||
if(!json_equal(copy, value))
|
||||
fail("deep copying a real produces an inequal copy");
|
||||
if(value->refcount != 1 || copy->refcount != 1)
|
||||
fail("invalid refcounts");
|
||||
json_decref(value);
|
||||
json_decref(copy);
|
||||
}
|
||||
|
||||
static void test_copy_array(void)
|
||||
{
|
||||
const char *json_array_text = "[1, \"foo\", 3.141592, {\"foo\": \"bar\"}]";
|
||||
|
||||
json_t *array, *copy;
|
||||
size_t i;
|
||||
|
||||
array = json_loads(json_array_text, 0, NULL);
|
||||
if(!array)
|
||||
fail("unable to parse an array");
|
||||
|
||||
copy = json_copy(array);
|
||||
if(!copy)
|
||||
fail("unable to copy an array");
|
||||
if(copy == array)
|
||||
fail("copying an array doesn't copy");
|
||||
if(!json_equal(copy, array))
|
||||
fail("copying an array produces an inequal copy");
|
||||
|
||||
for(i = 0; i < json_array_size(copy); i++)
|
||||
{
|
||||
if(json_array_get(array, i) != json_array_get(copy, i))
|
||||
fail("copying an array modifies its elements");
|
||||
}
|
||||
|
||||
json_decref(array);
|
||||
json_decref(copy);
|
||||
}
|
||||
|
||||
static void test_deep_copy_array(void)
|
||||
{
|
||||
const char *json_array_text = "[1, \"foo\", 3.141592, {\"foo\": \"bar\"}]";
|
||||
|
||||
json_t *array, *copy;
|
||||
size_t i;
|
||||
|
||||
array = json_loads(json_array_text, 0, NULL);
|
||||
if(!array)
|
||||
fail("unable to parse an array");
|
||||
|
||||
copy = json_deep_copy(array);
|
||||
if(!copy)
|
||||
fail("unable to deep copy an array");
|
||||
if(copy == array)
|
||||
fail("deep copying an array doesn't copy");
|
||||
if(!json_equal(copy, array))
|
||||
fail("deep copying an array produces an inequal copy");
|
||||
|
||||
for(i = 0; i < json_array_size(copy); i++)
|
||||
{
|
||||
if(json_array_get(array, i) == json_array_get(copy, i))
|
||||
fail("deep copying an array doesn't copy its elements");
|
||||
}
|
||||
|
||||
json_decref(array);
|
||||
json_decref(copy);
|
||||
}
|
||||
|
||||
static void test_copy_object(void)
|
||||
{
|
||||
const char *json_object_text =
|
||||
"{\"foo\": \"bar\", \"a\": 1, \"b\": 3.141592, \"c\": [1,2,3,4]}";
|
||||
|
||||
json_t *object, *copy;
|
||||
void *iter;
|
||||
|
||||
object = json_loads(json_object_text, 0, NULL);
|
||||
if(!object)
|
||||
fail("unable to parse an object");
|
||||
|
||||
copy = json_copy(object);
|
||||
if(!copy)
|
||||
fail("unable to copy an object");
|
||||
if(copy == object)
|
||||
fail("copying an object doesn't copy");
|
||||
if(!json_equal(copy, object))
|
||||
fail("copying an object produces an inequal copy");
|
||||
|
||||
iter = json_object_iter(object);
|
||||
while(iter)
|
||||
{
|
||||
const char *key;
|
||||
json_t *value1, *value2;
|
||||
|
||||
key = json_object_iter_key(iter);
|
||||
value1 = json_object_iter_value(iter);
|
||||
value2 = json_object_get(copy, key);
|
||||
|
||||
if(value1 != value2)
|
||||
fail("deep copying an object modifies its items");
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
}
|
||||
|
||||
json_decref(object);
|
||||
json_decref(copy);
|
||||
}
|
||||
|
||||
static void test_deep_copy_object(void)
|
||||
{
|
||||
const char *json_object_text =
|
||||
"{\"foo\": \"bar\", \"a\": 1, \"b\": 3.141592, \"c\": [1,2,3,4]}";
|
||||
|
||||
json_t *object, *copy;
|
||||
void *iter;
|
||||
|
||||
object = json_loads(json_object_text, 0, NULL);
|
||||
if(!object)
|
||||
fail("unable to parse an object");
|
||||
|
||||
copy = json_deep_copy(object);
|
||||
if(!copy)
|
||||
fail("unable to deep copy an object");
|
||||
if(copy == object)
|
||||
fail("deep copying an object doesn't copy");
|
||||
if(!json_equal(copy, object))
|
||||
fail("deep copying an object produces an inequal copy");
|
||||
|
||||
iter = json_object_iter(object);
|
||||
while(iter)
|
||||
{
|
||||
const char *key;
|
||||
json_t *value1, *value2;
|
||||
|
||||
key = json_object_iter_key(iter);
|
||||
value1 = json_object_iter_value(iter);
|
||||
value2 = json_object_get(copy, key);
|
||||
|
||||
if(value1 == value2)
|
||||
fail("deep copying an object doesn't copy its items");
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
}
|
||||
|
||||
json_decref(object);
|
||||
json_decref(copy);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_copy_simple();
|
||||
test_deep_copy_simple();
|
||||
test_copy_array();
|
||||
test_deep_copy_array();
|
||||
test_copy_object();
|
||||
test_deep_copy_object();
|
||||
return 0;
|
||||
}
|
||||
142
test/suites/api/test_dump.c
Normal file
142
test/suites/api/test_dump.c
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <jansson.h>
|
||||
#include <string.h>
|
||||
#include "util.h"
|
||||
|
||||
static void encode_twice()
|
||||
{
|
||||
/* Encode an empty object/array, add an item, encode again */
|
||||
|
||||
json_t *json;
|
||||
char *result;
|
||||
|
||||
json = json_object();
|
||||
result = json_dumps(json, 0);
|
||||
if(!result || strcmp(result, "{}"))
|
||||
fail("json_dumps failed");
|
||||
free(result);
|
||||
|
||||
json_object_set_new(json, "foo", json_integer(5));
|
||||
result = json_dumps(json, 0);
|
||||
if(!result || strcmp(result, "{\"foo\": 5}"))
|
||||
fail("json_dumps failed");
|
||||
free(result);
|
||||
|
||||
json_decref(json);
|
||||
|
||||
json = json_array();
|
||||
result = json_dumps(json, 0);
|
||||
if(!result || strcmp(result, "[]"))
|
||||
fail("json_dumps failed");
|
||||
free(result);
|
||||
|
||||
json_array_append_new(json, json_integer(5));
|
||||
result = json_dumps(json, 0);
|
||||
if(!result || strcmp(result, "[5]"))
|
||||
fail("json_dumps failed");
|
||||
free(result);
|
||||
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
static void circular_references()
|
||||
{
|
||||
/* Construct a JSON object/array with a circular reference:
|
||||
|
||||
object: {"a": {"b": {"c": <circular reference to $.a>}}}
|
||||
array: [[[<circular reference to the $[0] array>]]]
|
||||
|
||||
Encode it, remove the circular reference and encode again.
|
||||
*/
|
||||
|
||||
json_t *json;
|
||||
char *result;
|
||||
|
||||
json = json_object();
|
||||
json_object_set_new(json, "a", json_object());
|
||||
json_object_set_new(json_object_get(json, "a"), "b", json_object());
|
||||
json_object_set(json_object_get(json_object_get(json, "a"), "b"), "c",
|
||||
json_object_get(json, "a"));
|
||||
|
||||
if(json_dumps(json, 0))
|
||||
fail("json_dumps encoded a circular reference!");
|
||||
|
||||
json_object_del(json_object_get(json_object_get(json, "a"), "b"), "c");
|
||||
|
||||
result = json_dumps(json, 0);
|
||||
if(!result || strcmp(result, "{\"a\": {\"b\": {}}}"))
|
||||
fail("json_dumps failed!");
|
||||
free(result);
|
||||
|
||||
json_decref(json);
|
||||
|
||||
json = json_array();
|
||||
json_array_append_new(json, json_array());
|
||||
json_array_append_new(json_array_get(json, 0), json_array());
|
||||
json_array_append(json_array_get(json_array_get(json, 0), 0),
|
||||
json_array_get(json, 0));
|
||||
|
||||
if(json_dumps(json, 0))
|
||||
fail("json_dumps encoded a circular reference!");
|
||||
|
||||
json_array_remove(json_array_get(json_array_get(json, 0), 0), 0);
|
||||
|
||||
result = json_dumps(json, 0);
|
||||
if(!result || strcmp(result, "[[[]]]"))
|
||||
fail("json_dumps failed!");
|
||||
free(result);
|
||||
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
static void encode_other_than_array_or_object()
|
||||
{
|
||||
/* Encoding anything other than array or object should only
|
||||
* succeed if the JSON_ENCODE_ANY flag is used */
|
||||
|
||||
json_t *json;
|
||||
FILE *fp = NULL;
|
||||
char *result;
|
||||
|
||||
json = json_string("foo");
|
||||
if(json_dumps(json, 0) != NULL)
|
||||
fail("json_dumps encoded a string!");
|
||||
if(json_dumpf(json, fp, 0) == 0)
|
||||
fail("json_dumpf encoded a string!");
|
||||
|
||||
result = json_dumps(json, JSON_ENCODE_ANY);
|
||||
if(!result || strcmp(result, "\"foo\"") != 0)
|
||||
fail("json_dumps failed to encode a string with JSON_ENCODE_ANY");
|
||||
|
||||
free(result);
|
||||
json_decref(json);
|
||||
|
||||
json = json_integer(42);
|
||||
if(json_dumps(json, 0) != NULL)
|
||||
fail("json_dumps encoded an integer!");
|
||||
if(json_dumpf(json, fp, 0) == 0)
|
||||
fail("json_dumpf encoded an integer!");
|
||||
|
||||
result = json_dumps(json, JSON_ENCODE_ANY);
|
||||
if(!result || strcmp(result, "42") != 0)
|
||||
fail("json_dumps failed to encode an integer with JSON_ENCODE_ANY");
|
||||
|
||||
free(result);
|
||||
json_decref(json);
|
||||
|
||||
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
encode_twice();
|
||||
circular_references();
|
||||
encode_other_than_array_or_object();
|
||||
return 0;
|
||||
}
|
||||
190
test/suites/api/test_equal.c
Normal file
190
test/suites/api/test_equal.c
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <jansson.h>
|
||||
#include "util.h"
|
||||
|
||||
static void test_equal_simple()
|
||||
{
|
||||
json_t *value1, *value2;
|
||||
|
||||
if(json_equal(NULL, NULL))
|
||||
fail("json_equal fails for two NULLs");
|
||||
|
||||
value1 = json_true();
|
||||
if(json_equal(value1, NULL) || json_equal(NULL, value1))
|
||||
fail("json_equal fails for NULL");
|
||||
|
||||
/* this covers true, false and null as they are singletons */
|
||||
if(!json_equal(value1, value1))
|
||||
fail("identical objects are not equal");
|
||||
json_decref(value1);
|
||||
|
||||
/* integer */
|
||||
value1 = json_integer(1);
|
||||
value2 = json_integer(1);
|
||||
if(!value1 || !value2)
|
||||
fail("unable to create integers");
|
||||
if(!json_equal(value1, value2))
|
||||
fail("json_equal fails for two equal integers");
|
||||
json_decref(value2);
|
||||
|
||||
value2 = json_integer(2);
|
||||
if(!value2)
|
||||
fail("unable to create an integer");
|
||||
if(json_equal(value1, value2))
|
||||
fail("json_equal fails for two inequal integers");
|
||||
|
||||
json_decref(value1);
|
||||
json_decref(value2);
|
||||
|
||||
/* real */
|
||||
value1 = json_real(1.2);
|
||||
value2 = json_real(1.2);
|
||||
if(!value1 || !value2)
|
||||
fail("unable to create reals");
|
||||
if(!json_equal(value1, value2))
|
||||
fail("json_equal fails for two equal reals");
|
||||
json_decref(value2);
|
||||
|
||||
value2 = json_real(3.141592);
|
||||
if(!value2)
|
||||
fail("unable to create an real");
|
||||
if(json_equal(value1, value2))
|
||||
fail("json_equal fails for two inequal reals");
|
||||
|
||||
json_decref(value1);
|
||||
json_decref(value2);
|
||||
|
||||
/* string */
|
||||
value1 = json_string("foo");
|
||||
value2 = json_string("foo");
|
||||
if(!value1 || !value2)
|
||||
fail("unable to create strings");
|
||||
if(!json_equal(value1, value2))
|
||||
fail("json_equal fails for two equal strings");
|
||||
json_decref(value2);
|
||||
|
||||
value2 = json_string("bar");
|
||||
if(!value2)
|
||||
fail("unable to create an string");
|
||||
if(json_equal(value1, value2))
|
||||
fail("json_equal fails for two inequal strings");
|
||||
|
||||
json_decref(value1);
|
||||
json_decref(value2);
|
||||
}
|
||||
|
||||
static void test_equal_array()
|
||||
{
|
||||
json_t *array1, *array2;
|
||||
|
||||
array1 = json_array();
|
||||
array2 = json_array();
|
||||
if(!array1 || !array2)
|
||||
fail("unable to create arrays");
|
||||
|
||||
if(!json_equal(array1, array2))
|
||||
fail("json_equal fails for two empty arrays");
|
||||
|
||||
json_array_append_new(array1, json_integer(1));
|
||||
json_array_append_new(array2, json_integer(1));
|
||||
json_array_append_new(array1, json_string("foo"));
|
||||
json_array_append_new(array2, json_string("foo"));
|
||||
json_array_append_new(array1, json_integer(2));
|
||||
json_array_append_new(array2, json_integer(2));
|
||||
if(!json_equal(array1, array2))
|
||||
fail("json_equal fails for two equal arrays");
|
||||
|
||||
json_array_remove(array2, 2);
|
||||
if(json_equal(array1, array2))
|
||||
fail("json_equal fails for two inequal arrays");
|
||||
|
||||
json_array_append_new(array2, json_integer(3));
|
||||
if(json_equal(array1, array2))
|
||||
fail("json_equal fails for two inequal arrays");
|
||||
|
||||
json_decref(array1);
|
||||
json_decref(array2);
|
||||
}
|
||||
|
||||
static void test_equal_object()
|
||||
{
|
||||
json_t *object1, *object2;
|
||||
|
||||
object1 = json_object();
|
||||
object2 = json_object();
|
||||
if(!object1 || !object2)
|
||||
fail("unable to create objects");
|
||||
|
||||
if(!json_equal(object1, object2))
|
||||
fail("json_equal fails for two empty objects");
|
||||
|
||||
json_object_set_new(object1, "a", json_integer(1));
|
||||
json_object_set_new(object2, "a", json_integer(1));
|
||||
json_object_set_new(object1, "b", json_string("foo"));
|
||||
json_object_set_new(object2, "b", json_string("foo"));
|
||||
json_object_set_new(object1, "c", json_integer(2));
|
||||
json_object_set_new(object2, "c", json_integer(2));
|
||||
if(!json_equal(object1, object2))
|
||||
fail("json_equal fails for two equal objects");
|
||||
|
||||
json_object_del(object2, "c");
|
||||
if(json_equal(object1, object2))
|
||||
fail("json_equal fails for two inequal objects");
|
||||
|
||||
json_object_set_new(object2, "c", json_integer(3));
|
||||
if(json_equal(object1, object2))
|
||||
fail("json_equal fails for two inequal objects");
|
||||
|
||||
json_object_del(object2, "c");
|
||||
json_object_set_new(object2, "d", json_integer(2));
|
||||
if(json_equal(object1, object2))
|
||||
fail("json_equal fails for two inequal objects");
|
||||
|
||||
json_decref(object1);
|
||||
json_decref(object2);
|
||||
}
|
||||
|
||||
static void test_equal_complex()
|
||||
{
|
||||
json_t *value1, *value2;
|
||||
|
||||
const char *complex_json =
|
||||
"{"
|
||||
" \"integer\": 1, "
|
||||
" \"real\": 3.141592, "
|
||||
" \"string\": \"foobar\", "
|
||||
" \"true\": true, "
|
||||
" \"object\": {"
|
||||
" \"array-in-object\": [1,true,\"foo\",{}],"
|
||||
" \"object-in-object\": {\"foo\": \"bar\"}"
|
||||
" },"
|
||||
" \"array\": [\"foo\", false, null, 1.234]"
|
||||
"}";
|
||||
|
||||
value1 = json_loads(complex_json, 0, NULL);
|
||||
value2 = json_loads(complex_json, 0, NULL);
|
||||
if(!value1 || !value2)
|
||||
fail("unable to parse JSON");
|
||||
if(!json_equal(value1, value2))
|
||||
fail("json_equal fails for two inequal strings");
|
||||
|
||||
json_decref(value1);
|
||||
json_decref(value2);
|
||||
|
||||
/* TODO: There's no negative test case here */
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_equal_simple();
|
||||
test_equal_array();
|
||||
test_equal_object();
|
||||
test_equal_complex();
|
||||
return 0;
|
||||
}
|
||||
60
test/suites/api/test_load.c
Normal file
60
test/suites/api/test_load.c
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <jansson.h>
|
||||
#include <string.h>
|
||||
#include "util.h"
|
||||
|
||||
static void file_not_found()
|
||||
{
|
||||
json_t *json;
|
||||
json_error_t error;
|
||||
|
||||
json = json_load_file("/path/to/nonexistent/file.json", 0, &error);
|
||||
if(json)
|
||||
fail("json_load_file returned non-NULL for a nonexistent file");
|
||||
if(error.line != -1)
|
||||
fail("json_load_file returned an invalid line number");
|
||||
if(strcmp(error.text, "unable to open /path/to/nonexistent/file.json: No such file or directory") != 0)
|
||||
fail("json_load_file returned an invalid error message");
|
||||
}
|
||||
|
||||
static void reject_duplicates()
|
||||
{
|
||||
json_error_t error;
|
||||
|
||||
if(json_loads("{\"foo\": 1, \"foo\": 2}", JSON_REJECT_DUPLICATES, &error))
|
||||
fail("json_loads did not detect a duplicate key");
|
||||
check_error("duplicate object key near '\"foo\"'", "<string>", 1, 16, 16);
|
||||
}
|
||||
|
||||
static void disable_eof_check()
|
||||
{
|
||||
json_error_t error;
|
||||
json_t *json;
|
||||
|
||||
const char *text = "{\"foo\": 1} garbage";
|
||||
|
||||
if(json_loads(text, 0, &error))
|
||||
fail("json_loads did not detect garbage after JSON text");
|
||||
check_error("end of file expected near 'garbage'", "<string>", 1, 18, 18);
|
||||
|
||||
json = json_loads(text, JSON_DISABLE_EOF_CHECK, &error);
|
||||
if(!json)
|
||||
fail("json_loads failed with JSON_DISABLE_EOF_CHECK");
|
||||
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
file_not_found();
|
||||
reject_duplicates();
|
||||
disable_eof_check();
|
||||
|
||||
return 0;
|
||||
}
|
||||
38
test/suites/api/test_loadb.c
Normal file
38
test/suites/api/test_loadb.c
Normal 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;
|
||||
}
|
||||
84
test/suites/api/test_memory_funcs.c
Normal file
84
test/suites/api/test_memory_funcs.c
Normal 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;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
451
test/suites/api/test_object.c
Normal file
451
test/suites/api/test_object.c
Normal file
@@ -0,0 +1,451 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <jansson.h>
|
||||
#include <string.h>
|
||||
#include "util.h"
|
||||
|
||||
static void test_clear()
|
||||
{
|
||||
json_t *object, *ten;
|
||||
|
||||
object = json_object();
|
||||
ten = json_integer(10);
|
||||
|
||||
if(!object)
|
||||
fail("unable to create object");
|
||||
if(!ten)
|
||||
fail("unable to create integer");
|
||||
|
||||
if(json_object_set(object, "a", ten) ||
|
||||
json_object_set(object, "b", ten) ||
|
||||
json_object_set(object, "c", ten) ||
|
||||
json_object_set(object, "d", ten) ||
|
||||
json_object_set(object, "e", ten))
|
||||
fail("unable to set value");
|
||||
|
||||
if(json_object_size(object) != 5)
|
||||
fail("invalid size");
|
||||
|
||||
json_object_clear(object);
|
||||
|
||||
if(json_object_size(object) != 0)
|
||||
fail("invalid size after clear");
|
||||
|
||||
json_decref(ten);
|
||||
json_decref(object);
|
||||
}
|
||||
|
||||
static void test_update()
|
||||
{
|
||||
json_t *object, *other, *nine, *ten;
|
||||
|
||||
object = json_object();
|
||||
other = json_object();
|
||||
|
||||
nine = json_integer(9);
|
||||
ten = json_integer(10);
|
||||
|
||||
if(!object || !other)
|
||||
fail("unable to create object");
|
||||
if(!nine || !ten)
|
||||
fail("unable to create integer");
|
||||
|
||||
|
||||
/* update an empty object with an empty object */
|
||||
|
||||
if(json_object_update(object, other))
|
||||
fail("unable to update an emtpy object with an empty object");
|
||||
|
||||
if(json_object_size(object) != 0)
|
||||
fail("invalid size after update");
|
||||
|
||||
if(json_object_size(other) != 0)
|
||||
fail("invalid size for updater after update");
|
||||
|
||||
|
||||
/* update an empty object with a nonempty object */
|
||||
|
||||
if(json_object_set(other, "a", ten) ||
|
||||
json_object_set(other, "b", ten) ||
|
||||
json_object_set(other, "c", ten) ||
|
||||
json_object_set(other, "d", ten) ||
|
||||
json_object_set(other, "e", ten))
|
||||
fail("unable to set value");
|
||||
|
||||
if(json_object_update(object, other))
|
||||
fail("unable to update an empty object");
|
||||
|
||||
if(json_object_size(object) != 5)
|
||||
fail("invalid size after update");
|
||||
|
||||
if(json_object_get(object, "a") != ten ||
|
||||
json_object_get(object, "b") != ten ||
|
||||
json_object_get(object, "c") != ten ||
|
||||
json_object_get(object, "d") != ten ||
|
||||
json_object_get(object, "e") != ten)
|
||||
fail("update works incorrectly");
|
||||
|
||||
|
||||
/* perform the same update again */
|
||||
|
||||
if(json_object_update(object, other))
|
||||
fail("unable to update an empty object");
|
||||
|
||||
if(json_object_size(object) != 5)
|
||||
fail("invalid size after update");
|
||||
|
||||
if(json_object_get(object, "a") != ten ||
|
||||
json_object_get(object, "b") != ten ||
|
||||
json_object_get(object, "c") != ten ||
|
||||
json_object_get(object, "d") != ten ||
|
||||
json_object_get(object, "e") != ten)
|
||||
fail("update works incorrectly");
|
||||
|
||||
|
||||
/* update a nonempty object with a nonempty object with both old
|
||||
and new keys */
|
||||
|
||||
if(json_object_clear(other))
|
||||
fail("clear failed");
|
||||
|
||||
if(json_object_set(other, "a", nine) ||
|
||||
json_object_set(other, "b", nine) ||
|
||||
json_object_set(other, "f", nine) ||
|
||||
json_object_set(other, "g", nine) ||
|
||||
json_object_set(other, "h", nine))
|
||||
fail("unable to set value");
|
||||
|
||||
if(json_object_update(object, other))
|
||||
fail("unable to update a nonempty object");
|
||||
|
||||
if(json_object_size(object) != 8)
|
||||
fail("invalid size after update");
|
||||
|
||||
if(json_object_get(object, "a") != nine ||
|
||||
json_object_get(object, "b") != nine ||
|
||||
json_object_get(object, "f") != nine ||
|
||||
json_object_get(object, "g") != nine ||
|
||||
json_object_get(object, "h") != nine)
|
||||
fail("update works incorrectly");
|
||||
|
||||
json_decref(nine);
|
||||
json_decref(ten);
|
||||
json_decref(other);
|
||||
json_decref(object);
|
||||
}
|
||||
|
||||
static void test_circular()
|
||||
{
|
||||
json_t *object1, *object2;
|
||||
|
||||
object1 = json_object();
|
||||
object2 = json_object();
|
||||
if(!object1 || !object2)
|
||||
fail("unable to create object");
|
||||
|
||||
/* the simple case is checked */
|
||||
if(json_object_set(object1, "a", object1) == 0)
|
||||
fail("able to set self");
|
||||
|
||||
/* create circular references */
|
||||
if(json_object_set(object1, "a", object2) ||
|
||||
json_object_set(object2, "a", object1))
|
||||
fail("unable to set value");
|
||||
|
||||
/* circularity is detected when dumping */
|
||||
if(json_dumps(object1, 0) != NULL)
|
||||
fail("able to dump circulars");
|
||||
|
||||
/* decref twice to deal with the circular references */
|
||||
json_decref(object1);
|
||||
json_decref(object2);
|
||||
json_decref(object1);
|
||||
}
|
||||
|
||||
static void test_set_nocheck()
|
||||
{
|
||||
json_t *object, *string;
|
||||
|
||||
object = json_object();
|
||||
string = json_string("bar");
|
||||
|
||||
if(!object)
|
||||
fail("unable to create object");
|
||||
if(!string)
|
||||
fail("unable to create string");
|
||||
|
||||
if(json_object_set_nocheck(object, "foo", string))
|
||||
fail("json_object_set_nocheck failed");
|
||||
if(json_object_get(object, "foo") != string)
|
||||
fail("json_object_get after json_object_set_nocheck failed");
|
||||
|
||||
/* invalid UTF-8 in key */
|
||||
if(json_object_set_nocheck(object, "a\xefz", string))
|
||||
fail("json_object_set_nocheck failed for invalid UTF-8");
|
||||
if(json_object_get(object, "a\xefz") != string)
|
||||
fail("json_object_get after json_object_set_nocheck failed");
|
||||
|
||||
if(json_object_set_new_nocheck(object, "bax", json_integer(123)))
|
||||
fail("json_object_set_new_nocheck failed");
|
||||
if(json_integer_value(json_object_get(object, "bax")) != 123)
|
||||
fail("json_object_get after json_object_set_new_nocheck failed");
|
||||
|
||||
/* invalid UTF-8 in key */
|
||||
if(json_object_set_new_nocheck(object, "asdf\xfe", json_integer(321)))
|
||||
fail("json_object_set_new_nocheck failed for invalid UTF-8");
|
||||
if(json_integer_value(json_object_get(object, "asdf\xfe")) != 321)
|
||||
fail("json_object_get after json_object_set_new_nocheck failed");
|
||||
|
||||
json_decref(string);
|
||||
json_decref(object);
|
||||
}
|
||||
|
||||
static void test_iterators()
|
||||
{
|
||||
json_t *object, *foo, *bar, *baz;
|
||||
void *iter;
|
||||
|
||||
if(json_object_iter(NULL))
|
||||
fail("able to iterate over NULL");
|
||||
|
||||
if(json_object_iter_next(NULL, NULL))
|
||||
fail("able to increment an iterator on a NULL object");
|
||||
|
||||
object = json_object();
|
||||
foo = json_string("foo");
|
||||
bar = json_string("bar");
|
||||
baz = json_string("baz");
|
||||
if(!object || !foo || !bar || !bar)
|
||||
fail("unable to create values");
|
||||
|
||||
if(json_object_iter_next(object, NULL))
|
||||
fail("able to increment a NULL iterator");
|
||||
|
||||
if(json_object_set(object, "a", foo) ||
|
||||
json_object_set(object, "b", bar) ||
|
||||
json_object_set(object, "c", baz))
|
||||
fail("unable to populate object");
|
||||
|
||||
iter = json_object_iter(object);
|
||||
if(!iter)
|
||||
fail("unable to get iterator");
|
||||
if(strcmp(json_object_iter_key(iter), "a"))
|
||||
fail("iterating failed: wrong key");
|
||||
if(json_object_iter_value(iter) != foo)
|
||||
fail("iterating failed: wrong value");
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
if(!iter)
|
||||
fail("unable to increment iterator");
|
||||
if(strcmp(json_object_iter_key(iter), "b"))
|
||||
fail("iterating failed: wrong key");
|
||||
if(json_object_iter_value(iter) != bar)
|
||||
fail("iterating failed: wrong value");
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
if(!iter)
|
||||
fail("unable to increment iterator");
|
||||
if(strcmp(json_object_iter_key(iter), "c"))
|
||||
fail("iterating failed: wrong key");
|
||||
if(json_object_iter_value(iter) != baz)
|
||||
fail("iterating failed: wrong value");
|
||||
|
||||
if(json_object_iter_next(object, iter) != NULL)
|
||||
fail("able to iterate over the end");
|
||||
|
||||
if(json_object_iter_at(object, "foo"))
|
||||
fail("json_object_iter_at() succeeds for non-existent key");
|
||||
|
||||
iter = json_object_iter_at(object, "b");
|
||||
if(!iter)
|
||||
fail("json_object_iter_at() fails for an existing key");
|
||||
|
||||
if(strcmp(json_object_iter_key(iter), "b"))
|
||||
fail("iterating failed: wrong key");
|
||||
if(json_object_iter_value(iter) != bar)
|
||||
fail("iterating failed: wrong value");
|
||||
|
||||
iter = json_object_iter_next(object, iter);
|
||||
if(!iter)
|
||||
fail("unable to increment iterator");
|
||||
if(strcmp(json_object_iter_key(iter), "c"))
|
||||
fail("iterating failed: wrong key");
|
||||
if(json_object_iter_value(iter) != baz)
|
||||
fail("iterating failed: wrong value");
|
||||
|
||||
if(json_object_iter_set(object, iter, bar))
|
||||
fail("unable to set value at iterator");
|
||||
|
||||
if(strcmp(json_object_iter_key(iter), "c"))
|
||||
fail("json_object_iter_key() fails after json_object_iter_set()");
|
||||
if(json_object_iter_value(iter) != bar)
|
||||
fail("json_object_iter_value() fails after json_object_iter_set()");
|
||||
if(json_object_get(object, "c") != bar)
|
||||
fail("json_object_get() fails after json_object_iter_set()");
|
||||
|
||||
json_decref(object);
|
||||
json_decref(foo);
|
||||
json_decref(bar);
|
||||
json_decref(baz);
|
||||
}
|
||||
|
||||
static void test_misc()
|
||||
{
|
||||
json_t *object, *string, *other_string, *value;
|
||||
|
||||
object = json_object();
|
||||
string = json_string("test");
|
||||
other_string = json_string("other");
|
||||
|
||||
if(!object)
|
||||
fail("unable to create object");
|
||||
if(!string || !other_string)
|
||||
fail("unable to create string");
|
||||
|
||||
if(json_object_get(object, "a"))
|
||||
fail("value for nonexisting key");
|
||||
|
||||
if(json_object_set(object, "a", string))
|
||||
fail("unable to set value");
|
||||
|
||||
if(!json_object_set(object, NULL, string))
|
||||
fail("able to set NULL key");
|
||||
|
||||
if(!json_object_set(object, "a", NULL))
|
||||
fail("able to set NULL value");
|
||||
|
||||
/* invalid UTF-8 in key */
|
||||
if(!json_object_set(object, "a\xefz", string))
|
||||
fail("able to set invalid unicode key");
|
||||
|
||||
value = json_object_get(object, "a");
|
||||
if(!value)
|
||||
fail("no value for existing key");
|
||||
if(value != string)
|
||||
fail("got different value than what was added");
|
||||
|
||||
/* "a", "lp" and "px" collide in a five-bucket hashtable */
|
||||
if(json_object_set(object, "b", string) ||
|
||||
json_object_set(object, "lp", string) ||
|
||||
json_object_set(object, "px", string))
|
||||
fail("unable to set value");
|
||||
|
||||
value = json_object_get(object, "a");
|
||||
if(!value)
|
||||
fail("no value for existing key");
|
||||
if(value != string)
|
||||
fail("got different value than what was added");
|
||||
|
||||
if(json_object_set(object, "a", other_string))
|
||||
fail("unable to replace an existing key");
|
||||
|
||||
value = json_object_get(object, "a");
|
||||
if(!value)
|
||||
fail("no value for existing key");
|
||||
if(value != other_string)
|
||||
fail("got different value than what was set");
|
||||
|
||||
if(!json_object_del(object, "nonexisting"))
|
||||
fail("able to delete a nonexisting key");
|
||||
|
||||
if(json_object_del(object, "px"))
|
||||
fail("unable to delete an existing key");
|
||||
|
||||
if(json_object_del(object, "a"))
|
||||
fail("unable to delete an existing key");
|
||||
|
||||
if(json_object_del(object, "lp"))
|
||||
fail("unable to delete an existing key");
|
||||
|
||||
|
||||
/* add many keys to initiate rehashing */
|
||||
|
||||
if(json_object_set(object, "a", string))
|
||||
fail("unable to set value");
|
||||
|
||||
if(json_object_set(object, "lp", string))
|
||||
fail("unable to set value");
|
||||
|
||||
if(json_object_set(object, "px", string))
|
||||
fail("unable to set value");
|
||||
|
||||
if(json_object_set(object, "c", string))
|
||||
fail("unable to set value");
|
||||
|
||||
if(json_object_set(object, "d", string))
|
||||
fail("unable to set value");
|
||||
|
||||
if(json_object_set(object, "e", string))
|
||||
fail("unable to set value");
|
||||
|
||||
|
||||
if(json_object_set_new(object, "foo", json_integer(123)))
|
||||
fail("unable to set new value");
|
||||
|
||||
value = json_object_get(object, "foo");
|
||||
if(!json_is_integer(value) || json_integer_value(value) != 123)
|
||||
fail("json_object_set_new works incorrectly");
|
||||
|
||||
if(!json_object_set_new(object, NULL, json_integer(432)))
|
||||
fail("able to set_new NULL key");
|
||||
|
||||
if(!json_object_set_new(object, "foo", NULL))
|
||||
fail("able to set_new NULL value");
|
||||
|
||||
json_decref(string);
|
||||
json_decref(other_string);
|
||||
json_decref(object);
|
||||
}
|
||||
|
||||
static void test_preserve_order()
|
||||
{
|
||||
json_t *object;
|
||||
char *result;
|
||||
|
||||
const char *expected = "{\"foobar\": 1, \"bazquux\": 6, \"lorem ipsum\": 3, \"sit amet\": 5, \"helicopter\": 7}";
|
||||
|
||||
object = json_object();
|
||||
|
||||
json_object_set_new(object, "foobar", json_integer(1));
|
||||
json_object_set_new(object, "bazquux", json_integer(2));
|
||||
json_object_set_new(object, "lorem ipsum", json_integer(3));
|
||||
json_object_set_new(object, "dolor", json_integer(4));
|
||||
json_object_set_new(object, "sit amet", json_integer(5));
|
||||
|
||||
/* changing a value should preserve the order */
|
||||
json_object_set_new(object, "bazquux", json_integer(6));
|
||||
|
||||
/* deletion shouldn't change the order of others */
|
||||
json_object_del(object, "dolor");
|
||||
|
||||
/* add a new item just to make sure */
|
||||
json_object_set_new(object, "helicopter", json_integer(7));
|
||||
|
||||
result = json_dumps(object, JSON_PRESERVE_ORDER);
|
||||
|
||||
if(strcmp(expected, result) != 0) {
|
||||
fprintf(stderr, "%s != %s", expected, result);
|
||||
fail("JSON_PRESERVE_ORDER doesn't work");
|
||||
}
|
||||
|
||||
free(result);
|
||||
json_decref(object);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_misc();
|
||||
test_clear();
|
||||
test_update();
|
||||
test_circular();
|
||||
test_set_nocheck();
|
||||
test_iterators();
|
||||
test_preserve_order();
|
||||
|
||||
return 0;
|
||||
}
|
||||
232
test/suites/api/test_pack.c
Normal file
232
test/suites/api/test_pack.c
Normal 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;
|
||||
}
|
||||
185
test/suites/api/test_simple.c
Normal file
185
test/suites/api/test_simple.c
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <jansson.h>
|
||||
#include "util.h"
|
||||
|
||||
/* Call the simple functions not covered by other tests of the public API */
|
||||
int main()
|
||||
{
|
||||
json_t *value;
|
||||
|
||||
value = json_integer(1);
|
||||
if(json_typeof(value) != JSON_INTEGER)
|
||||
fail("json_typeof failed");
|
||||
|
||||
if(json_is_object(value))
|
||||
fail("json_is_object failed");
|
||||
|
||||
if(json_is_array(value))
|
||||
fail("json_is_array failed");
|
||||
|
||||
if(json_is_string(value))
|
||||
fail("json_is_string failed");
|
||||
|
||||
if(!json_is_integer(value))
|
||||
fail("json_is_integer failed");
|
||||
|
||||
if(json_is_real(value))
|
||||
fail("json_is_real failed");
|
||||
|
||||
if(!json_is_number(value))
|
||||
fail("json_is_number failed");
|
||||
|
||||
if(json_is_true(value))
|
||||
fail("json_is_true failed");
|
||||
|
||||
if(json_is_false(value))
|
||||
fail("json_is_false failed");
|
||||
|
||||
if(json_is_boolean(value))
|
||||
fail("json_is_boolean failed");
|
||||
|
||||
if(json_is_null(value))
|
||||
fail("json_is_null failed");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
|
||||
value = json_string("foo");
|
||||
if(!value)
|
||||
fail("json_string failed");
|
||||
if(strcmp(json_string_value(value), "foo"))
|
||||
fail("invalid string value");
|
||||
|
||||
if(json_string_set(value, "bar"))
|
||||
fail("json_string_set failed");
|
||||
if(strcmp(json_string_value(value), "bar"))
|
||||
fail("invalid string value");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
value = json_string(NULL);
|
||||
if(value)
|
||||
fail("json_string(NULL) failed");
|
||||
|
||||
/* invalid UTF-8 */
|
||||
value = json_string("a\xefz");
|
||||
if(value)
|
||||
fail("json_string(<invalid utf-8>) failed");
|
||||
|
||||
value = json_string_nocheck("foo");
|
||||
if(!value)
|
||||
fail("json_string_nocheck failed");
|
||||
if(strcmp(json_string_value(value), "foo"))
|
||||
fail("invalid string value");
|
||||
|
||||
if(json_string_set_nocheck(value, "bar"))
|
||||
fail("json_string_set_nocheck failed");
|
||||
if(strcmp(json_string_value(value), "bar"))
|
||||
fail("invalid string value");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
/* invalid UTF-8 */
|
||||
value = json_string_nocheck("qu\xff");
|
||||
if(!value)
|
||||
fail("json_string_nocheck failed");
|
||||
if(strcmp(json_string_value(value), "qu\xff"))
|
||||
fail("invalid string value");
|
||||
|
||||
if(json_string_set_nocheck(value, "\xfd\xfe\xff"))
|
||||
fail("json_string_set_nocheck failed");
|
||||
if(strcmp(json_string_value(value), "\xfd\xfe\xff"))
|
||||
fail("invalid string value");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
|
||||
value = json_integer(123);
|
||||
if(!value)
|
||||
fail("json_integer failed");
|
||||
if(json_integer_value(value) != 123)
|
||||
fail("invalid integer value");
|
||||
if(json_number_value(value) != 123.0)
|
||||
fail("invalid number value");
|
||||
|
||||
if(json_integer_set(value, 321))
|
||||
fail("json_integer_set failed");
|
||||
if(json_integer_value(value) != 321)
|
||||
fail("invalid integer value");
|
||||
if(json_number_value(value) != 321.0)
|
||||
fail("invalid number value");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
value = json_real(123.123);
|
||||
if(!value)
|
||||
fail("json_real failed");
|
||||
if(json_real_value(value) != 123.123)
|
||||
fail("invalid integer value");
|
||||
if(json_number_value(value) != 123.123)
|
||||
fail("invalid number value");
|
||||
|
||||
if(json_real_set(value, 321.321))
|
||||
fail("json_real_set failed");
|
||||
if(json_real_value(value) != 321.321)
|
||||
fail("invalid real value");
|
||||
if(json_number_value(value) != 321.321)
|
||||
fail("invalid number value");
|
||||
|
||||
json_decref(value);
|
||||
|
||||
value = json_true();
|
||||
if(!value)
|
||||
fail("json_true failed");
|
||||
json_decref(value);
|
||||
|
||||
value = json_false();
|
||||
if(!value)
|
||||
fail("json_false failed");
|
||||
json_decref(value);
|
||||
|
||||
value = json_null();
|
||||
if(!value)
|
||||
fail("json_null failed");
|
||||
json_decref(value);
|
||||
|
||||
/* Test reference counting on singletons (true, false, null) */
|
||||
value = json_true();
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("refcounting true works incorrectly");
|
||||
json_decref(value);
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("refcounting true works incorrectly");
|
||||
json_incref(value);
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("refcounting true works incorrectly");
|
||||
|
||||
value = json_false();
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("refcounting false works incorrectly");
|
||||
json_decref(value);
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("refcounting false works incorrectly");
|
||||
json_incref(value);
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("refcounting false works incorrectly");
|
||||
|
||||
value = json_null();
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("refcounting null works incorrectly");
|
||||
json_decref(value);
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("refcounting null works incorrectly");
|
||||
json_incref(value);
|
||||
if(value->refcount != (size_t)-1)
|
||||
fail("refcounting null works incorrectly");
|
||||
|
||||
return 0;
|
||||
}
|
||||
341
test/suites/api/test_unpack.c
Normal file
341
test/suites/api/test_unpack.c
Normal file
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
* Copyright (c) 2010-2011 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <jansson.h>
|
||||
#include <stdio.h>
|
||||
#include "util.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
json_t *j, *j2;
|
||||
int i1, i2, i3;
|
||||
json_int_t I1;
|
||||
int rv;
|
||||
double f;
|
||||
char *s;
|
||||
|
||||
json_error_t error;
|
||||
|
||||
/*
|
||||
* Simple, valid json_pack cases
|
||||
*/
|
||||
|
||||
/* true */
|
||||
rv = json_unpack(json_true(), "b", &i1);
|
||||
if(rv || !i1)
|
||||
fail("json_unpack boolean failed");
|
||||
|
||||
/* false */
|
||||
rv = json_unpack(json_false(), "b", &i1);
|
||||
if(rv || i1)
|
||||
fail("json_unpack boolean failed");
|
||||
|
||||
/* null */
|
||||
if(json_unpack(json_null(), "n"))
|
||||
fail("json_unpack null failed");
|
||||
|
||||
/* integer */
|
||||
j = json_integer(42);
|
||||
rv = json_unpack(j, "i", &i1);
|
||||
if(rv || i1 != 42)
|
||||
fail("json_unpack integer failed");
|
||||
json_decref(j);
|
||||
|
||||
/* json_int_t */
|
||||
j = json_integer(5555555);
|
||||
rv = json_unpack(j, "I", &I1);
|
||||
if(rv || I1 != 5555555)
|
||||
fail("json_unpack json_int_t failed");
|
||||
json_decref(j);
|
||||
|
||||
/* real */
|
||||
j = json_real(1.7);
|
||||
rv = json_unpack(j, "f", &f);
|
||||
if(rv || f != 1.7)
|
||||
fail("json_unpack real failed");
|
||||
json_decref(j);
|
||||
|
||||
/* number */
|
||||
j = json_integer(12345);
|
||||
rv = json_unpack(j, "F", &f);
|
||||
if(rv || f != 12345.0)
|
||||
fail("json_unpack (real or) integer failed");
|
||||
json_decref(j);
|
||||
|
||||
j = json_real(1.7);
|
||||
rv = json_unpack(j, "F", &f);
|
||||
if(rv || f != 1.7)
|
||||
fail("json_unpack real (or integer) failed");
|
||||
json_decref(j);
|
||||
|
||||
/* string */
|
||||
j = json_string("foo");
|
||||
rv = json_unpack(j, "s", &s);
|
||||
if(rv || strcmp(s, "foo"))
|
||||
fail("json_unpack string failed");
|
||||
json_decref(j);
|
||||
|
||||
/* empty object */
|
||||
j = json_object();
|
||||
if(json_unpack(j, "{}"))
|
||||
fail("json_unpack empty object failed");
|
||||
json_decref(j);
|
||||
|
||||
/* empty list */
|
||||
j = json_array();
|
||||
if(json_unpack(j, "[]"))
|
||||
fail("json_unpack empty list failed");
|
||||
json_decref(j);
|
||||
|
||||
/* non-incref'd object */
|
||||
j = json_object();
|
||||
rv = json_unpack(j, "o", &j2);
|
||||
if(j2 != j || j->refcount != 1)
|
||||
fail("json_unpack object failed");
|
||||
json_decref(j);
|
||||
|
||||
/* incref'd object */
|
||||
j = json_object();
|
||||
rv = json_unpack(j, "O", &j2);
|
||||
if(j2 != j || j->refcount != 2)
|
||||
fail("json_unpack object failed");
|
||||
json_decref(j);
|
||||
json_decref(j);
|
||||
|
||||
/* simple object */
|
||||
j = json_pack("{s:i}", "foo", 42);
|
||||
rv = json_unpack(j, "{s:i}", "foo", &i1);
|
||||
if(rv || i1 != 42)
|
||||
fail("json_unpack simple object failed");
|
||||
json_decref(j);
|
||||
|
||||
/* simple array */
|
||||
j = json_pack("[iii]", 1, 2, 3);
|
||||
rv = json_unpack(j, "[i,i,i]", &i1, &i2, &i3);
|
||||
if(rv || i1 != 1 || i2 != 2 || i3 != 3)
|
||||
fail("json_unpack simple array failed");
|
||||
json_decref(j);
|
||||
|
||||
/* object with many items & strict checking */
|
||||
j = json_pack("{s:i, s:i, s:i}", "a", 1, "b", 2, "c", 3);
|
||||
rv = json_unpack(j, "{s:i, s:i, s:i}", "a", &i1, "b", &i2, "c", &i3);
|
||||
if(rv || i1 != 1 || i2 != 2 || i3 != 3)
|
||||
fail("json_unpack object with many items failed");
|
||||
json_decref(j);
|
||||
|
||||
/*
|
||||
* Invalid cases
|
||||
*/
|
||||
|
||||
j = json_integer(42);
|
||||
if(!json_unpack_ex(j, &error, 0, "z"))
|
||||
fail("json_unpack succeeded with invalid format character");
|
||||
check_error("Unexpected format character 'z'", "<format>", 1, 1, 1);
|
||||
|
||||
if(!json_unpack_ex(NULL, &error, 0, "[i]"))
|
||||
fail("json_unpack succeeded with NULL root");
|
||||
check_error("NULL root value", "<root>", -1, -1, 0);
|
||||
json_decref(j);
|
||||
|
||||
/* mismatched open/close array/object */
|
||||
j = json_pack("[]");
|
||||
if(!json_unpack_ex(j, &error, 0, "[}"))
|
||||
fail("json_unpack failed to catch mismatched ']'");
|
||||
check_error("Unexpected format character '}'", "<format>", 1, 2, 2);
|
||||
json_decref(j);
|
||||
|
||||
j = json_pack("{}");
|
||||
if(!json_unpack_ex(j, &error, 0, "{]"))
|
||||
fail("json_unpack failed to catch mismatched '}'");
|
||||
check_error("Expected format 's', got ']'", "<format>", 1, 2, 2);
|
||||
json_decref(j);
|
||||
|
||||
/* missing close array */
|
||||
j = json_pack("[]");
|
||||
if(!json_unpack_ex(j, &error, 0, "["))
|
||||
fail("json_unpack failed to catch missing ']'");
|
||||
check_error("Unexpected end of format string", "<format>", 1, 2, 2);
|
||||
json_decref(j);
|
||||
|
||||
/* missing close object */
|
||||
j = json_pack("{}");
|
||||
if(!json_unpack_ex(j, &error, 0, "{"))
|
||||
fail("json_unpack failed to catch missing '}'");
|
||||
check_error("Unexpected end of format string", "<format>", 1, 2, 2);
|
||||
json_decref(j);
|
||||
|
||||
/* garbage after format string */
|
||||
j = json_pack("[i]", 42);
|
||||
if(!json_unpack_ex(j, &error, 0, "[i]a", &i1))
|
||||
fail("json_unpack failed to catch garbage after format string");
|
||||
check_error("Garbage after format string", "<format>", 1, 4, 4);
|
||||
json_decref(j);
|
||||
|
||||
j = json_integer(12345);
|
||||
if(!json_unpack_ex(j, &error, 0, "ia", &i1))
|
||||
fail("json_unpack failed to catch garbage after format string");
|
||||
check_error("Garbage after format string", "<format>", 1, 2, 2);
|
||||
json_decref(j);
|
||||
|
||||
/* NULL format string */
|
||||
j = json_pack("[]");
|
||||
if(!json_unpack_ex(j, &error, 0, NULL))
|
||||
fail("json_unpack failed to catch null format string");
|
||||
check_error("NULL or empty format string", "<format>", -1, -1, 0);
|
||||
json_decref(j);
|
||||
|
||||
/* NULL string pointer */
|
||||
j = json_string("foobie");
|
||||
if(!json_unpack_ex(j, &error, 0, "s", NULL))
|
||||
fail("json_unpack failed to catch null string pointer");
|
||||
check_error("NULL string argument", "<args>", 1, 1, 1);
|
||||
json_decref(j);
|
||||
|
||||
/* invalid types */
|
||||
j = json_integer(42);
|
||||
j2 = json_string("foo");
|
||||
if(!json_unpack_ex(j, &error, 0, "s"))
|
||||
fail("json_unpack failed to catch invalid type");
|
||||
check_error("Expected string, got integer", "<validation>", 1, 1, 1);
|
||||
|
||||
if(!json_unpack_ex(j, &error, 0, "n"))
|
||||
fail("json_unpack failed to catch invalid type");
|
||||
check_error("Expected null, got integer", "<validation>", 1, 1, 1);
|
||||
|
||||
if(!json_unpack_ex(j, &error, 0, "b"))
|
||||
fail("json_unpack failed to catch invalid type");
|
||||
check_error("Expected true or false, got integer", "<validation>", 1, 1, 1);
|
||||
|
||||
if(!json_unpack_ex(j2, &error, 0, "i"))
|
||||
fail("json_unpack failed to catch invalid type");
|
||||
check_error("Expected integer, got string", "<validation>", 1, 1, 1);
|
||||
|
||||
if(!json_unpack_ex(j2, &error, 0, "I"))
|
||||
fail("json_unpack failed to catch invalid type");
|
||||
check_error("Expected integer, got string", "<validation>", 1, 1, 1);
|
||||
|
||||
if(!json_unpack_ex(j, &error, 0, "f"))
|
||||
fail("json_unpack failed to catch invalid type");
|
||||
check_error("Expected real, got integer", "<validation>", 1, 1, 1);
|
||||
|
||||
if(!json_unpack_ex(j2, &error, 0, "F"))
|
||||
fail("json_unpack failed to catch invalid type");
|
||||
check_error("Expected real or integer, got string", "<validation>", 1, 1, 1);
|
||||
|
||||
if(!json_unpack_ex(j, &error, 0, "[i]"))
|
||||
fail("json_unpack failed to catch invalid type");
|
||||
check_error("Expected array, got integer", "<validation>", 1, 1, 1);
|
||||
|
||||
if(!json_unpack_ex(j, &error, 0, "{si}", "foo"))
|
||||
fail("json_unpack failed to catch invalid type");
|
||||
check_error("Expected object, got integer", "<validation>", 1, 1, 1);
|
||||
|
||||
json_decref(j);
|
||||
json_decref(j2);
|
||||
|
||||
/* Array index out of range */
|
||||
j = json_pack("[i]", 1);
|
||||
if(!json_unpack_ex(j, &error, 0, "[ii]", &i1, &i2))
|
||||
fail("json_unpack failed to catch index out of array bounds");
|
||||
check_error("Array index 1 out of range", "<validation>", 1, 3, 3);
|
||||
json_decref(j);
|
||||
|
||||
/* NULL object key */
|
||||
j = json_pack("{si}", "foo", 42);
|
||||
if(!json_unpack_ex(j, &error, 0, "{si}", NULL, &i1))
|
||||
fail("json_unpack failed to catch null string pointer");
|
||||
check_error("NULL object key", "<args>", 1, 2, 2);
|
||||
json_decref(j);
|
||||
|
||||
/* Object key not found */
|
||||
j = json_pack("{si}", "foo", 42);
|
||||
if(!json_unpack_ex(j, &error, 0, "{si}", "baz", &i1))
|
||||
fail("json_unpack failed to catch null string pointer");
|
||||
check_error("Object item not found: baz", "<validation>", 1, 3, 3);
|
||||
json_decref(j);
|
||||
|
||||
/*
|
||||
* Strict validation
|
||||
*/
|
||||
|
||||
j = json_pack("[iii]", 1, 2, 3);
|
||||
rv = json_unpack(j, "[iii!]", &i1, &i2, &i3);
|
||||
if(rv || i1 != 1 || i2 != 2 || i3 != 3)
|
||||
fail("json_unpack array with strict validation failed");
|
||||
json_decref(j);
|
||||
|
||||
j = json_pack("[iii]", 1, 2, 3);
|
||||
if(!json_unpack_ex(j, &error, 0, "[ii!]", &i1, &i2))
|
||||
fail("json_unpack array with strict validation failed");
|
||||
check_error("1 array item(s) left unpacked", "<validation>", 1, 5, 5);
|
||||
json_decref(j);
|
||||
|
||||
/* Like above, but with JSON_STRICT instead of '!' format */
|
||||
j = json_pack("[iii]", 1, 2, 3);
|
||||
if(!json_unpack_ex(j, &error, JSON_STRICT, "[ii]", &i1, &i2))
|
||||
fail("json_unpack array with strict validation failed");
|
||||
check_error("1 array item(s) left unpacked", "<validation>", 1, 4, 4);
|
||||
json_decref(j);
|
||||
|
||||
j = json_pack("{s:s, s:i}", "foo", "bar", "baz", 42);
|
||||
rv = json_unpack(j, "{sssi!}", "foo", &s, "baz", &i1);
|
||||
if(rv || strcmp(s, "bar") != 0 || i1 != 42)
|
||||
fail("json_unpack object with strict validation failed");
|
||||
json_decref(j);
|
||||
|
||||
/* Unpack the same item twice */
|
||||
j = json_pack("{s:s, s:i}", "foo", "bar", "baz", 42);
|
||||
if(!json_unpack_ex(j, &error, 0, "{s:s,s:s!}", "foo", &s, "foo", &s))
|
||||
fail("json_unpack object with strict validation failed");
|
||||
check_error("1 object item(s) left unpacked", "<validation>", 1, 10, 10);
|
||||
json_decref(j);
|
||||
|
||||
j = json_pack("[i,{s:i,s:n},[i,i]]", 1, "foo", 2, "bar", 3, 4);
|
||||
if(json_unpack_ex(j, NULL, JSON_STRICT | JSON_VALIDATE_ONLY,
|
||||
"[i{sisn}[ii]]", "foo", "bar"))
|
||||
fail("json_unpack complex value with strict validation failed");
|
||||
json_decref(j);
|
||||
|
||||
/* ! and * must be last */
|
||||
j = json_pack("[ii]", 1, 2);
|
||||
if(!json_unpack_ex(j, &error, 0, "[i!i]", &i1, &i2))
|
||||
fail("json_unpack failed to catch ! in the middle of an array");
|
||||
check_error("Expected ']' after '!', got 'i'", "<format>", 1, 4, 4);
|
||||
|
||||
if(!json_unpack_ex(j, &error, 0, "[i*i]", &i1, &i2))
|
||||
fail("json_unpack failed to catch * in the middle of an array");
|
||||
check_error("Expected ']' after '*', got 'i'", "<format>", 1, 4, 4);
|
||||
json_decref(j);
|
||||
|
||||
j = json_pack("{sssi}", "foo", "bar", "baz", 42);
|
||||
if(!json_unpack_ex(j, &error, 0, "{ss!si}", "foo", &s, "baz", &i1))
|
||||
fail("json_unpack failed to catch ! in the middle of an object");
|
||||
check_error("Expected '}' after '!', got 's'", "<format>", 1, 5, 5);
|
||||
|
||||
if(!json_unpack_ex(j, &error, 0, "{ss*si}", "foo", &s, "baz", &i1))
|
||||
fail("json_unpack failed to catch ! in the middle of an object");
|
||||
check_error("Expected '}' after '*', got 's'", "<format>", 1, 5, 5);
|
||||
json_decref(j);
|
||||
|
||||
/* Error in nested object */
|
||||
j = json_pack("{s{snsn}}", "foo", "bar", "baz");
|
||||
if(!json_unpack_ex(j, &error, 0, "{s{sn!}}", "foo", "bar"))
|
||||
fail("json_unpack nested object with strict validation failed");
|
||||
check_error("1 object item(s) left unpacked", "<validation>", 1, 7, 7);
|
||||
json_decref(j);
|
||||
|
||||
/* Error in nested array */
|
||||
j = json_pack("[[ii]]", 1, 2);
|
||||
if(!json_unpack_ex(j, &error, 0, "[[i!]]", &i1))
|
||||
fail("json_unpack nested array with strict validation failed");
|
||||
check_error("1 array item(s) left unpacked", "<validation>", 1, 5, 5);
|
||||
json_decref(j);
|
||||
|
||||
return 0;
|
||||
}
|
||||
55
test/suites/api/util.h
Normal file
55
test/suites/api/util.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <jansson.h>
|
||||
|
||||
#define failhdr fprintf(stderr, "%s:%s:%d: ", __FILE__, __FUNCTION__, __LINE__)
|
||||
|
||||
#define fail(msg) \
|
||||
do { \
|
||||
failhdr; \
|
||||
fprintf(stderr, "%s\n", msg); \
|
||||
exit(1); \
|
||||
} while(0)
|
||||
|
||||
/* Assumes json_error_t error */
|
||||
#define check_error(text_, source_, line_, column_, position_) \
|
||||
do { \
|
||||
if(strcmp(error.text, text_) != 0) { \
|
||||
failhdr; \
|
||||
fprintf(stderr, "text: \"%s\" != \"%s\"\n", error.text, text_); \
|
||||
exit(1); \
|
||||
} \
|
||||
if(strcmp(error.source, source_) != 0) { \
|
||||
failhdr; \
|
||||
\
|
||||
fprintf(stderr, "source: \"%s\" != \"%s\"\n", error.source, source_); \
|
||||
exit(1); \
|
||||
} \
|
||||
if(error.line != line_) { \
|
||||
failhdr; \
|
||||
fprintf(stderr, "line: %d != %d\n", error.line, line_); \
|
||||
exit(1); \
|
||||
} \
|
||||
if(error.column != column_) { \
|
||||
failhdr; \
|
||||
fprintf(stderr, "column: %d != %d\n", error.column, column_); \
|
||||
exit(1); \
|
||||
} \
|
||||
if(error.position != position_) { \
|
||||
failhdr; \
|
||||
fprintf(stderr, "position: %d != %d\n", error.position, position_); \
|
||||
exit(1); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#endif
|
||||
1
test/suites/encoding-flags/array/input
Normal file
1
test/suites/encoding-flags/array/input
Normal file
@@ -0,0 +1 @@
|
||||
[1, 2]
|
||||
1
test/suites/encoding-flags/array/output
Normal file
1
test/suites/encoding-flags/array/output
Normal file
@@ -0,0 +1 @@
|
||||
[1, 2]
|
||||
1
test/suites/encoding-flags/compact-array/env
Normal file
1
test/suites/encoding-flags/compact-array/env
Normal file
@@ -0,0 +1 @@
|
||||
export JSON_COMPACT=1
|
||||
1
test/suites/encoding-flags/compact-array/input
Normal file
1
test/suites/encoding-flags/compact-array/input
Normal file
@@ -0,0 +1 @@
|
||||
[1, 2]
|
||||
1
test/suites/encoding-flags/compact-array/output
Normal file
1
test/suites/encoding-flags/compact-array/output
Normal file
@@ -0,0 +1 @@
|
||||
[1,2]
|
||||
1
test/suites/encoding-flags/compact-object/env
Normal file
1
test/suites/encoding-flags/compact-object/env
Normal file
@@ -0,0 +1 @@
|
||||
export JSON_COMPACT=1
|
||||
1
test/suites/encoding-flags/compact-object/input
Normal file
1
test/suites/encoding-flags/compact-object/input
Normal file
@@ -0,0 +1 @@
|
||||
{"a": 1, "b": 2}
|
||||
1
test/suites/encoding-flags/compact-object/output
Normal file
1
test/suites/encoding-flags/compact-object/output
Normal file
@@ -0,0 +1 @@
|
||||
{"a":1,"b":2}
|
||||
1
test/suites/encoding-flags/ensure-ascii/env
Normal file
1
test/suites/encoding-flags/ensure-ascii/env
Normal file
@@ -0,0 +1 @@
|
||||
export JSON_ENSURE_ASCII=1
|
||||
8
test/suites/encoding-flags/ensure-ascii/input
Normal file
8
test/suites/encoding-flags/ensure-ascii/input
Normal file
@@ -0,0 +1,8 @@
|
||||
[
|
||||
"foo",
|
||||
"å ä ö",
|
||||
"foo åä",
|
||||
"åä foo",
|
||||
"å foo ä",
|
||||
"clef g: 𝄞"
|
||||
]
|
||||
1
test/suites/encoding-flags/ensure-ascii/output
Normal file
1
test/suites/encoding-flags/ensure-ascii/output
Normal file
@@ -0,0 +1 @@
|
||||
["foo", "\u00e5 \u00e4 \u00f6", "foo \u00e5\u00e4", "\u00e5\u00e4 foo", "\u00e5 foo \u00e4", "clef g: \ud834\udd1e"]
|
||||
1
test/suites/encoding-flags/indent-array/env
Normal file
1
test/suites/encoding-flags/indent-array/env
Normal file
@@ -0,0 +1 @@
|
||||
export JSON_INDENT=4
|
||||
1
test/suites/encoding-flags/indent-array/input
Normal file
1
test/suites/encoding-flags/indent-array/input
Normal file
@@ -0,0 +1 @@
|
||||
[1, 2]
|
||||
4
test/suites/encoding-flags/indent-array/output
Normal file
4
test/suites/encoding-flags/indent-array/output
Normal file
@@ -0,0 +1,4 @@
|
||||
[
|
||||
1,
|
||||
2
|
||||
]
|
||||
2
test/suites/encoding-flags/indent-compact-array/env
Normal file
2
test/suites/encoding-flags/indent-compact-array/env
Normal file
@@ -0,0 +1,2 @@
|
||||
export JSON_INDENT=4
|
||||
export JSON_COMPACT=1
|
||||
1
test/suites/encoding-flags/indent-compact-array/input
Normal file
1
test/suites/encoding-flags/indent-compact-array/input
Normal file
@@ -0,0 +1 @@
|
||||
[1, 2]
|
||||
4
test/suites/encoding-flags/indent-compact-array/output
Normal file
4
test/suites/encoding-flags/indent-compact-array/output
Normal file
@@ -0,0 +1,4 @@
|
||||
[
|
||||
1,
|
||||
2
|
||||
]
|
||||
2
test/suites/encoding-flags/indent-compact-object/env
Normal file
2
test/suites/encoding-flags/indent-compact-object/env
Normal file
@@ -0,0 +1,2 @@
|
||||
export JSON_INDENT=4
|
||||
export JSON_COMPACT=1
|
||||
1
test/suites/encoding-flags/indent-compact-object/input
Normal file
1
test/suites/encoding-flags/indent-compact-object/input
Normal file
@@ -0,0 +1 @@
|
||||
{"a": 1, "b": 2}
|
||||
4
test/suites/encoding-flags/indent-compact-object/output
Normal file
4
test/suites/encoding-flags/indent-compact-object/output
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"a":1,
|
||||
"b":2
|
||||
}
|
||||
1
test/suites/encoding-flags/indent-object/env
Normal file
1
test/suites/encoding-flags/indent-object/env
Normal file
@@ -0,0 +1 @@
|
||||
export JSON_INDENT=4
|
||||
1
test/suites/encoding-flags/indent-object/input
Normal file
1
test/suites/encoding-flags/indent-object/input
Normal file
@@ -0,0 +1 @@
|
||||
{"a": 1, "b": 2}
|
||||
4
test/suites/encoding-flags/indent-object/output
Normal file
4
test/suites/encoding-flags/indent-object/output
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"a": 1,
|
||||
"b": 2
|
||||
}
|
||||
1
test/suites/encoding-flags/object/input
Normal file
1
test/suites/encoding-flags/object/input
Normal file
@@ -0,0 +1 @@
|
||||
{"a": 1, "b": 2}
|
||||
1
test/suites/encoding-flags/object/output
Normal file
1
test/suites/encoding-flags/object/output
Normal file
@@ -0,0 +1 @@
|
||||
{"a": 1, "b": 2}
|
||||
1
test/suites/encoding-flags/preserve-order/env
Normal file
1
test/suites/encoding-flags/preserve-order/env
Normal file
@@ -0,0 +1 @@
|
||||
export JSON_PRESERVE_ORDER=1
|
||||
1
test/suites/encoding-flags/preserve-order/input
Normal file
1
test/suites/encoding-flags/preserve-order/input
Normal file
@@ -0,0 +1 @@
|
||||
{"foo": 1, "bar": 2, "asdf": 3, "deadbeef": 4, "badc0ffee": 5, "qwerty": 6}
|
||||
1
test/suites/encoding-flags/preserve-order/output
Normal file
1
test/suites/encoding-flags/preserve-order/output
Normal file
@@ -0,0 +1 @@
|
||||
{"foo": 1, "bar": 2, "asdf": 3, "deadbeef": 4, "badc0ffee": 5, "qwerty": 6}
|
||||
32
test/suites/encoding-flags/run
Executable file
32
test/suites/encoding-flags/run
Executable file
@@ -0,0 +1,32 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
|
||||
#
|
||||
# Jansson is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
|
||||
is_test() {
|
||||
test -d $test_path
|
||||
}
|
||||
|
||||
run_test() {
|
||||
(
|
||||
if [ -f $test_path/env ]; then
|
||||
. $test_path/env
|
||||
fi
|
||||
$json_process <$test_path/input >$test_log/stdout 2>$test_log/stderr
|
||||
)
|
||||
valgrind_check $test_log/stderr || return 1
|
||||
cmp -s $test_path/output $test_log/stdout
|
||||
}
|
||||
|
||||
show_error() {
|
||||
valgrind_show_error && return
|
||||
|
||||
echo "EXPECTED OUTPUT:"
|
||||
nl -bn $test_path/output
|
||||
echo "ACTUAL OUTPUT:"
|
||||
nl -bn $test_log/stdout
|
||||
}
|
||||
|
||||
. $top_srcdir/test/scripts/run-tests.sh
|
||||
1
test/suites/encoding-flags/sort-keys/env
Normal file
1
test/suites/encoding-flags/sort-keys/env
Normal file
@@ -0,0 +1 @@
|
||||
export JSON_SORT_KEYS=1
|
||||
1
test/suites/encoding-flags/sort-keys/input
Normal file
1
test/suites/encoding-flags/sort-keys/input
Normal file
@@ -0,0 +1 @@
|
||||
{"foo": 1, "bar": 2, "baz": 3, "quux": 4}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user