429 Commits
v2.1 ... v2.8

Author SHA1 Message Date
Petri Lehtinen
012c5f0eca jansson 2.8 2016-08-30 21:09:28 +03:00
Petri Lehtinen
a931aace16 jansson 2.8 2016-08-26 20:35:52 +03:00
Petri Lehtinen
14573dc920 Merge pull request #293 from akheron/object-insertion-order
Always preserve insertion order of object items
2016-08-25 20:20:34 +03:00
Petri Lehtinen
71594af7d5 Merge pull request #298 from pasiopou/oom-crash
OOM crash
2016-08-11 22:15:01 +03:00
Andreas Pasiopoulos
6a4b3f878d Conform to c89 2016-08-11 18:51:29 +03:00
Andreas Pasiopoulos
86fdf76f79 Check the allocation was successful before freeing existing hashtable buckets
and increasing hashtable order

Fixes a crash observed when there is OOM in hashtable_do_rehash
2016-08-11 17:48:29 +03:00
Andreas Pasiopoulos
835290dfdf Add a test case for OOM while allocating memory 2016-08-11 17:18:46 +03:00
Petri Lehtinen
7daffabf07 Fix a typo 2016-06-24 06:55:49 +03:00
Petri Lehtinen
9df267054f Always preserve insertion order of object items 2016-06-20 21:10:23 +03:00
Petri Lehtinen
8f067962f6 Merge pull request #290 from Thynix/documentation
Polish API documentation
2016-06-20 07:29:29 +03:00
Steve Dougherty
603fdc9154 doc: fix code block formatting 2016-06-17 10:45:47 -04:00
Petri Lehtinen
520340998f Merge pull request #289 from akheron/simplify-snprintf-checks
Simplify snprintf and vsnprintf checking for Visual Studio
2016-06-03 12:29:55 +03:00
Petri Lehtinen
2d710d832f appveyor.yml: Build on all Visual Studio versions 2016-06-03 12:08:36 +03:00
Steve Dougherty
a8c834c882 doc: improve conciseness
The object_to_key_iter() example is now formatted like
json_object_foreach() and json_object_foreach(). The edited descriptions were
redundant, and the removed headers didn't add useful information.
2016-06-01 11:27:22 -04:00
Steve Dougherty
7438cc8ba8 doc: correct argument names
The function signatures use "json", not "root".
2016-06-01 11:27:22 -04:00
Petri Lehtinen
72fd2fec4c Simplify snprintf and vsnprintf checking for Visual Studio 2016-05-30 11:51:39 +03:00
Petri Lehtinen
762b299e71 Fix subnormal number parsing on mingw32
Patch by Blanca Elio Rosario.
2016-05-17 11:31:55 +03:00
Petri Lehtinen
013c3892c3 Merge pull request #284 from dev-zzo/issue-282
Don't crash on deeply nested values by limiting parser recursion depth
2016-05-03 13:07:46 +03:00
Dmitry Janushkevich
64ce0ad373 Fix for issue #282
The fix limits recursion depths when parsing arrays and objects.
The limit is configurable via the `JSON_PARSER_MAX_DEPTH` setting
within `jansson_config.h` and is set by default to 2048.

Update the RFC conformance document to note the limit; the RFC
allows limits to be set by the implementation so nothing has
actually changed w.r.t. conformance state.

Reported by Gustavo Grieco.
2016-05-03 10:22:06 +02:00
Petri Lehtinen
087ed94c45 Change readthedocs domain 2016-04-28 07:04:00 +03:00
Petri Lehtinen
006638a6a2 Merge pull request #275 from Mephistophiles/master
Fix proto in doc
2016-03-03 07:26:20 +02:00
Maxim Zhukov
130c2fff31 Fix proto in doc 2016-03-02 17:40:49 +03:00
Petri Lehtinen
cfd817895a doc: Fix a typo 2016-02-25 07:29:28 +02:00
Petri Lehtinen
ac97e0bf79 json_pack(): Document s?, o?, O? 2016-02-25 07:23:53 +02:00
Petri Lehtinen
1927eeb4b4 Merge pull request #270 from Alteous/master
Implemented json_pack() format specifiers 's?', 'o?', and 'O?'.
2016-02-25 07:13:04 +02:00
David Harvey-Macaulay
c8361441fe Revised commit. 2016-02-23 19:33:52 +00:00
David Harvey-Macaulay
107cfe9499 Implemented json_pack() format specifiers 's?', 'o?', and 'O?'. 2016-02-23 19:28:56 +00:00
David Harvey-Macaulay
811965b475 Implemented json_pack() format specifiers 's?', 'o?', and 'O?'. 2016-02-22 21:32:20 +00:00
Petri Lehtinen
e08101704c Merge pull request #268 from wingyplus/fix-libdir-pkg-config
Fix libdir is wrong path when compile with cmake
2016-02-11 07:29:57 +02:00
Petri Lehtinen
c17be5870b Merge pull request #267 from Mephistophiles/master
Optimized functions
2016-02-11 07:26:00 +02:00
Thanabodee Charoenpiriyakij
9d71d006cc Fix libdir is wrong when compile with cmake
because JANSSON_INSTALL_LIB_DIR is set to /usr/local/lib after
foreach loop
2016-02-10 18:46:43 +07:00
Maxim Zhukov
b698ca13de [Optimized functions]
Optimized json_equal, json_delete, json_copy, json_deep_copy
Replaced if to switch
Removed unnecessary check for NULL.
2016-02-03 17:34:54 +03:00
Petri Lehtinen
0f50bb10b6 Fix a typo 2015-12-23 08:18:27 +02:00
Petri Lehtinen
e6b60da043 Update CHANGES 2015-12-23 08:17:18 +02:00
Petri Lehtinen
89f4c15e6d Change doc version to 2.8-dev
It's shown in the front page of readthedocs/latest.
2015-12-23 07:40:58 +02:00
Petri Lehtinen
1c2e707b34 Fix a comment 2015-12-23 07:40:02 +02:00
Petri Lehtinen
52015cf35c Merge pull request #263 from wking/display-first-unrecognized-key
pack_unpack: List first unrecognized key in strict unpacking
2015-12-23 07:38:03 +02:00
W. Trevor King
1d513d063a pack_unpack: List unrecognized keys in strict unpacking
Otherwise figuring out what's wrong with your JSON can be tricky,
especially if you're using a single fmt string to validate a large,
complicated schema.

The comma delimiting will make separating keys that contain commas
difficult.  For example:

  {"foo, bar": true, "baz": false}

will generate errors like:

  2 object item(s) left unpacked: foo, bar, baz

but that seems like a small enough corner case to not be worth much
worrying.

I wanted to find a way to handle this without have_unrecognized_keys,
but the strbuffer tooling makes it look like I shouldn't be reaching
in to do things like:

  strbuffer_t unrecognized_keys;
  unrecognized_keys.value = NULL;

and then using 'unrecognized_keys.value == NULL' in place of
have_unrecognized_keys.
2015-12-22 10:05:31 -08:00
Petri Lehtinen
581d5b899c Add versionadded to json_get_alloc_funcs doc 2015-12-22 13:05:28 +02:00
Petri Lehtinen
4f49c07781 Merge pull request #264 from npmccallum/master
Add json_get_alloc_funcs() to allow alloc function fetching
2015-12-22 13:01:36 +02:00
Nathaniel McCallum
245e532934 Add json_get_alloc_funcs() to allow alloc function fetching
This is particularly useful in modular situations where the allocation
functions are either unknown or private. For instance, in such cases,
the caller of json_dumps() has no way to free the returned buffer.
2015-12-21 11:46:32 -05:00
Petri Lehtinen
e44b2231b5 doc: Fix a broken link
Fixes #258
2015-10-20 10:21:08 +03:00
Petri Lehtinen
40bd71f064 Merge pull request #257 from denis2342/master
changed code from while to do/while
2015-10-14 08:54:37 +03:00
Denis Ahrens
067dc50efe changed code from while to do/while 2015-10-12 16:11:56 +02:00
Petri Lehtinen
4876bda857 Merge pull request #255 from gonzzor/snprintf_dump.c
Improve usage of snprintf in dump.c
2015-10-05 15:43:45 +03:00
Jonas Jonsson
e89538f685 Format %x expects unsigned int, not int
Be more explicit that a cast is happening.
2015-10-05 14:00:44 +02:00
Jonas Jonsson
f7331c7194 Use sizeof(seq) instead of magic number 13
Be more future proof by keeping the number of magic constants low.
2015-10-05 13:59:09 +02:00
Petri Lehtinen
02464862ed Merge pull request #248 from phoenix0428/patch-1
Update test_memory_funcs.c
2015-09-08 21:13:08 +03:00
Petri Lehtinen
db0213ae56 Merge pull request #252 from OlehKulykov/master
Buildable with MS Compiler version  >= 1900
2015-09-01 21:13:25 +03:00
OlehKulykov
8f2298bad8 Buildable with MS Compiler version >= 1900 2015-08-31 23:43:29 +02:00
Petri Lehtinen
4c4f692bd6 Add json_object_foreach_safe
Fixes #230.
2015-08-26 17:32:55 +03:00
Kinam Kim
875b78dc97 Update test_memory_funcs.c 2015-08-13 16:44:31 +09:00
Petri Lehtinen
fef27e6d3e Merge branch '2.7' 2015-05-19 08:54:13 +03:00
Petri Lehtinen
1dab656dee doc: Copying doesn't preserve key insertion order
Fixes #237.
2015-05-19 08:54:09 +03:00
Petri Lehtinen
69678aaa35 doc: Copying doesn't preserve key insertion order
Fixes #237.
2015-05-19 08:49:10 +03:00
Petri Lehtinen
d384acd706 Merge pull request #234 from flok99/master
Optimized dump_indent to reduce the number of fwrite calls.
2015-05-04 09:00:26 +03:00
Folkert van Heusden
5d42e1520a Optimized dump_indent to reduce the number of fwrite calls. 2015-05-01 15:18:53 +02:00
Petri Lehtinen
d8753db4ac Merge pull request #233 from haldean/master
Use snprintf and strncpy in place of sprintf and strcpy

OpenBSD linker nags about using sprintf and strcpy.
2015-04-30 10:43:39 +03:00
Haldean
95dd927857 use snprintf and strncpy in place of sprintf and strcpy
This is both good practice and nice for OpenBSD users, who will no
longer get the nag message to not use sprintf/strcpy every time they
link against jansson. It's worth noting that the existing code seems
safe to me - bounds checks were already happening before the actual
calls - and that this is for extra security.
2015-04-30 00:11:25 -07:00
Petri Lehtinen
76760011ff Don't crash when building documentation man pages
Fixes #207.
2015-03-07 09:14:06 +02:00
Petri Lehtinen
11813f4128 Fix a use after free 2015-02-07 07:36:29 +02:00
Petri Lehtinen
8b1bdcacb7 Merge pull request #221 from lioncash/test
test_object: Fix a duplicate conditional check in test_iterators
2015-02-05 09:28:43 +02:00
Lioncash
c242b46016 test_object: Fix a duplicate conditional check in test_iterators 2015-02-05 01:26:13 -05:00
Petri Lehtinen
58c188e1d5 Merge pull request #217 from JoakimSoderberg/simple_parse_cmake
Compile simple_parse example for CMake project.
2015-01-14 08:05:51 +02:00
Petri Lehtinen
ffc18128f4 Merge pull request #216 from JoakimSoderberg/infinity_warning
Disable warning for deliberate use on MSVS.
2015-01-14 08:04:37 +02:00
Joakim Soderberg
6a38d0d431 Disable warning for deliberate use on MSVS.
Disable "warning C4756: overflow in constant arithmetic" when deliberately
triggering it in a test using infinity.
2015-01-13 16:30:00 +01:00
Joakim Soderberg
d0a8ad4c06 Compile simple_parse example for CMake project. 2015-01-13 16:17:48 +01:00
Petri Lehtinen
c244b1483e examples/README.rst: Fix formatting 2014-12-30 08:56:07 +02:00
Petri Lehtinen
fc83f10c85 Add Makefile.am for examples 2014-12-30 08:50:33 +02:00
Petri Lehtinen
8d561cd94e examples/simple_parse.c: Reindent, fix warnings & style 2014-12-30 08:50:33 +02:00
Petri Lehtinen
970c6988a5 Merge pull request #214 from rdpoor/master
adding simple-parse example
2014-12-30 08:33:34 +02:00
Robert Poor
220dcb7be3 use fgets() rather than getline() 2014-12-29 06:22:19 -08:00
Robert Poor
48e0488f07 adding simple-parse example 2014-12-26 10:49:15 -08:00
Petri Lehtinen
890760b2fb Increase test coverage 2014-12-19 08:35:46 +02:00
Petri Lehtinen
15653c47dd Add a test case for \u0000 2014-12-19 07:57:33 +02:00
Petri Lehtinen
5508ab403d Honor JSON_DECODE_INT_AS_REAL at lexical stage
This has the consequence that numbers are never converted to integers
when JSON_DECODE_INT_AS_REAL is set, and thus it works correctly all
integers that are representable as double.

Fixes #212.
2014-12-18 15:16:14 +02:00
Petri Lehtinen
d799ee11b4 doc: Update the Getting Started chapter
- It's not true anymore that there are no configure options that modify
  Jansson's behaviour

- Remove trailing whitespace
2014-12-18 14:47:08 +02:00
Petri Lehtinen
abaae7630e Make it possible to set initial hashtable size
Fixes #213.
2014-12-18 14:43:44 +02:00
Petri Lehtinen
5c1d87592a wip: add tests 2014-12-15 08:30:59 +02:00
Petri Lehtinen
5885035f5f Add coveralls.io badge to README.rst 2014-12-15 08:14:57 +02:00
Petri Lehtinen
dee4a7c29e Merge pull request #211 from JoakimSoderberg/coveralls
Add support for coverage/coveralls.io in cmake project.
2014-12-15 08:09:13 +02:00
david bigagli
82a55ef205 doc patch for jansson 2014-12-15 07:47:19 +02:00
Joakim Söderberg
19f33c0e71 Add support for coverage/coveralls.io in cmake project.
This adds support for http://coveralls.io/ to the cmake project. This can then be run via a new Travis job, which uploads json containing the coverage data to the website.

To use this, please login usin github at http://coveralls.io/ and enable the Jansson project. You can then get a nice percentage badge for code coverage after each Travis buid. Coveralls will also comment on pull request with coverage info.

To test and run it locally do:

```bash
$ mkdir build && cd build
$ cmake -DJANSSON_COVERALLS=ON -DCMAKE_BUILD_TYPE=Debug ..
$ cmake --build .                      # $ make
$ cmake --build . --target coveralls   # $ make coveralls
```

There is also another script that generates a local HTML page using lcov CodeCoverage.cmake which can be run using

```bash
$ make coverage
```

The required depdencies to run this are:
gcov
curl
lcov (is needed for the normal CodeCoverage script)
2014-12-12 23:57:01 +01:00
Petri Lehtinen
3c9e5c9925 Move the list member to the top of hashtable_pair struct
This may make debugging easier with limited debuggers.
2014-12-10 07:41:41 +02:00
Petri Lehtinen
1c38ab17f5 Include jansson_config.h with quotes
Fixes #209.
2014-12-04 10:45:26 +02:00
Petri Lehtinen
d5edfcc6fd Merge pull request #208 from skeid21/master
Fix warnings on LLVM 6.0 targetion iOS arm64.
2014-11-29 11:52:11 +02:00
Shawn Harris
9b435df3d4 Warnings - use size_t where appropriate to prevent warning when compilation is targeting 64 bit 2014-11-28 18:11:26 -07:00
Petri Lehtinen
bc743ad2d9 README: Documentation is hosted at RTD 2014-10-20 20:27:56 +03:00
Petri Lehtinen
19cc800ad3 Merge pull request #206 from chrullrich/fix-win-build
Fix CMake configuration for Windows build
2014-10-14 12:28:19 +03:00
Christian Ullrich
b52e7a69aa Fix CFLAGS for static-CRT build.
Setting the variables to only "/MT(d)" resets all other flags to their defaults, so the debug build used release flags.
2014-10-13 13:06:02 +02:00
Christian Ullrich
1395e4303a Fix static CRT selection. 2014-10-13 13:04:04 +02:00
Petri Lehtinen
d7a6269a17 Use expr instead of $((...)) in shell scripts
For Solaris 10 compatibility.
2014-10-03 08:49:08 +03:00
Petri Lehtinen
7fbe7c3960 Don't use the nonstandard __FUNCTION__ macro 2014-10-03 08:38:57 +03:00
Petri Lehtinen
ee27b7e3dd jansson 2.7 2014-10-02 13:10:45 +03:00
Petri Lehtinen
391d9101a8 Update CHANGES for 2.7 2014-10-02 08:57:07 +03:00
Petri Lehtinen
2137e0c895 Add AppVeyor badge to README.rst 2014-09-25 07:51:55 +03:00
Petri Lehtinen
5fc44e10aa Merge pull request #205 from JoakimSoderberg/appveyor
Added Windows CI support with appveyor
2014-09-25 07:45:24 +03:00
Joakim Soderberg
b94d767f86 Added Windows CI support with appveyor
Added support for http://www.appveyor.com/ (login via github account).

This will build the project and run the unit tests on Windows as well.
Possible to add a badge like with Travis in the README.md as well.
2014-09-24 18:08:45 +02:00
Petri Lehtinen
43f17d010a Merge pull request #204 from bgoglin/master
Don't use GNU-make specific export for global AM_CFLAGS
2014-08-27 08:02:59 +03:00
Petri Lehtinen
fa20e80860 Parse subnormal numbers correctly
Fixes #202.
2014-08-27 07:59:52 +03:00
Brice Goglin
485c7640a1 Don't use GNU-make specific export for global AM_CFLAGS
Just define it at configure time, it's automatically set in all makefiles.

Fixes #203.
2014-08-26 15:01:13 +02:00
Petri Lehtinen
d4a7de7c11 Android.mk: Add -DHAVE_STDINT_H to LOCAL_CFLAGS
See #200.
2014-07-31 07:50:31 +03:00
Petri Lehtinen
a6229a2d3e doc: Enhance string overview 2014-07-21 08:15:34 +03:00
Petri Lehtinen
abd151f5d7 Merge pull request #198 from Smilex/mem_fix
Fixed cases where file would be opened, but not closed.
2014-07-21 08:09:18 +03:00
Ian T. Jacobsen
4f1238af71 Fix whitespace issues 2014-07-16 00:21:16 +01:00
Ian T. Jacobsen
122a1e2af9 Fixed cases where file would be opened, but not closed. And small realloc memory issue. 2014-07-16 00:16:32 +01:00
Petri Lehtinen
c8d017bd88 Merge branch '2.6' 2014-07-12 06:43:07 +03:00
Petri Lehtinen
23b1b7ba9a Document JANSSON_BUILD_SHARED_LIBS CMake option
Fixes #187.
2014-07-12 06:42:36 +03:00
Petri Lehtinen
88943b64e3 Define JSON_MAX_INDENT
Fixes #191.
2014-07-07 11:34:50 +03:00
Petri Lehtinen
348401e7b8 CMake: Use add_definitions for compiler flags
Fixes #193.
2014-07-02 22:04:34 +03:00
Petri Lehtinen
df248712a5 Include android config in tarballs
Issue #166.
2014-06-28 22:41:48 +03:00
Petri Lehtinen
de1b5db70e Fix autoreconf on Debian
For some reason, Automake doesn't search the current directory correctly
when searching for helper scripts.

Fixes #182.
2014-05-14 16:03:22 +03:00
Petri Lehtinen
4debe8e567 Add missing tests for JSON_REAL_PRECISION 2014-05-07 08:19:39 +03:00
Petri Lehtinen
28d362cf28 Merge pull request #180 from Sannis/master
Enable usage of AddressSanitizer to check Jansson
2014-05-05 14:45:09 +03:00
Oleg Efimov
541cdf5960 Remove unneeded undef 2014-05-05 15:40:41 +04:00
Oleg Efimov
fc6e314498 Enable usage of AddressSanitizer to check Jansson 2014-05-04 00:34:08 +04:00
Petri Lehtinen
17b5fdd94b Add JSON_REAL_PRECISION
Fixes #178.
2014-04-30 12:46:34 +03:00
Petri Lehtinen
5b88cc5ded Merge branch '2.6' 2014-04-30 12:06:03 +03:00
Petri Lehtinen
db285b3764 Check uint16_t and uint8_t support correctly
Fixes #177.
2014-04-30 12:05:51 +03:00
Petri Lehtinen
a425f8c650 Rename variables to work aroung a GCC bug on Solaris
Fixes #175.
2014-04-23 12:17:14 +03:00
Petri Lehtinen
96f8916e50 Merge pull request #174 from KimJeongYeon/master
fix build error for android
2014-04-14 08:32:47 +03:00
KimJeongYeon
dd2bf2971e fix build error for android 2014-04-11 23:14:34 +09:00
Petri Lehtinen
34a64cfe53 Merge pull request #171 from firepick1/master
Solaris build fix
2014-04-08 20:39:46 +03:00
Karl Lew
571617ebfc use __sun instead of sun 2014-04-08 15:57:58 +00:00
Karl Lew
3bd8a5729d Merge remote-tracking branch 'refs/remotes/origin/master' 2014-04-06 04:31:34 +00:00
Karl Lew
33e4988180 SmartOS build fix 2014-04-06 04:28:52 +00:00
firepick1
663fbfb7a4 SmartOS (Solaris) build 2014-04-04 17:28:08 -07:00
Petri Lehtinen
88aa6a9e30 Remove VS2010 project files, CMake should be used on Windows instead
Fixes #165.
2014-03-04 08:28:52 +02:00
Petri Lehtinen
92e2588d6e Merge branch '2.6' 2014-02-16 22:22:14 +02:00
Petri Lehtinen
960ead07f2 Merge pull request #164 from vincentbernat/fix/gitignore
Add new autotools-generated files to .gitignore
2014-02-16 22:20:25 +02:00
Petri Lehtinen
78da1de021 Merge pull request #163 from vincentbernat/fix/unpack-mix-optional-and-strict
Fix unpack when mixing strict mode and optional keys
2014-02-16 22:20:02 +02:00
Petri Lehtinen
946531bd7b Merge pull request #162 from nmlgc/master
Three fixes for hashtable seeding on Windows
2014-02-16 22:19:37 +02:00
Petri Lehtinen
10009d61a7 Merge pull request #164 from vincentbernat/fix/gitignore
Add new autotools-generated files to .gitignore
2014-02-16 22:10:16 +02:00
Petri Lehtinen
49fc6b1194 Merge pull request #163 from vincentbernat/fix/unpack-mix-optional-and-strict
Fix unpack when mixing strict mode and optional keys
2014-02-16 22:09:41 +02:00
Petri Lehtinen
6e8b2e161a Merge pull request #162 from nmlgc/master
Three fixes for hashtable seeding on Windows
2014-02-16 22:09:09 +02:00
Vincent Bernat
3922f84a2f Add new autotools-generated files to .gitignore
Additionally, `/jansson_private_config.h.in` is also added as well as
"build" directory for those of us who like to use an out-of-tree build
system.
2014-02-15 17:47:48 +01:00
Vincent Bernat
56a50e147d Micro-optimization for JSON_STRICT when no optional key is used
The previous commit introduced a loop on all input keys to check the
strict mode. We can avoid this if we don't expect an optional key. In
this case, we fallback to the previous method to compare the length of
the set of expected keys and the length of the parsed keys.
2014-02-15 17:44:02 +01:00
Vincent Bernat
7a0b9af662 Allow to mix JSON_STRICT with optional keys
On unpack, one may want to mix `JSON_STRICT` and optional keys by using
a format like `{s:i,s?o!}`. Unfortunately, this fails the stric test
with `-1 object item(s) left unpacked` error when the second key is not
specified.

To fix that, we iter on each key and we check if we have successfully
unpacked them. This is less efficient than the previous method but it
brings correctness.
2014-02-15 17:40:22 +01:00
nmlgc
4fbe44605b Three fixes for hashtable seeding on Windows
First, wrap "advapi32.dll" into the TEXT() macro. If UNICODE is defined,
GetModuleHandle() redirects to GetModuleHandleW(), which excepts a wchar_t*
UTF-16 parameter, thus causing a compile error. TEXT() prefixes the string
literal with L in this case, and does nothing otherwise.

Second, make sure that CryptGenRandom() is actually called through the function
pointer retrieved by the call to GetProcAddress() above.

And third, replace _getpid() with the equivalent and more ubiquitous Win32 API
function GetCurrentProcessId(). Since _getpid() is not exported by all C
runtimes on Windows (most notably the Driver Development Kit), using it might
introduce previously unneeded runtime dependencies. GetCurrentProcessId(), on
the other hand, has been available in kernel32.dll since at least Windows 95,
just like the other API functions used in this code (GetModuleHandle() and
GetProcAddress()).
2014-02-15 17:11:11 +01:00
Petri Lehtinen
ea7a77236c Merge branch '2.6' 2014-02-12 08:56:17 +02:00
Petri Lehtinen
1dc87ed5a1 Make valgrind happy 2014-02-12 08:54:26 +02:00
Petri Lehtinen
50a811ca07 Fix feature checks to use correct __ATOMIC flags 2014-02-12 08:54:26 +02:00
Petri Lehtinen
4bbb53a25d Make valgrind happy 2014-02-12 08:54:06 +02:00
Petri Lehtinen
569295fe30 Fix feature checks to use correct __ATOMIC flags 2014-02-12 08:44:29 +02:00
Petri Lehtinen
3f5acaa3fb Merge branch '2.6' 2014-02-11 15:15:00 +02:00
Petri Lehtinen
42016a35c8 Oops, ATOMIC_ACQ_REL is not a correct memmodel for __atomic_store_n 2014-02-11 15:14:47 +02:00
Petri Lehtinen
17ec22f514 Merge branch '2.6' 2014-02-11 14:49:15 +02:00
Petri Lehtinen
e83ded066a jansson 2.6 2014-02-11 09:45:30 +02:00
Petri Lehtinen
492feb26ce Document json_object_seed() 2014-02-11 09:38:45 +02:00
Petri Lehtinen
8f80c2d838 CVE-2013-6401: Change hash function, randomize hashes
Thanks to Florian Weimer and Eric Sesterhenn for reporting, reviewing
and testing.
2014-02-11 08:41:30 +02:00
Chip Salzenberg
b9c588de3d expect "export" in test configs 2014-02-11 08:41:14 +02:00
Petri Lehtinen
a721d36f41 Merge pull request #161 from JoakimSoderberg/cmake_travis
Add CMake build to Travis config.
2014-01-28 06:02:17 -08:00
Petri Lehtinen
eee85cd53c Merge pull request #160 from JoakimSoderberg/cmake_memcheck_fix
Fix so that valgrind checks actually works with CMake.
2014-01-28 06:01:03 -08:00
Joakim Soderberg
7c80778827 Fix so that valgrind checks actually works with CMake.
The valgrind tests where not run on the suites.

And valgrind was always returning 0 so set an explicit exit code on exit. I also had forgotten to change the name of TEST_WITH_VALGRIND to JANSSON_TEST_WITH_VALGRIND so that the tests would never use valgrind.
2014-01-28 13:07:59 +00:00
Joakim Soderberg
4c2cc0e36c Add CMake build to Travis config. 2014-01-28 14:06:43 +01:00
Petri Lehtinen
a630e226ba Merge pull request #159 from JoakimSoderberg/config_fix
Make sure the private config is included.
2014-01-28 01:11:57 -08:00
Joakim Soderberg
0c95a22888 Make sure the private config is included. 2014-01-28 09:59:43 +01:00
Petri Lehtinen
30fdf6067e Update copyrights for 2014 2014-01-28 09:16:05 +02:00
Petri Lehtinen
cffc5df600 Merge pull request #157 from JoakimSoderberg/cmake_config_new
Cmake config
2014-01-27 23:10:55 -08:00
Joakim Soderberg
3fe8f74e7f Renamed config.h -> jansson_private_config.h
This is because it's really easy to get a name collission if compiling
Jansson as a subproject in a larger CMake project. If one project includes
several subprojects each having their own config.h, this will cause the
wrong file to be loaded.
2014-01-27 12:48:52 +01:00
Joakim Soderberg
913937c98d Added CMake config files.
This will simplify linking against the lib, both from the build-tree and
install-tree from other CMake projects.

CMakes find_package command uses these configs to locate the exported
targets for the library.

* Also changed so that all CMake options for the project are prepended
  with JANSSON_ so that there is no ambiguity when including this as a
  subdirectory in another CMake project.
2014-01-24 14:13:02 +01:00
Petri Lehtinen
b21cd65d30 Fix CMake build flags for MinGW 2014-01-16 08:03:05 +02:00
Petri Lehtinen
c89638d73c Merge pull request #151 from nmlgc/master
Check for key == NULL in json_object_get and json_object_del
2013-12-28 02:56:35 -08:00
nmlgc
98610bfcec Check for key == NULL in json_object_get and json_object_del 2013-12-28 06:11:49 +01:00
Petri Lehtinen
9c8b3c833f Merge branch '2.5' 2013-12-27 08:55:52 +02:00
Petri Lehtinen
dc69aa797b github_commits.c: GitHub API v3 requires a User-Agent header 2013-12-27 08:55:05 +02:00
Petri Lehtinen
4e8c4bfbd2 Merge branch '2.5' 2013-12-13 09:27:42 +02:00
Petri Lehtinen
316492e4d0 Merge 2013-12-13 09:26:58 +02:00
Petri Lehtinen
3347679d8f Merge branch '2.5' 2013-12-13 09:22:50 +02:00
Petri Lehtinen
b951baec0d Include CMake specific files in release tarballs
Fixes #147.
2013-12-13 09:22:36 +02:00
Petri Lehtinen
05f7d30e5a Add json_boolean_value as alias for json_is_true
Fixes #146.
2013-12-04 09:14:19 +02:00
Petri Lehtinen
34d8b92dce Merge branch '2.5' 2013-11-14 08:49:24 +02:00
Petri Lehtinen
67a7bc7376 doc: Set all memory to zero in secure_free 2013-11-14 08:49:16 +02:00
Petri Lehtinen
30a4c88843 Fix an off-by-one error 2013-10-15 08:49:40 +03:00
Petri Lehtinen
08be94e8e6 Don't call strlen twice
Oops.
2013-10-15 08:46:17 +03:00
Petri Lehtinen
d544852ff6 Avoid integer overflows with very long strings 2013-10-15 08:44:04 +03:00
Petri Lehtinen
8dc3233f3b Merge pull request #143 from chipdude/master
paper bag bug: json_string_set* functions should set the length too
2013-10-03 22:36:34 -07:00
Chip Salzenberg
c7d479c740 in simple string tests, always check lengths too 2013-10-03 19:07:50 -07:00
Chip Salzenberg
a76dc45512 paper bag bug: json_string_set* functions should set the length too 2013-10-03 19:07:20 -07:00
Petri Lehtinen
9bb5a266dd Merge pull request #142 from chipdude/master
fix type mismatch anywhere int != int32 (Vorne)
2013-09-30 22:41:25 -07:00
Chip Salzenberg
6ebd0bc7a6 fix type mismatch anywhere int != int32 (Vorne) 2013-09-30 16:56:01 -07:00
Petri Lehtinen
f8d8d524cf Document JSON_ALLOW_NUL and clarify NUL byte handling 2013-09-30 10:55:34 +03:00
Petri Lehtinen
1bfc33362e Add JSON_ALLOW_NUL decoding flag for enabling NUL byte support 2013-09-30 10:45:02 +03:00
Petri Lehtinen
5744468c99 Clarify json_string_length doc 2013-09-30 10:45:02 +03:00
Petri Lehtinen
4d5aead31c Fix NUL byte check for object keys 2013-09-30 10:45:02 +03:00
Petri Lehtinen
78a80b8899 load.c: Change an error message 2013-09-30 09:24:36 +03:00
Petri Lehtinen
b961e8101e Add Chip's changes to release notes 2013-09-30 08:56:39 +03:00
Petri Lehtinen
7876125a22 Document s% and s# for json_pack, s% for json_unpack 2013-09-30 08:47:05 +03:00
Petri Lehtinen
e2dcf94598 Merge pull request #141 from chipdude/nuls
add new public functions to jansson.def
2013-09-29 22:31:15 -07:00
Chip Salzenberg
6c1ce27095 add new public functions to jansson.def 2013-09-29 16:57:23 -07:00
Petri Lehtinen
46e27ae6d5 Merge pull request #140 from chipdude/nuls
Support "\u0000" in strings
2013-09-29 11:57:57 -07:00
Chip Salzenberg
0be5c959da document new null-friendly functions 2013-09-27 19:10:32 -07:00
Chip Salzenberg
face43929d expect "export" in test configs 2013-09-27 17:32:25 -07:00
Chip Salzenberg
dcaa90d21e dump \u sequences as upper case for readability (just like error messages) 2013-09-27 17:32:25 -07:00
Chip Salzenberg
9c259c07aa Support \u0000 - add size_t string lengths to API, load and dump \u000, etc.
Also:
  Steal strings during parsing for half the mallocs!
  Change all input-caused assertions to errors.  No crashes please, we're programmers.
2013-09-27 17:32:06 -07:00
Petri Lehtinen
e4d6a9f6f4 Add versionadded to the new json_pack() format specifiers 2013-09-23 09:34:28 +03:00
Petri Lehtinen
641002da37 jansson 2.5 2013-09-19 21:26:46 +03:00
Petri Lehtinen
9f9c9fe410 doc: Fix index type for json_array_foreach() 2013-09-19 21:26:46 +03:00
Petri Lehtinen
e39112b29c Merge branch '2.4' 2013-09-19 20:47:33 +03:00
Petri Lehtinen
bf2584703a Mention success first in json_load_callback_t docs 2013-09-19 20:47:17 +03:00
Phillip Howell
db8ca3645f Clarified json_load_callback_t return value docs 2013-09-19 20:47:02 +03:00
Petri Lehtinen
0490907fb3 Merge branch '2.4' 2013-09-19 20:46:07 +03:00
Petri Lehtinen
c2e8fdde9d Mention success first in json_load_callback_t docs 2013-09-19 20:45:51 +03:00
Petri Lehtinen
24c67966aa Mention success first in json_load_callback_t docs 2013-09-16 08:49:38 +03:00
Phillip Howell
0747d3943f Clarified json_load_callback_t return value docs 2013-09-16 08:49:38 +03:00
Petri Lehtinen
5f9ef108d0 Merge pull request #134 from lano1106/master
Replace strcpy with memcpy
2013-08-27 22:37:44 -07:00
Olivier Langlois
21599b95f8 Replace strcpy with memcpy
Since len is known, the copy function does not need to check byte by byte
the end of the string.

Signed-off-by: Olivier Langlois <olivier@olivierlanglois.net>
2013-08-27 22:47:18 -04:00
Petri Lehtinen
3196ad48ed pack: Add format specifiers s#, + and +# 2013-08-14 21:54:11 +03:00
Petri Lehtinen
49ad5328c7 doc: Add an example for circumventing problems with Windows heaps 2013-08-06 08:12:20 +03:00
Petri Lehtinen
71676acd0b Fix memory leaks and warnings in github_commits.c 2013-07-27 21:23:34 +03:00
Petri Lehtinen
2489ea90b1 Update copyrights for 2013 2013-07-23 13:20:06 +03:00
Petri Lehtinen
11d45b0090 Fix integer overflow in jsonp_strdup()
Fixes #129.
2013-07-21 13:00:32 +03:00
Petri Lehtinen
8490e377c0 Make the argument of json_deep_copy() const
Fixes #126.
2013-07-08 12:36:56 +03:00
Petri Lehtinen
ef666519f7 Merge pull request #128 from JoakimSoderberg/lex_unget_unsave_fix
BUGFIX: Compilation error with -DNDEBUG defined.
2013-07-01 21:41:55 -07:00
Joakim Soderberg
6fe231757e BUGFIX: Compilation error with -DNDEBUG defined.
When building a "MinSizeRel" with CMake I get a compilation error in lex_unget_unsave. This is because assertions are turned off using -DNDEBUG:

```
/usr/bin/gcc  -DHAVE_CONFIG_H -fPIC -Os -DNDEBUG -Ijansson/build/include -Ijansson/build/private_include    -Wall -Wextra -Wdeclaration-after-statement -Werror -o CMakeFiles/jansson.dir/src/load.c.o   -c jansson/src/load.c
jansson/src/load.c: In function âx_unget_unsaveâjansson/src/load.c:256:14: error: variable â set but not used [-Werror=unused-but-set-variable]
cc1: all warnings being treated as errors
```

This will then remove the insert, which makes the "d" variable unused, which is treated as an error since we have -Wall set. We can't simply get rid of the variable either and put the strbuffer_pop call in the assert call, since it's a macro and would remove the call entirely. So I simply added a check for NDEBUG to fix it.
2013-06-26 09:50:46 +00:00
Petri Lehtinen
84b5bfe173 Merge pull request #127 from cryptobiote/master
Adding option for MSVC for those who are linking statically.
2013-06-25 11:30:39 -07:00
cryptobiote
d9ee5a7f1b Update CMakeLists.txt 2013-06-25 09:36:11 -06:00
cryptobiote
19a19d1952 Adding option for MSVC for those who are linking statically. 2013-06-20 11:00:40 -06:00
Petri Lehtinen
68e201add8 Merge pull request #125 from jjwchoy/master
Prevent compiler warning
2013-06-13 22:59:22 -07:00
Jason Choy
62531bd012 Prevent unused-variable warning 2013-06-13 11:37:07 +01:00
Petri Lehtinen
f639fbd2c3 Tweak the JSON_DECODE_INT_AS_REAL test introduced in #123
Only run the imprecision part if json_int_t is long long, otherwise
the imprecision cannot be tested against json_int_t.

Also, convert the double value to json_int_t before checking the
imprecision, because otherwise the json_int_t is converted to double
and the imprecision happens in the "wrong direction".
2013-06-12 08:36:53 +03:00
Petri Lehtinen
a38704df58 Fix indentation in docs 2013-06-12 08:30:28 +03:00
Petri Lehtinen
8d439710cd Merge pull request #123 from jjwchoy/decode-ignore-int
Implemented a decode option to only decode numbers to reals
2013-06-11 22:23:40 -07:00
Jason Choy
120a90a155 Using jsonp_strtod instead of casting to double to catch double overflow 2013-06-11 15:09:08 +01:00
Jason Choy
c3e9725f02 Changed :versionadded for JSON_DECODE_INT_AS_REAL docs 2013-06-11 12:31:41 +01:00
Jason Choy
02a3829363 Renamed flag to JSON_DECODE_INT_AS_REAL and added documentation 2013-06-11 12:24:28 +01:00
Petri Lehtinen
018f7fea31 Merge pull request #120 from paulharris/mine_pre_2.5
Adjust CMakeLists.txt and .def: add _d postfix onto jansson_d.lib/dll
2013-06-09 22:18:05 -07:00
Jason Choy
e6bd0aba9d Added a simple test for the JSON_DECODE_NO_INT option 2013-06-09 15:34:30 +01:00
Jason Choy
9e7f11a847 Implemented a decode option to only decode numbers to reals and never integers 2013-06-09 15:14:47 +01:00
Petri Lehtinen
042d4b2a6b Merge pull request #121 from jjwchoy/master
Added missing return valua check in test
2013-06-07 05:17:46 -07:00
Jason Choy
ddd10b5e0b Added missing rv check - caught by clang 2013-06-07 13:08:22 +01:00
Paul Harris
2fd66fab1a Adjust CMakeLists.txt and .def: add _d postfix onto jansson_d.lib/dll
Had to remove the "LIBRARY" line from jansson.def, which was optional,
and was overriding CMake's desire for a specific name.
2013-06-02 22:00:32 +08:00
Petri Lehtinen
5cc594c9e8 Don't shadow the stdlib function index() with arg names
Fixes #119.
2013-05-21 08:54:27 +03:00
Petri Lehtinen
ffb7ef4b75 Merge pull request #118 from dtgriscom/master
Added json_array_foreach(), parallelling json_object_foreach()
2013-05-12 22:16:09 -07:00
Daniel Griscom
b49280be72 Fixed "comparison between signed and unsigned" warning 2013-05-12 15:45:33 -04:00
Daniel Griscom
7121134abd Removed (non-functioning) printfs from test code 2013-05-12 15:36:09 -04:00
Daniel Griscom
e8c812b500 Added test cases and documentation for json_array_foreach() 2013-05-12 15:34:26 -04:00
Daniel Griscom
6950cd203b Add json_array_foreach() #define, parallelling json_object_foreach() 2013-05-12 15:03:26 -04:00
Petri Lehtinen
d3bd463897 Merge pull request #115 from JoakimSoderberg/cmake_pkgconfig
Cmake pkgconfig
2013-05-06 22:38:28 -07:00
Joakim Soderberg
54d86fb4a4 Create and install the pkg-config file jansson.pc for CMake.
Use the same jansson.pc.in file as the ./configure script does to generate the jansson.pc file for pkg-config. Also make an install rule for this.
2013-05-06 12:34:52 +00:00
Joakim Soderberg
42d398243d Fix the cmake Sphinx find script.
The cmake sphinx find script version parsing assumes a format of 1.2.3, but sphinx version can be 1.2b3 as well, so parse those correctly as well.
2013-05-06 12:31:29 +00:00
Andrei Epure
f675514815 jansson:doc: modified incref doc
Signed-off-by: Andrei Epure <epure.andrei@gmail.com>
2013-04-29 15:32:52 +03:00
Andrei Epure
5793142861 test:suites:api: modified error message
In the "/* perform the same update again */" area the error
message should be "unable to update an non-empty object"
instead of "unable to update an empty object".

Signed-off-by: Andrei Epure <epure.andrei@gmail.com>
2013-04-29 15:32:52 +03:00
Petri Lehtinen
b227f96c45 Merge pull request #112 from JoakimSoderberg/fpic_cmake_support
Additional CMake documentation + Fpic cmake support
2013-04-29 03:56:23 -07:00
Joakim Soderberg
0687442f5d Fixed some minor issues. 2013-04-25 13:59:26 +00:00
Joakim Soderberg
8390c90a91 Added more detailed CMake documentation.
- Also added better FindSphinx support so that we can detect the version and not  have a fatal error if it's too old.
- Added support for building latex documentation with -DBUILD_LATEX. Off by default.
- Added suppor for building man pages with -DBUILD_MAN. On by default (unless sphins is too old to support it).
2013-04-24 14:14:00 +00:00
Joakim Soderberg
3000831365 Use -fPIC when possible.
So that we can link the library statically to shared libraries.
2013-04-18 09:25:17 +00:00
Petri Lehtinen
6b1cba94e3 Document CMake build process, deprecate the VS2010 project 2013-04-15 10:31:23 +03:00
Petri Lehtinen
9591d3a155 Tweak Android build documentation 2013-04-15 10:25:32 +03:00
Petri Lehtinen
83d04ddd68 Merge branch 'master' of git://github.com/svic/jansson 2013-04-15 10:21:31 +03:00
Petri Lehtinen
70232676f7 Merge branch '2.4' 2013-04-15 09:15:35 +03:00
Petri Lehtinen
257a716073 Fix the signature again, and also rest of the errors
Next time, remember to check with -Werror first...
2013-04-15 09:15:03 +03:00
Petri Lehtinen
0ccd2c7715 Merge branch '2.4' 2013-04-15 09:09:01 +03:00
Petri Lehtinen
42e307b3c5 Fix the signature of a test function 2013-04-15 09:08:47 +03:00
Petri Lehtinen
61cba2ae50 Merge branch '2.4' 2013-04-15 08:49:47 +03:00
Petri Lehtinen
867079c820 json_dump*(): Don't crash if json is NULL and JSON_ENCODE_ANY is set 2013-04-15 08:49:09 +03:00
Garner Lee
d20751d53d Add android doc. 2013-04-11 13:32:04 -07:00
Petri Lehtinen
6281d1c549 Merge pull request #108 from Zillode/1edff47af38a2335f88300ee6dc437118f5a20ff
Fixes redefined warnings ("_GNU_SOURCE")
2013-04-02 05:40:49 -07:00
Petri Lehtinen
8fd475bf96 Merge branch '2.4' 2013-04-01 14:41:14 +03:00
Petri Lehtinen
bbd3b0ca4f Fix a typo 2013-04-01 14:41:10 +03:00
Garner Lee
7c4f323abc Create Android.mk, preconfigured jansson_config.h and CleanSpec.mk for Android-like build systems. 2013-03-27 17:31:05 -07:00
Lode Hoste
1edff47af3 Do not define _GNU_SOURCE is already defined 2013-03-14 21:34:11 +01:00
Petri Lehtinen
dc2d54d9c6 Fix a refleak in test/bin/json_process.c 2013-03-14 22:24:57 +02:00
Petri Lehtinen
263fd0c9d3 Remove trailing whitespace 2013-03-14 22:18:21 +02:00
Petri Lehtinen
6db70f0301 cmake: Add stripped tests 2013-03-14 22:16:16 +02:00
Petri Lehtinen
70f663db29 Merge branch 'rebased_cmake_patch' of git://github.com/JoakimSoderberg/jansson 2013-03-14 22:16:10 +02:00
Joakim Söderberg
59bc1f42e4 Got rid of some warnings for unused variables. 2013-03-13 00:15:37 +01:00
Joakim Söderberg
b1b4f307d5 Add back the old ENV variabe approach to json_process.
Enable using the old envrionment variable passing of settings for json_process
so that the current autoconf test suite scripts can run nicely.

json_process now takes an optional command line parameter --env which
causes it to use the old method of reading the settings from
environment variables instead of directly from the "env" file.

Also added a --strip command line option, this will be used to
run the stripped tests with CMake as well.
2013-03-13 00:09:08 +01:00
Petri Lehtinen
a3e6369105 Merge branch '2.4' 2013-03-09 21:15:22 +02:00
Petri Lehtinen
3d0d61fdaf Use json_decref() properly in tutorial 2013-03-09 21:15:09 +02:00
Joakim Soderberg
650707fccc Moved *config.h.cmake to the cmake directory. 2013-03-07 18:38:47 +01:00
Joakim Soderberg
33b0855b27 Get rid of type warning on MSVC.
We should use json_int_t instead of int, since this can be different
depending on platform. Also MSVC warns that this can cause "loss of
information" since it's converting long long to int.
2013-03-07 16:27:41 +01:00
Joakim Soderberg
7214a222c7 Skip using a debug postfix with visual studio.
The output dir is different anyway, and this just makes it more
complicated to build projects that link to this, because they need
different link rules based on the build type.
2013-03-07 16:21:35 +01:00
Joakim Söderberg
81ce127048 Compile tests by default.
- Encourage actually using the unit tests by compiling them by default.
Set the WITHOUT_TESTS variable to build without instead...

- Changed comments to keep within 80 characters.
- Don't have a hard coded path for valgrind, simply assuem it's in the path instead (an OSX ports install puts it in /opt/local/bin for instance).
- Fixed building the shared lib.
2013-03-07 16:19:56 +01:00
Joakim Söderberg
344d2b00ea Fix incorrect cast to ssize_t.
gcc 4.2.1 warns about a possible incorrect cast to ssize_t when comparing against refcount, which is of type size_t. signed vs unsigned comparison.

Sinc warnings are treated as errors (as in the autoconf project) this would cause a compile error.
2013-03-07 16:13:40 +01:00
Joakim Söderberg
52a8072727 Rename the CMakeModules directory to simply cmake.
This is more consistent with the other directories in the project.
2013-03-07 16:12:08 +01:00
Joakim Soderberg
741e6915e2 Always use LF as line ending so that we don't get any problems with line
endings in any tests.
2013-03-07 16:12:07 +01:00
Joakim Soderberg
e40d52c550 Consolidated the CMake project.
- Moved everything to one CMakeLists.txt
- Added support for the json_process test suites (instead of just the API
  tests).
- Changed to use the modified json_process version that does away with the
  environment variables (originally written by DanielT).
- Had to exclude "test_memory_funcs" on MSVC, since void pointer
  arithmetics are not allowed as it is done in secure_malloc and
  secure_free.
- Had to add a check for "ssize_t". This is not available on Windows and
  maybe on some other platforms (used in test_pack.c)
- Result from running ctest (The failure seems unrelated to CMake, it's
  just that the expected result is in a different order):
99% tests passed, 1 tests failed out of 121

Total Test time (real) =   1.31 sec

The following tests FAILED:
         24 - valid__complex-array (Failed)
2013-03-07 16:11:07 +01:00
Paul Harris
3d5bea5714 Fixes for cmake for MSVC2010 2013-03-07 16:06:38 +01:00
Paul Harris
3e03b07831 Add CMake build system.
Added multiple CMake-related files to project.
Supports building the library and the tests.
See CMakeLists.txt for notes on how it works.

I had to adjust 3 existing files in order to disable some configuration
that should be taken care of by cmake/automake anyway.

I also added jansson.def from a future jansson version,
to test cmake's support for .def files (which works fine).
2013-03-07 16:06:01 +01:00
Petri Lehtinen
e00cd4f941 Merge branch '2.4' 2013-02-05 12:51:06 +02:00
Petri Lehtinen
a1882fee02 check-exports test: Filter out symbols that start with an underscore
These are usually internal to libc, and such symbols are exported in
the shared library on some platforms.
2013-02-05 12:48:38 +02:00
Petri Lehtinen
4a5626695c Merge branch '2.4' 2013-02-04 09:55:39 +02:00
Petri Lehtinen
afa1d37e20 Enable -Werror in Travis builds 2013-02-04 09:55:34 +02:00
Petri Lehtinen
b9967fdbcf Merge branch '2.4' 2013-02-04 09:52:31 +02:00
Petri Lehtinen
ccf6e48a52 Don't use -Werror by default
It may be problematic when compilers add new warnings over time.
./configure && make should always just work.
2013-02-04 09:51:42 +02:00
Petri Lehtinen
93a3c7f663 Enable gcc specific flags in tests correctly
... and fix issues found.
2013-02-04 09:49:58 +02:00
Petri Lehtinen
5dbac70a67 Compress bz2 doc tarballs with bzip2 instead of gzip
Fixes #99.
2013-01-06 14:18:28 +02:00
Petri Lehtinen
40c2e532b9 Merge branch '2.4' 2013-01-03 10:46:46 +02:00
Petri Lehtinen
02beb90db3 Make it possible to forward declare struct json_t
This allows e.g. to use json_t in a header file that doesn't include
jansson.h:

    struct json_t;
    typedef struct json_t json_t;

    json_t *foo(json_t *bar);
2013-01-03 10:45:38 +02:00
Petri Lehtinen
92bf4f6fa8 Merge branch '2.4' 2012-12-28 15:30:18 +02:00
Petri Lehtinen
8ad98c9ad3 Add travis build status image more cleverly 2012-12-28 15:30:15 +02:00
Petri Lehtinen
7a4ec36f31 Merge branch '2.4' 2012-12-28 14:27:49 +02:00
Petri Lehtinen
3fab956599 Add travis build status to README.rst 2012-12-28 14:27:31 +02:00
Petri Lehtinen
a2de27a828 travis: Run "make check" instead of "make test" 2012-12-28 14:22:24 +02:00
Petri Lehtinen
99855b2d63 travis: Bootstrap autotools before building 2012-12-28 14:20:25 +02:00
Petri Lehtinen
08c5ec8f01 Merge branch '2.4' 2012-12-28 14:11:59 +02:00
Petri Lehtinen
e9cb9dbf60 Fix // -> /* */ 2012-12-28 14:11:43 +02:00
Petri Lehtinen
8284b7d3da Merge branch '2.4' 2012-12-23 07:10:02 +02:00
Petri Lehtinen
511db446d7 Add Travis configuration 2012-12-23 07:09:55 +02:00
Petri Lehtinen
b98be1f18d Merge branch '2.4' 2012-12-10 08:47:11 +02:00
Jacob Potter
872f847655 Change const pointer to array
Closes #95.
2012-12-10 08:47:00 +02:00
Jacob Potter
f86bb0377f Mark some constant data as const.
Issue #95.
2012-12-10 08:47:00 +02:00
Attie Grande
c82cea9d32 fixed l_isxdigit() macro
Closes #97.
2012-12-10 08:47:00 +02:00
Petri Lehtinen
67c002f7c9 Merge branch '2.4' 2012-12-04 21:23:33 +02:00
Petri Lehtinen
e0a7f81b39 Really fix the off-by-one error in json_array_remove()
It didn't affect only the last element but all of them. One item too
much was always moved.
2012-12-04 21:20:59 +02:00
Petri Lehtinen
7b35a18ac0 Merge branch '2.4' 2012-12-04 09:33:48 +02:00
Petri Lehtinen
54d59c743c Fix an off-by-one error in json_array_remove()
Uninitialized memory was read when the last item of a "full" array was
removed.
2012-12-04 09:32:45 +02:00
Petri Lehtinen
6279610ce4 Merge branch '2.4' 2012-10-26 15:55:00 +03:00
Petri Lehtinen
ac0ca9223b Merge pull request #93 from bertwesarg/vpath-for-2.4
Support building and testing with VPATH.
2012-10-26 05:48:32 -07:00
Bert Wesarg
4853a3454c Support building and testing with VPATH. 2012-10-26 14:43:46 +02:00
Petri Lehtinen
cf1074e70e Add a script for making releases 2012-09-23 13:42:56 +03:00
Petri Lehtinen
5804e9de9b Set master for 2.5 development mode 2012-09-23 13:41:40 +03:00
Petri Lehtinen
3279aacdee jansson 2.4 2012-09-23 12:52:41 +03:00
Petri Lehtinen
2158670177 Distribute win32 files 2012-09-23 12:52:40 +03:00
Petri Lehtinen
4cff593dd4 Update CHANGES for v2.4 2012-09-22 14:26:16 +03:00
Petri Lehtinen
597423ea80 doc: Add versionadded directive for json_boolean() 2012-09-22 14:25:29 +03:00
Petri Lehtinen
c3fc1d7382 Merge pull request #90 from luke-jr/patch-1
Add -no-undefined to LDFLAGS
2012-09-18 21:11:26 -07:00
Luke Dashjr
c922354076 Add -no-undefined to LDFLAGS
This tells libtool that jansson does not require any external symbols, and allows building it as a shared library (DLL) on Windows.
2012-09-15 08:45:29 +00:00
Petri Lehtinen
4118315afa Disallow NaN or Inf real values 2012-09-13 21:30:19 +03:00
Petri Lehtinen
ee13c667f1 Fix json_real_set() to return -1 on error 2012-09-13 08:46:56 +03:00
Petri Lehtinen
23d563434a Fix a typo on doc/apiref.rst 2012-09-06 12:06:41 +03:00
Petri Lehtinen
6142dbd8d0 Fix a typo in README.rst 2012-09-05 21:16:43 +03:00
Petri Lehtinen
0dac319bc4 CHANGES entry for #88 2012-08-11 20:45:25 +03:00
Petri Lehtinen
0b871a113c Merge branch '2.3' 2012-08-11 20:45:15 +03:00
Alessandro Ghedini
8176527f56 fix check-exports test on ppc64 2012-08-11 20:42:21 +03:00
Petri Lehtinen
ec7bb71d75 Add an initial CHANGES entry for v2.4 2012-07-30 07:52:41 +03:00
Petri Lehtinen
b6a1d8cfd4 Add json_boolean() macro
Mostly for symmetry reasons. Makes it easier e.g. to:

  int ok = 0;
  if(something)
      ok = 1;

  json_object_set_new(obj, "ok", json_boolean(ok));

Fixes #86.
2012-07-30 07:20:37 +03:00
Petri Lehtinen
52924288b9 Merge branch '2.3' 2012-07-27 07:28:40 +03:00
Petri Lehtinen
7892ecce1c Fix a small error in tutorial
Fixes #84.
2012-07-27 07:28:18 +03:00
Petri Lehtinen
a501a39626 Document the JSON_ESCAPE_SLASH encoding flag 2012-06-29 13:32:17 +03:00
Petri Lehtinen
37bc3bbf4b Tweak slash escaping 2012-06-29 13:25:02 +03:00
Petri Lehtinen
2d46ea069b Merge branch 'fix-slash' of git://github.com/jrbasso/jansson
Fixes #81.
2012-06-29 13:24:55 +03:00
Juan Basso
b217cd6689 Created flag to dump escaping slash 2012-06-28 22:04:36 -04:00
Juan Basso
a0c262d08b Escaping the slash when dump 2012-06-27 23:54:08 -04:00
Petri Lehtinen
6ce273e2e6 Merge branch '2.3' 2012-06-06 21:10:28 +03:00
Paul Harris
f62b1f5d69 Bugfix: must cast void* to do pointer math
Fixes #78.
2012-06-06 21:09:47 +03:00
Paul Harris
2b87fdcb43 Bugfix: Changed all use of ssize_t to size_t.
ssize_t is only used in a test, and its comparing to a size_t variable.

Fixes #77.
2012-06-06 08:47:17 +03:00
Petri Lehtinen
c0139681cd Update the documentation to mention Visual Studio 2010 support 2012-05-19 21:22:59 +03:00
Petri Lehtinen
9d6f9511f5 Merge pull request #74 from rogerz/contrib
Add vs2010 solution and project, fix warnings under MinGW
2012-05-03 11:31:19 -07:00
Rogerz Zhang
a79f64e155 Fix build warnings under MinGW 2012-05-01 08:02:12 +08:00
Rogerz Zhang
7ca783c3bc Add vs2010 solution and project 2012-04-30 21:51:07 +08:00
Petri Lehtinen
ff0c05b8f1 Support building on Windows
Jansson now builds correctly with Visual C++ Express 2010.
2012-04-29 22:09:29 +03:00
Petri Lehtinen
4601bf71e5 Merge branch '2.3' 2012-04-26 22:11:22 +03:00
Jack Mitchell
c7f86abf6d fix slight typo in json_pack apiref example
Fixes #73.
2012-04-26 22:10:46 +03:00
Petri Lehtinen
f309e30320 Merge branch '2.3' 2012-04-26 22:09:48 +03:00
Jack Mitchell
233574e8e0 fix slight typo in json_pack apiref example
Fixes #73.
2012-04-26 22:09:29 +03:00
Petri Lehtinen
4a6939ef87 Merge branch '2.3' 2012-04-26 09:00:49 +03:00
Petri Lehtinen
42bc7a3c50 Check for missing args and envvars in run-tests.sh
Fixes #71.
2012-04-26 08:58:36 +03:00
Petri Lehtinen
abdb8d99d7 Merge branch '2.3'
Conflicts:
	configure.ac
	doc/conf.py
	src/jansson.h
2012-04-20 21:41:25 +03:00
Petri Lehtinen
ff6e6ee293 jansson 2.3.1 2012-04-20 21:35:00 +03:00
Petri Lehtinen
8b2bfd5586 Merge branch '2.3' 2012-04-18 21:59:10 +03:00
Petri Lehtinen
e46b912f53 Fix tutorial to use Github API v3
The v2 API will be shut down on May 1st 2012.

Fixes #65.
2012-04-18 21:58:52 +03:00
Petri Lehtinen
1581f26a7f Merge branch '2.3'
Closes #70.
2012-04-18 21:27:26 +03:00
Janne Kulmala
aabfd493d3 load: Change buffer_pos to be a size_t
buffer_pos should be type size_t, because it's used to store the
current position in the read buffer. Also, it never can be negative.
2012-04-18 21:21:17 +03:00
Janne Kulmala
bd72efbd80 load: Avoid unexpected behaviour in macro expansion
Macros can be dangerous if the inserted arguments are not properly
parenthesised. As macro expansion does a simple replacement, inserting
a certain expression can cause the evaluation order of the macro expression
to change.
2012-04-18 21:21:17 +03:00
Petri Lehtinen
e8fd3e3085 Document and tweak json_load_callback()
Change the return value of json_load_callback_t to size_t, as it's
feels more correct. The callback should return (size_t)-1 on failure.

Issue #57.
2012-03-26 21:53:21 +03:00
Petri Lehtinen
873eddaf19 Merge pull request #60 from rogerz/contrib
Ignore the binary test_load_callback
2012-03-25 10:46:45 -07:00
Rogerz
bd2c0c730d Ignore the binary test_load_callback 2012-03-25 13:22:09 +00:00
Petri Lehtinen
17a51a4bf0 Merge branch '2.3' 2012-03-22 09:02:51 +02:00
Petri Lehtinen
09c39adc55 Add json_load_callback to the list of exported symbols 2012-03-22 09:02:49 +02:00
Petri Lehtinen
cbb80baf03 Merge pull request #57 from rogerz/contrib
Add json_load_callback()
2012-03-22 00:00:24 -07:00
Rogerz Zhang
040bd7b0fa Add json_load_callback() 2012-03-22 14:52:57 +08:00
Petri Lehtinen
2637faa450 Make test stripping locale independent 2012-03-22 08:48:28 +02:00
Petri Lehtinen
952e1d4ba9 Merge branch '2.3'
Closes #56.
2012-03-21 14:03:51 +02:00
Rogerz Zhang
353b5e08ba Ignore *.exe 2012-03-21 14:03:46 +02:00
Petri Lehtinen
d286e7b753 Merge branch '2.3' 2012-03-20 20:56:00 +02:00
Petri Lehtinen
3c6e36ba2d Update copyright notices for 2012 2012-03-20 20:55:55 +02:00
Petri Lehtinen
9af64480e1 Merge branch '2.3' 2012-03-20 20:48:36 +02:00
Petri Lehtinen
4ae5736bd0 Make sure strtoll() is available when using long long 2012-03-20 20:47:57 +02:00
Petri Lehtinen
56039ed596 Merge branch '2.3' 2012-03-11 21:16:41 +02:00
Petri Lehtinen
02b915af54 Remove unused declarations 2012-03-11 21:12:52 +02:00
Petri Lehtinen
1eb274c555 Merge branch '2.3' 2012-03-08 15:03:03 +02:00
Petri Lehtinen
6ac6f311b5 Fix tests on shells that don't support the export FOO=bar syntax 2012-03-08 15:01:52 +02:00
Petri Lehtinen
f736e705b2 Merge branch '2.3' 2012-02-12 16:05:35 +02:00
Petri Lehtinen
54d88753a6 Disribute the check-exports tests 2012-02-12 16:05:27 +02:00
Petri Lehtinen
98a99fb2bd Merge branch '2.3' 2012-02-02 17:03:03 +02:00
Petri Lehtinen
ac97f6f225 doc: Fix the names of library version constants
Fixes #52.
2012-02-02 17:00:35 +02:00
Petri Lehtinen
60fd7ab781 Merge branch '2.3' 2012-01-30 21:23:49 +02:00
Petri Lehtinen
0d64e8ef89 Make test_load.c not depend on the C locale
Fixes #51.
2012-01-30 21:23:18 +02:00
Petri Lehtinen
a894980258 Set master to 2.4 development mode 2012-01-30 20:59:32 +02:00
Petri Lehtinen
f227483846 jansson 2.3 2012-01-27 21:02:12 +02:00
Petri Lehtinen
6cb14dd337 Add support for optional object keys for json_unpack() and friends
Initial patch by Andrew Thompson.
2012-01-26 21:16:36 +02:00
Petri Lehtinen
fa268b5017 Add json_object_update_{existing,missing}
Closes #37.
2012-01-24 21:03:36 +02:00
Petri Lehtinen
a307974731 Implement json_object_foreach()
Also change many places to use it internally to replace hand-crafted
iteration.

Closes #45, #46.
2012-01-24 21:01:24 +02:00
Petri Lehtinen
a2381948bb Make hashtable less generic
This will make it possible to implement json_object_foreach(). It
should also have some (positive) effect on speed.
2012-01-24 21:01:23 +02:00
Petri Lehtinen
5eb2c442a9 Fix a potential memory leak 2012-01-24 20:35:59 +02:00
Petri Lehtinen
f471e63bb3 Write number of bytes read to error position on successful decode
Closes #49.
2012-01-23 21:18:04 +02:00
Petri Lehtinen
bb24697d9b Merge branch '2.2' 2011-11-14 21:16:42 +02:00
Petri Lehtinen
c4a7bf90cf Revert "json_dump_file: Open the output file in wb mode"
JSON is read as text, so line endings should be preserved.

This reverts commit 32cd821273.
2011-11-14 21:16:36 +02:00
Petri Lehtinen
bef87fc258 Merge branch '2.2'
Closes GH-43.
2011-11-14 21:11:18 +02:00
Petri Lehtinen
68d6410da0 Update the documentation of JSON_DECODE_ANY 2011-11-14 21:10:16 +02:00
Andrea Marchesini
1e36667193 JSON_DECODE_ANY
Closes GH-4.
2011-11-14 21:10:09 +02:00
Petri Lehtinen
0931d938b0 doc: The same JSON values must not be encoded in parallel
Closes GH-42.
2011-11-14 21:01:34 +02:00
Petri Lehtinen
0f2cdd70ff Avoid problems with object's serial number growing too big
Transform serial key comparison from substraction to real comparison.
Reset serial to zero in json_object_clear() to avoid it growing out of
bounds when reusing objects.

Closes GH-40.
Closes GH-41.
2011-11-14 21:01:13 +02:00
Andrea Marchesini
5ec101ec21 json_load* return NULL if the first argument is NULL 2011-11-14 20:57:41 +02:00
Petri Lehtinen
72cd84b92a doc: The configure script can be used on MinGW 2011-11-02 08:01:49 +02:00
Petri Lehtinen
6799c8e4c9 Distribute jansson_config.h.win32 2011-11-02 07:57:19 +02:00
Petri Lehtinen
8484ea3fb2 Remove '+' and leading zeros from exponents in the encoder
Fixes GH-39.
2011-11-01 20:50:16 +02:00
Petri Lehtinen
92d9b89d59 Fix test file comparisons on MinGW
Don't output any DOS line terminators from json_process.

Issue GH-39.
2011-11-01 20:49:52 +02:00
Petri Lehtinen
bc98ab6a69 Use %I64d format for printing long longs on Windows
Fixes GH-38.
2011-11-01 20:49:28 +02:00
Petri Lehtinen
32cd821273 json_dump_file: Open the output file in wb mode
For maximum compatibility.
2011-10-07 20:52:54 +03:00
Petri Lehtinen
9c6cb42f17 jansson 2.2.1 2011-10-06 21:23:09 +03:00
Petri Lehtinen
ed06777937 Distribute doc/portability.rst 2011-10-06 21:23:09 +03:00
Petri Lehtinen
6362032513 json_load_file: Open the input file in rb mode
For maximum compatibility.
2011-10-04 21:11:31 +03:00
Petri Lehtinen
84f739036d Documentation fixes 2011-10-04 21:04:56 +03:00
Petri Lehtinen
0f358c8eaa doc: Add project status to introduction, add portability chapter
Move the multithreading-and-setlocale documentation to the new
portability chapter.

Fixes GH-36.
2011-10-03 22:42:39 +03:00
Petri Lehtinen
f0d5c04734 Make identifier decoding work under all locales
Replace isxxx() functions from ctype.h with locale-independent macros.

Fixes GH-35.
2011-10-03 21:43:16 +03:00
Petri Lehtinen
fd56deb7dd Use strchr() when searching for a single character 2011-10-03 08:52:30 +03:00
Petri Lehtinen
d7ddbf3661 Make real number encoding and decoding work under all locales
The decimal point '.' is changed to locale's decimal point
before/after JSON conversion to make C standard library's
locale-specific string conversion functions work correctly.

All the tests now call setlocale(LC_ALL, "") on startup to use the
locale set in the environment.

Fixes GH-32.
2011-10-02 21:31:17 +03:00
Petri Lehtinen
b6d0191e51 Implement all other encoding functions using json_dump_callback
This way we can check for JSON_ENCODE_ANY flag in one place only.
2011-09-24 21:10:34 +03:00
Petri Lehtinen
2a70d62251 Merge pull request #33 from bakulf/master
Little fixes
2011-09-20 11:11:55 -07:00
Andrea Marchesini
909874b1b9 scope error. result points to a out-of-score variable. 2011-09-20 13:10:32 +02:00
Andrea Marchesini
ff57dee13d unsed static variable removed 2011-09-20 11:46:29 +02:00
Petri Lehtinen
e4cc77ce52 doc: Clarify the result of 's' format of json_unpack()
Fixes GH-31.
2011-09-12 21:22:20 +03:00
Petri Lehtinen
889f295958 jansson 2.2 2011-09-02 21:39:40 +03:00
Petri Lehtinen
68809cd913 doc: json_dump_callback was added in v2.2 2011-09-02 21:29:01 +03:00
Petri Lehtinen
910fb92267 Merge branch '2.1' 2011-08-08 21:00:47 +03:00
Petri Lehtinen
f241e14cab doc: Explain the non-constness of the first parameter of json_unpack() et al
Fixes GH-30.
2011-08-08 20:59:10 +03:00
Petri Lehtinen
7e9c293986 Merge branch '2.1' 2011-06-30 21:48:51 +03:00
Petri Lehtinen
d43464a1ec Merge branch 'issue-29'
Fixes GH-29.
2011-06-30 21:48:33 +03:00
Petri Lehtinen
c7079a25eb doc: Add documentation for json_dump_callback() 2011-06-30 21:47:37 +03:00
JKL
54f38d250c test file for new json_dump_callback function 2011-06-30 21:47:12 +03:00
JKL
c7d543d36c new typedef json_dump_callback_t, function json_dump_callback 2011-06-30 21:47:12 +03:00
Petri Lehtinen
7fab57dcef doc: Fix several typos and other little errors 2011-06-30 21:04:49 +03:00
JKL
cd9757512d use size_t for strbuffer writes, and avoid integer overflow 2011-06-30 20:45:18 +03:00
Petri Lehtinen
c0193bfb7f Check that target is string and value is not NULL in json_string_set() 2011-06-17 21:42:19 +03:00
Petri Lehtinen
6e1f4bb560 doc: Clarify that removing from containers decrements the refcount
While at it, reword the object iteration protocol description a bit.
2011-06-17 20:53:32 +03:00
Petri Lehtinen
1358d0bfac CHANGES: Fix v2.1 release date 2011-06-10 21:53:14 +03:00
114 changed files with 7197 additions and 1311 deletions

6
.gitignore vendored
View File

@@ -19,9 +19,15 @@ install-sh
libtool
ltmain.sh
missing
compile
test-driver
*.lo
*.la
stamp-h1
*.pyc
*.pc
/src/jansson_config.h
/jansson_private_config.h.in
/jansson_private_config.h
/build
*.exe

22
.travis.yml Normal file
View File

@@ -0,0 +1,22 @@
env:
matrix:
- JANSSON_BUILD_METHOD=cmake JANSSON_CMAKE_OPTIONS="-DJANSSON_TEST_WITH_VALGRIND=ON" JANSSON_EXTRA_INSTALL="valgrind"
- JANSSON_BUILD_METHOD=autotools
- JANSSON_BUILD_METHOD=coverage JANSSON_CMAKE_OPTIONS="-DJANSSON_COVERAGE=ON -DJANSSON_COVERALLS=ON -DCMAKE_BUILD_TYPE=Debug" JANSSON_EXTRA_INSTALL="lcov curl"
language: c
compiler:
- gcc
- clang
matrix:
exclude:
- compiler: clang
env: JANSSON_BUILD_METHOD=coverage JANSSON_CMAKE_OPTIONS="-DJANSSON_COVERAGE=ON -DJANSSON_COVERALLS=ON -DCMAKE_BUILD_TYPE=Debug" JANSSON_EXTRA_INSTALL="lcov curl"
allow_failures:
- env: JANSSON_BUILD_METHOD=coverage JANSSON_CMAKE_OPTIONS="-DJANSSON_COVERAGE=ON -DJANSSON_COVERALLS=ON -DCMAKE_BUILD_TYPE=Debug" JANSSON_EXTRA_INSTALL="lcov curl"
install:
- sudo apt-get update -qq
- sudo apt-get install -y -qq cmake $JANSSON_EXTRA_INSTALL
script:
- if [ "$JANSSON_BUILD_METHOD" = "autotools" ]; then autoreconf -f -i && CFLAGS=-Werror ./configure && make check; fi
- if [ "$JANSSON_BUILD_METHOD" = "cmake" ]; then mkdir build && cd build && cmake $JANSSON_CMAKE_OPTIONS .. && cmake --build . && ctest --output-on-failure; fi
- if [ "$JANSSON_BUILD_METHOD" = "coverage" ]; then mkdir build && cd build && cmake $JANSSON_CMAKE_OPTIONS .. && cmake --build . && cmake --build . --target coveralls; fi

30
Android.mk Normal file
View File

@@ -0,0 +1,30 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_ARM_MODE := arm
LOCAL_SRC_FILES := \
src/dump.c \
src/error.c \
src/hashtable.c \
src/hashtable_seed.c \
src/load.c \
src/memory.c \
src/pack_unpack.c \
src/strbuffer.c \
src/strconv.c \
src/utf.c \
src/value.c
LOCAL_C_INCLUDES += \
$(LOCAL_PATH) \
$(LOCAL_PATH)/android \
$(LOCAL_PATH)/src
LOCAL_MODULE_TAGS := optional
LOCAL_SHARED_LIBRARIES := libc
LOCAL_CFLAGS += -O3 -DHAVE_STDINT_H=1
LOCAL_MODULE:= libjansson
include $(BUILD_SHARED_LIBRARY)

448
CHANGES
View File

@@ -1,5 +1,449 @@
Version 2.1 (in development)
============================
Version 2.8
===========
Released 2016-08-30
* New features:
- Always preserve insertion order of object items.
`json_object_iter()` and friends, `json_object_foreach()` and
json_dumps() and friends now always work in the insertion order of
object items (#293).
- Add `json_object_foreach_safe()` macro that allows
`json_object_del()` calls during iteration (#230).
- Add `json_get_alloc_funcs()` to allow reading the allocation
functions set by `json_set_alloc_funcs()` (#262, #264).
- Add `json_pack()` format specifiers s?, o? and O? for values that
can be null (#261, #270).
* Bug fixes:
- Fix a crash when parsing inputs consisting of very deeply nested
arrays or objects (#282, #284).
- Never convert numbers to integers in the parser when
JSON_DECODE_INT_AS_REAL is set. This fixes error messages for
overflowing numbers when JSON_DECODE_INT_AS_REAL is set (#212).
- Fix a use-after-free in `json_pack()` error handling.
- Fix subnormal number parsing on mingw32.
- Handle out-of-memory situations gracefully in the hashtable
implementation (#298).
* Build:
- Fix build with CMake on all versions of Visual Studio up to 2015
(#262, #289).
- Fix pkgconfig libdir when using CMake (#268).
- Fix CMake config for static CRT builds on Windows (#206).
- Fix warnings on LLVM 6.0 targeting iOS arm64 (#208).
- Add coverlls.io support via Travis for a nice test coverage badge
(#211).
- Don't expect ``jansson_config.h`` to be in the compiler's include
path (#209).
- Add a build-time option to set initial hashtable size (#213).
- Use snprintf and strncpy in place of sprintf and strcpy to silence
linker warnings on OpenBSD (#233).
* Documentation:
- Fix various typos in documentation, and a broken link (#258).
- Add an example program in ``examples/`` (#214, #217).
- Fix building of documentation man pages (#207).
- Document the fact that copying objects doesn't preserve the
insertion order of keys (#237).
* Tests:
- Don't use the nonstandard __FUNCTION__ macro in tests.
- Use expr instead of $((...)) in shell scripts for Solaris 10
compatibility.
- Disable Visual Studio warning C4756 when triggered deliberately in
tests (#216).
- Other minor fixes (#221, #248).
* Ohter changes:
- List all unrecognized object keys when strict unpacking fails
(#263).
- Alter the order of the members of the hashtable_pair struct for
easier debugging.
- Minor performance improvement to `json_dump()` and friends (#234).
- Minor style fixes (#255, #257).
Version 2.7
===========
Released 2014-10-02
* New features:
- `json_pack()` and friends: Add format specifiers ``s%`` and ``+%``
for a size_t string length (#141).
- `json_unpack()` and friends: Add format specifier ``s%`` for
unpacking the string length along with the string itself (#141).
- Add length-aware string constructors `json_stringn()` and
`json_stringn_nocheck()`, length-aware string mutators
`json_string_setn()` and `json_string_setn_nocheck()`, and a
function for getting string's length `json_string_length()` (#141,
#143).
- Support ``\u0000`` escapes in the decoder. The support can be
enabled by using the ``JSON_ALLOW_NUL`` decoding flag (#141).
- Add `json_boolean_value()` as an alias for `json_is_true()`
(#146).
- Add JSON_REAL_PRECISION encoding flag/macro for controlling real
number precision (#178).
- Define the maximum indentation as JSON_MAX_INDENT (#191).
* Bug fixes:
- Some malformed ``\uNNNN`` escapes could crash the decoder with an
assertion failure.
- Avoid integer overflows with very long strings in UTF-8 decoder and
hashtable.
- Check for *NULL* key in `json_object_get()` and
`json_object_del()` (#151).
- Enhance hashtable seeding on Windows (#162).
- `json_unpack()`: Allow mixing JSON_STRICT with optional keys
(#162, #163).
- Fix int/int32 mismatch (#142).
- Parse subnormal numbers correctly (#202).
* Build:
- Remove VS2010 build files. CMake should be used on Windows instead
(#165).
- Fix CMake build flags for MinGW (#193).
- Add CMake config files for find_package. Rename config.h to
jansson_private_config.h (#157, #159).
- Make Valgrind checks work with CMake (#160).
- Fix feature checks to use correct __ATOMIC flags.
- Fix CMake checks for uint16_t and uint8_t support (#177).
- Make Jansson build on SmartOS/Solaris (#171).
- Work around a GCC bug on Solaris (#175).
- Fix autoreconf on Debian (#182).
- Don't use GNU make specific export for global AM_CFLAGS (#203,
#204).
- Fix building on Android using the supplied Android.mk (#166,
#174).
- Android.mk: Add -DHAVE_STDINT_H to LOCAL_CFLAGS (#200).
* Documentation:
- Document JANSSON_BUILD_SHARED_LIBS CMake option (#187).
* Tests:
- Close file handles correctly (#198).
* Other changes:
- ``\uNNNN`` escapes are now encoded in upper case for better
readability.
- Enable usage of AddressSanitizer (#180).
Version 2.6
===========
Released 2014-02-11
* Security:
- CVE-2013-6401: The hash function used by the hashtable
implementation has been changed, and is automatically seeded with
random data when the first JSON object is created. This prevents
an attacker from causing large JSON objects with specially crafted
keys perform poorly.
* New features:
- `json_object_seed()`: Set the seed value of the hash function.
* Bug fixes:
- Include CMake specific files in the release tarball.
* Documentation:
- Fix tutorial source to send a User-Agent header, which is now
required by the GitHub API.
- Set all memory to zero in secure_free() example.
Version 2.5
===========
Released 2013-09-19
* New features:
- `json_pack()` and friends: Add format specifiers ``s#``, ``+`` and
``+#``.
- Add ``JSON_DECODE_INT_AS_REAL`` decoding flag to treat all numbers
as real in the decoder (#123).
- Add `json_array_foreach()`, paralleling `json_object_foreach()`
(#118).
* Bug fixes:
- `json_dumps()` and friends: Don't crash if json is *NULL* and
``JSON_ENCODE_ANY`` is set.
- Fix a theoretical integer overflow in `jsonp_strdup()`.
- Fix `l_isxdigit()` macro (#97).
- Fix an off-by-one error in `json_array_remove()`.
* Build:
- Support CMake in addition to GNU Autotools (#106, #107, #112,
#115, #120, #127).
- Support building for Android (#109).
- Don't use ``-Werror`` by default.
- Support building and testing with VPATH (#93).
- Fix compilation when ``NDEBUG`` is defined (#128)
* Tests:
- Fix a refleak in ``test/bin/json_process.c``.
* Documentation:
- Clarify the return value of `json_load_callback_t`.
- Document how to circumvent problems with separate heaps on Windows.
- Fix memory leaks and warnings in ``github_commits.c``.
- Use `json_decref()` properly in tutorial.
* Other:
- Make it possible to forward declare ``struct json_t``.
Version 2.4
===========
Released 2012-09-23
* New features:
- Add `json_boolean()` macro that returns the JSON true or false
value based on its argument (#86).
- Add `json_load_callback()` that calls a callback function
repeatedly to read the JSON input (#57).
- Add JSON_ESCAPE_SLASH encoding flag to escape all occurences of
``/`` with ``\/``.
* Bug fixes:
- Check for and reject NaN and Inf values for reals. Encoding these
values resulted in invalid JSON.
- Fix `json_real_set()` to return -1 on error.
* Build:
- Jansson now builds on Windows with Visual Studio 2010, and
includes solution and project files in ``win32/vs2010/``
directory.
- Fix build warnings (#77, #78).
- Add ``-no-undefined`` to LDFLAGS (#90).
* Tests:
- Fix the symbol exports test on Linux/PPC64 (#88).
* Documentation:
- Fix typos (#73, #84).
Version 2.3.1
=============
Released 2012-04-20
* Build issues:
- Only use ``long long`` if ``strtoll()`` is also available.
* Documentation:
- Fix the names of library version constants in documentation. (#52)
- Change the tutorial to use GitHub API v3. (#65)
* Tests:
- Make some tests locale independent. (#51)
- Distribute the library exports test in the tarball.
- Make test run on shells that don't support the ``export FOO=bar``
syntax.
Version 2.3
===========
Released 2012-01-27
* New features:
- `json_unpack()` and friends: Add support for optional object keys
with the ``{s?o}`` syntax.
- Add `json_object_update_existing()` and
`json_object_update_missing()`, for updating only existing keys or
only adding missing keys to an object. (#37)
- Add `json_object_foreach()` for more convenient iteration over
objects. (#45, #46)
- When decoding JSON, write the number of bytes that were read from
input to ``error.position`` also on success. This is handy with
``JSON_DISABLE_EOF_CHECK``.
- Add support for decoding any JSON value, not just arrays or
objects. The support is enabled with the new ``JSON_DECODE_ANY``
flag. Patch by Andrea Marchesini. (#4)
* Bug fixes
- Avoid problems with object's serial number growing too big. (#40,
#41)
- Decoding functions now return NULL if the first argument is NULL.
Patch by Andrea Marchesini.
- Include ``jansson_config.h.win32`` in the distribution tarball.
- Remove ``+`` and leading zeros from exponents in the encoder.
(#39)
- Make Jansson build and work on MinGW. (#39, #38)
* Documentation
- Note that the same JSON values must not be encoded in parallel by
separate threads. (#42)
- Document MinGW support.
Version 2.2.1
=============
Released 2011-10-06
* Bug fixes:
- Fix real number encoding and decoding under non-C locales. (#32)
- Fix identifier decoding under non-UTF-8 locales. (#35)
- `json_load_file()`: Open the input file in binary mode for maximum
compatiblity.
* Documentation:
- Clarify the lifecycle of the result of the ``s`` fromat of
`json_unpack()`. (#31)
- Add some portability info. (#36)
- Little clarifications here and there.
* Other:
- Some style fixes, issues detected by static analyzers.
Version 2.2
===========
Released 2011-09-03
* New features:
- `json_dump_callback()`: Pass the encoder output to a callback
function in chunks.
* Bug fixes:
- `json_string_set()`: Check that target is a string and value is
not NULL.
* Other:
- Documentation typo fixes and clarifications.
Version 2.1
===========
Released 2011-06-10
* New features:

681
CMakeLists.txt Normal file
View File

@@ -0,0 +1,681 @@
# Notes:
#
# Author: Paul Harris, June 2012
# Additions: Joakim Soderberg, Febuary 2013
#
# Supports: building static/shared, release/debug/etc, can also build html docs
# and some of the tests.
# Note that its designed for out-of-tree builds, so it will not pollute your
# source tree.
#
# TODO 1: Finish implementing tests. api tests are working, but the valgrind
# variants are not flagging problems.
#
# TODO 2: There is a check_exports script that would try and incorporate.
#
# TODO 3: Consolidate version numbers, currently the version number is written
# into: * cmake (here) * autotools (the configure) * source code header files.
# Should not be written directly into header files, autotools/cmake can do
# that job.
#
# Brief intro on how to use cmake:
# > mkdir build (somewhere - we do out-of-tree builds)
# > use cmake, ccmake, or cmake-gui to configure the project. for linux, you
# can only choose one variant: release,debug,etc... and static or shared.
# >> example:
# >> cd build
# >> ccmake -i ../path_to_jansson_dir
# >> inside, configure your options. press C until there are no lines
# with * next to them.
# >> note, I like to configure the 'install' path to ../install, so I get
# self-contained clean installs I can point other projects to.
# >> press G to 'generate' the project files.
# >> make (to build the project)
# >> make install
# >> make test (to run the tests, if you enabled them)
#
# Brief description on how it works:
# There is a small heirachy of CMakeLists.txt files which define how the
# project is built.
# Header file detection etc is done, and the results are written into config.h
# and jansson_config.h, which are generated from the corresponding
# config.h.cmake and jansson_config.h.cmake template files.
# The generated header files end up in the build directory - not in
# the source directory.
# The rest is down to the usual make process.
cmake_minimum_required (VERSION 2.8)
# required for exports? cmake_minimum_required (VERSION 2.8.6)
project (jansson C)
# Options
option(JANSSON_BUILD_SHARED_LIBS "Build shared libraries." OFF)
option(USE_URANDOM "Use /dev/urandom to seed the hash function." ON)
option(USE_WINDOWS_CRYPTOAPI "Use CryptGenRandom to seed the hash function." ON)
if (MSVC)
# This option must match the settings used in your program, in particular if you
# are linking statically
option(JANSSON_STATIC_CRT "Link the static CRT libraries" OFF )
endif ()
option(JANSSON_EXAMPLES "Compile example applications" ON)
if (UNIX)
option(JANSSON_COVERAGE "(GCC Only! Requires gcov/lcov to be installed). Include target for doing coverage analysis for the test suite. Note that -DCMAKE_BUILD_TYPE=Debug must be set" OFF)
option(JANSSON_COVERALLS "Generate coverage info for Coveralls" OFF)
option(JANSSON_COVERALLS_UPLOAD "Upload coverage info to Coveralls (Only works via Travis)" ON)
endif ()
# Set some nicer output dirs.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
set(JANSSON_TEMP_DIR ${PROJECT_BINARY_DIR}/tmp)
# Give the debug version a different postfix for windows,
# so both the debug and release version can be built in the
# same build-tree on Windows (MSVC).
if (WIN32)
set(CMAKE_DEBUG_POSTFIX "_d")
endif (WIN32)
# This is how I thought it should go
# set (JANSSON_VERSION "2.3.1")
# set (JANSSON_SOVERSION 2)
set(JANSSON_DISPLAY_VERSION "2.8")
# This is what is required to match the same numbers as automake's
set(JANSSON_VERSION "4.8.0")
set(JANSSON_SOVERSION 4)
# for CheckFunctionKeywords
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include (CheckCSourceCompiles)
include (CheckFunctionExists)
include (CheckFunctionKeywords)
include (CheckIncludeFiles)
include (CheckTypeSize)
if (MSVC)
# Turn off Microsofts "security" warnings.
add_definitions( "/W3 /D_CRT_SECURE_NO_WARNINGS /wd4005 /wd4996 /nologo" )
if (JANSSON_STATIC_CRT)
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd")
endif()
endif()
if (NOT WIN32 AND (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX))
add_definitions("-fPIC")
endif()
message("C compiler: ${CMAKE_C_COMPILER_ID}")
# Coverage only works with GCC for a debug build.
if (JANSSON_COVERALLS)
set(JANSSON_COVERAGE ON)
endif()
if (JANSSON_COVERAGE)
include(CodeCoverage)
include(Coveralls)
# This adds coverage arguments to gcc/clang.
coveralls_turn_on_coverage()
endif()
check_include_files (endian.h HAVE_ENDIAN_H)
check_include_files (fcntl.h HAVE_FCNTL_H)
check_include_files (sched.h HAVE_SCHED_H)
check_include_files (unistd.h HAVE_UNISTD_H)
check_include_files (sys/param.h HAVE_SYS_PARAM_H)
check_include_files (sys/stat.h HAVE_SYS_STAT_H)
check_include_files (sys/time.h HAVE_SYS_TIME_H)
check_include_files (sys/time.h HAVE_SYS_TYPES_H)
check_function_exists (close HAVE_CLOSE)
check_function_exists (getpid HAVE_GETPID)
check_function_exists (gettimeofday HAVE_GETTIMEOFDAY)
check_function_exists (open HAVE_OPEN)
check_function_exists (read HAVE_READ)
check_function_exists (sched_yield HAVE_SCHED_YIELD)
# Check for the int-type includes
check_include_files (stdint.h HAVE_STDINT_H)
# Check our 64 bit integer sizes
check_type_size (__int64 __INT64)
check_type_size (int64_t INT64_T)
check_type_size ("long long" LONG_LONG_INT)
# Check our 32 bit integer sizes
check_type_size (int32_t INT32_T)
check_type_size (__int32 __INT32)
check_type_size ("long" LONG_INT)
check_type_size ("int" INT)
if (HAVE_INT32_T)
set (JSON_INT32 int32_t)
elseif (HAVE___INT32)
set (JSON_INT32 __int32)
elseif (HAVE_LONG_INT AND (${LONG_INT} EQUAL 4))
set (JSON_INT32 long)
elseif (HAVE_INT AND (${INT} EQUAL 4))
set (JSON_INT32 int)
else ()
message (FATAL_ERROR "Could not detect a valid 32-bit integer type")
endif ()
check_type_size ("unsigned long" UNSIGNED_LONG_INT)
check_type_size ("unsigned int" UNSIGNED_INT)
check_type_size ("unsigned short" UNSIGNED_SHORT)
check_type_size (uint32_t UINT32_T)
check_type_size (__uint32 __UINT32)
if (HAVE_UINT32_T)
set (JSON_UINT32 uint32_t)
elseif (HAVE___UINT32)
set (JSON_UINT32 __uint32)
elseif (HAVE_UNSIGNED_LONG_INT AND (${UNSIGNED_LONG_INT} EQUAL 4))
set (JSON_UINT32 "unsigned long")
elseif (HAVE_UNSIGNED_INT AND (${UNSIGNED_INT} EQUAL 4))
set (JSON_UINT32 "unsigned int")
else ()
message (FATAL_ERROR "Could not detect a valid unsigned 32-bit integer type")
endif ()
check_type_size (uint16_t UINT16_T)
check_type_size (__uint16 __UINT16)
if (HAVE_UINT16_T)
set (JSON_UINT16 uint16_t)
elseif (HAVE___UINT16)
set (JSON_UINT16 __uint16)
elseif (HAVE_UNSIGNED_INT AND (${UNSIGNED_INT} EQUAL 2))
set (JSON_UINT16 "unsigned int")
elseif (HAVE_UNSIGNED_SHORT AND (${UNSIGNED_SHORT} EQUAL 2))
set (JSON_UINT16 "unsigned short")
else ()
message (FATAL_ERROR "Could not detect a valid unsigned 16-bit integer type")
endif ()
check_type_size (uint8_t UINT8_T)
check_type_size (__uint8 __UINT8)
if (HAVE_UINT8_T)
set (JSON_UINT8 uint8_t)
elseif (HAVE___UINT8)
set (JSON_UINT8 __uint8)
else ()
set (JSON_UINT8 "unsigned char")
endif ()
# Check for ssize_t and SSIZE_T existance.
check_type_size(ssize_t SSIZE_T)
check_type_size(SSIZE_T UPPERCASE_SSIZE_T)
if(NOT HAVE_SSIZE_T)
if(HAVE_UPPERCASE_SSIZE_T)
set(JSON_SSIZE SSIZE_T)
else()
set(JSON_SSIZE int)
endif()
endif()
set(CMAKE_EXTRA_INCLUDE_FILES "")
# Check for all the variants of strtoll
check_function_exists (strtoll HAVE_STRTOLL)
check_function_exists (strtoq HAVE_STRTOQ)
check_function_exists (_strtoi64 HAVE__STRTOI64)
# Figure out what variant we should use
if (HAVE_STRTOLL)
set (JSON_STRTOINT strtoll)
elseif (HAVE_STRTOQ)
set (JSON_STRTOINT strtoq)
elseif (HAVE__STRTOI64)
set (JSON_STRTOINT _strtoi64)
else ()
# fallback to strtol (32 bit)
# this will set all the required variables
set (JSON_STRTOINT strtol)
set (JSON_INT_T long)
set (JSON_INTEGER_FORMAT "\"ld\"")
endif ()
# if we haven't defined JSON_INT_T, then we have a 64 bit conversion function.
# detect what to use for the 64 bit type.
# Note: I will prefer long long if I can get it, as that is what the automake system aimed for.
if (NOT DEFINED JSON_INT_T)
if (HAVE_LONG_LONG_INT AND (${LONG_LONG_INT} EQUAL 8))
set (JSON_INT_T "long long")
elseif (HAVE_INT64_T)
set (JSON_INT_T int64_t)
elseif (HAVE___INT64)
set (JSON_INT_T __int64)
else ()
message (FATAL_ERROR "Could not detect 64 bit type, although I detected the strtoll equivalent")
endif ()
# Apparently, Borland BCC and MSVC wants I64d,
# Borland BCC could also accept LD
# and gcc wants ldd,
# I am not sure what cygwin will want, so I will assume I64d
if (WIN32) # matches both msvc and cygwin
set (JSON_INTEGER_FORMAT "\"I64d\"")
else ()
set (JSON_INTEGER_FORMAT "\"lld\"")
endif ()
endif ()
# If locale.h and localeconv() are available, define to 1, otherwise to 0.
check_include_files (locale.h HAVE_LOCALE_H)
check_function_exists (localeconv HAVE_LOCALECONV)
if (HAVE_LOCALECONV AND HAVE_LOCALE_H)
set (JSON_HAVE_LOCALECONV 1)
else ()
set (JSON_HAVE_LOCALECONV 0)
endif()
# check if we have setlocale
check_function_exists(setlocale HAVE_SETLOCALE)
# Check what the inline keyword is.
# Note that the original JSON_INLINE was always set to just 'inline', so this goes further.
check_function_keywords("inline")
check_function_keywords("__inline")
check_function_keywords("__inline__")
if (HAVE_INLINE)
set(JSON_INLINE inline)
elseif (HAVE___INLINE)
set(JSON_INLINE __inline)
elseif (HAVE___INLINE__)
set(JSON_INLINE __inline__)
else()
# no inline on this platform
set (JSON_INLINE)
endif()
check_c_source_compiles ("int main() { unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1); return 0; } " HAVE_SYNC_BUILTINS)
check_c_source_compiles ("int main() { char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_RELEASE); __atomic_load_n(&v, __ATOMIC_ACQUIRE); return 0; }" HAVE_ATOMIC_BUILTINS)
set (JANSSON_INITIAL_HASHTABLE_ORDER 3 CACHE STRING "Number of buckets new object hashtables contain is 2 raised to this power. The default is 3, so empty hashtables contain 2^3 = 8 buckets.")
# configure the public config file
configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cmake/jansson_config.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/include/jansson_config.h)
# Copy the jansson.h file to the public include folder
file (COPY ${CMAKE_CURRENT_SOURCE_DIR}/src/jansson.h
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include/)
add_definitions(-DJANSSON_USING_CMAKE)
# configure the private config file
configure_file (${CMAKE_CURRENT_SOURCE_DIR}/cmake/jansson_private_config.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/private_include/jansson_private_config.h)
# and tell the source code to include it
add_definitions(-DHAVE_CONFIG_H)
include_directories (${CMAKE_CURRENT_BINARY_DIR}/include)
include_directories (${CMAKE_CURRENT_BINARY_DIR}/private_include)
# Add the lib sources.
file(GLOB JANSSON_SRC src/*.c)
set(JANSSON_HDR_PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/hashtable.h
${CMAKE_CURRENT_SOURCE_DIR}/src/jansson_private.h
${CMAKE_CURRENT_SOURCE_DIR}/src/strbuffer.h
${CMAKE_CURRENT_SOURCE_DIR}/src/utf.h
${CMAKE_CURRENT_BINARY_DIR}/private_include/jansson_private_config.h)
set(JANSSON_HDR_PUBLIC
${CMAKE_CURRENT_BINARY_DIR}/include/jansson_config.h
${CMAKE_CURRENT_SOURCE_DIR}/src/jansson.h)
source_group("Library Sources" FILES ${JANSSON_SRC})
source_group("Library Private Headers" FILES ${JANSSON_HDR_PRIVATE})
source_group("Library Public Headers" FILES ${JANSSON_HDR_PUBLIC})
if(JANSSON_BUILD_SHARED_LIBS)
add_library(jansson SHARED
${JANSSON_SRC}
${JANSSON_HDR_PRIVATE}
${JANSSON_HDR_PUBLIC}
src/jansson.def)
set_target_properties(jansson PROPERTIES
VERSION ${JANSSON_VERSION}
SOVERSION ${JANSSON_SOVERSION})
else()
add_library(jansson
${JANSSON_SRC}
${JANSSON_HDR_PRIVATE}
${JANSSON_HDR_PUBLIC})
endif()
if (JANSSON_EXAMPLES)
add_executable(simple_parse "${PROJECT_SOURCE_DIR}/examples/simple_parse.c")
target_link_libraries(simple_parse jansson)
endif()
# For building Documentation (uses Sphinx)
option(JANSSON_BUILD_DOCS "Build documentation (uses python-sphinx)." ON)
if (JANSSON_BUILD_DOCS)
find_package(Sphinx)
if (NOT SPHINX_FOUND)
message(WARNING "Sphinx not found. Cannot generate documentation!
Set -DJANSSON_BUILD_DOCS=OFF to get rid of this message.")
else()
if (Sphinx_VERSION_STRING VERSION_LESS 1.0)
message(WARNING "Your Sphinx version is too old!
This project requires Sphinx v1.0 or above to produce
proper documentation (you have v${Sphinx_VERSION_STRING}).
You will get output but it will have errors.")
endif()
# configured documentation tools and intermediate build results
set(BINARY_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/_build")
# Sphinx cache with pickled ReST documents
set(SPHINX_CACHE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_doctrees")
# CMake could be used to build the conf.py file too,
# eg it could automatically write the version of the program or change the theme.
# if(NOT DEFINED SPHINX_THEME)
# set(SPHINX_THEME default)
# endif()
#
# if(NOT DEFINED SPHINX_THEME_DIR)
# set(SPHINX_THEME_DIR)
# endif()
#
# configure_file(
# "${CMAKE_CURRENT_SOURCE_DIR}/conf.py.in"
# "${BINARY_BUILD_DIR}/conf.py"
# @ONLY)
# TODO: Add support for all sphinx builders: http://sphinx-doc.org/builders.html
# Add documentation targets.
set(DOC_TARGETS html)
option(JANSSON_BUILD_MAN "Create a target for building man pages." ON)
if (JANSSON_BUILD_MAN)
if (Sphinx_VERSION_STRING VERSION_LESS 1.0)
message(WARNING "Sphinx version 1.0 > is required to build man pages. You have v${Sphinx_VERSION_STRING}.")
else()
list(APPEND DOC_TARGETS man)
endif()
endif()
option(JANSSON_BUILD_LATEX "Create a target for building latex docs (to create PDF)." OFF)
if (JANSSON_BUILD_LATEX)
find_package(LATEX)
if (NOT LATEX_COMPILER)
message("Couldn't find Latex, can't build latex docs using Sphinx")
else()
message("Latex found! If you have problems building, see Sphinx documentation for required Latex packages.")
list(APPEND DOC_TARGETS latex)
endif()
endif()
# The doc target will build all documentation targets.
add_custom_target(doc)
foreach (DOC_TARGET ${DOC_TARGETS})
add_custom_target(${DOC_TARGET}
${SPHINX_EXECUTABLE}
# -q # Enable for quiet mode
-b ${DOC_TARGET}
-d "${SPHINX_CACHE_DIR}"
# -c "${BINARY_BUILD_DIR}" # enable if using cmake-generated conf.py
"${CMAKE_CURRENT_SOURCE_DIR}/doc"
"${CMAKE_CURRENT_BINARY_DIR}/doc/${DOC_TARGET}"
COMMENT "Building ${DOC_TARGET} documentation with Sphinx")
add_dependencies(doc ${DOC_TARGET})
endforeach()
message("Building documentation enabled for: ${DOC_TARGETS}")
endif()
endif ()
option(JANSSON_WITHOUT_TESTS "Don't build tests ('make test' to execute tests)" OFF)
if (NOT JANSSON_WITHOUT_TESTS)
option(JANSSON_TEST_WITH_VALGRIND "Enable valgrind tests." OFF)
ENABLE_TESTING()
if (JANSSON_TEST_WITH_VALGRIND)
# TODO: Add FindValgrind.cmake instead of having a hardcoded path.
add_definitions(-DVALGRIND)
# enable valgrind
set(CMAKE_MEMORYCHECK_COMMAND valgrind)
set(CMAKE_MEMORYCHECK_COMMAND_OPTIONS
"--error-exitcode=1 --leak-check=full --show-reachable=yes --track-origins=yes -q")
set(MEMCHECK_COMMAND
"${CMAKE_MEMORYCHECK_COMMAND} ${CMAKE_MEMORYCHECK_COMMAND_OPTIONS}")
separate_arguments(MEMCHECK_COMMAND)
endif ()
#
# Test suites.
#
if (CMAKE_COMPILER_IS_GNUCC)
add_definitions(-Wall -Wextra -Wdeclaration-after-statement)
endif ()
set(api_tests
test_array
test_copy
test_dump
test_dump_callback
test_equal
test_load
test_loadb
test_number
test_object
test_pack
test_simple
test_unpack)
# Doing arithmetic on void pointers is not allowed by Microsofts compiler
# such as secure_malloc and secure_free is doing, so exclude it for now.
if (NOT MSVC)
list(APPEND api_tests test_memory_funcs)
endif()
# Helper macro for building and linking a test program.
macro(build_testprog name dir)
add_executable(${name} ${dir}/${name}.c)
add_dependencies(${name} jansson)
target_link_libraries(${name} jansson)
endmacro(build_testprog)
# Create executables and tests/valgrind tests for API tests.
foreach (test ${api_tests})
build_testprog(${test} ${PROJECT_SOURCE_DIR}/test/suites/api)
if (JANSSON_TEST_WITH_VALGRIND)
add_test(memcheck__${test}
${MEMCHECK_COMMAND} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test}
WORKING_DIRECTORY ${JANSSON_TEMP_DIR})
else()
add_test(${test}
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test}
WORKING_DIRECTORY ${JANSSON_TEMP_DIR})
endif ()
endforeach ()
# Test harness for the suites tests.
build_testprog(json_process ${PROJECT_SOURCE_DIR}/test/bin)
set(SUITE_TEST_CMD ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/json_process)
set(SUITES encoding-flags valid invalid invalid-unicode)
foreach (SUITE ${SUITES})
file(GLOB TESTDIRS ${jansson_SOURCE_DIR}/test/suites/${SUITE}/*)
foreach (TESTDIR ${TESTDIRS})
if (IS_DIRECTORY ${TESTDIR})
get_filename_component(TNAME ${TESTDIR} NAME)
if (JANSSON_TEST_WITH_VALGRIND)
add_test(memcheck__${SUITE}__${TNAME}
${MEMCHECK_COMMAND} ${SUITE_TEST_CMD} ${TESTDIR})
else()
add_test(${SUITE}__${TNAME}
${SUITE_TEST_CMD} ${TESTDIR})
endif()
if ((${SUITE} STREQUAL "valid" OR ${SUITE} STREQUAL "invalid") AND NOT EXISTS ${TESTDIR}/nostrip)
if (JANSSON_TEST_WITH_VALGRIND)
add_test(memcheck__${SUITE}__${TNAME}__strip
${MEMCHECK_COMMAND} ${SUITE_TEST_CMD} --strip ${TESTDIR})
else()
add_test(${SUITE}__${TNAME}__strip
${SUITE_TEST_CMD} --strip ${TESTDIR})
endif()
endif ()
endif ()
endforeach ()
endforeach ()
if (JANSSON_COVERAGE)
setup_target_for_coverage(
coverage # Coverage make target "make coverage".
coverage # Name of output directory.
make # Name of test runner executable.
test) # Arguments to the test runner above (make test).
if (JANSSON_COVERALLS)
set(COVERAGE_SRCS ${JANSSON_SRC})
coveralls_setup("${COVERAGE_SRCS}" ${JANSSON_COVERALLS_UPLOAD})
endif ()
endif ()
# Enable using "make check" just like the autotools project.
# By default cmake creates a target "make test"
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}
DEPENDS json_process ${api_tests})
endif ()
#
# Installation preparation.
#
# Allow the user to override installation directories.
set(JANSSON_INSTALL_LIB_DIR lib CACHE PATH "Installation directory for libraries")
set(JANSSON_INSTALL_BIN_DIR bin CACHE PATH "Installation directory for executables")
set(JANSSON_INSTALL_INCLUDE_DIR include CACHE PATH "Installation directory for header files")
if(WIN32 AND NOT CYGWIN)
set(DEF_INSTALL_CMAKE_DIR cmake)
else()
set(DEF_INSTALL_CMAKE_DIR lib/cmake/jansson)
endif()
set(JANSSON_INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation directory for CMake files")
# Create pkg-conf file.
# (We use the same files as ./configure does, so we
# have to defined the same variables used there).
set(prefix ${CMAKE_INSTALL_PREFIX})
set(exec_prefix ${CMAKE_INSTALL_PREFIX})
set(libdir ${CMAKE_INSTALL_PREFIX}/${JANSSON_INSTALL_LIB_DIR})
set(VERSION ${JANSSON_DISPLAY_VERSION})
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/jansson.pc.in
${CMAKE_CURRENT_BINARY_DIR}/jansson.pc @ONLY)
# Make sure the paths are absolute.
foreach(p LIB BIN INCLUDE CMAKE)
set(var JANSSON_INSTALL_${p}_DIR)
if(NOT IS_ABSOLUTE "${${var}}")
set(${var} "${CMAKE_INSTALL_PREFIX}/${${var}}")
endif()
endforeach()
# Export targets (This is used for other CMake projects to easily find the libraries and include files).
export(TARGETS jansson
FILE "${PROJECT_BINARY_DIR}/JanssonTargets.cmake")
export(PACKAGE jansson)
# Generate the config file for the build-tree.
set(JANSSON__INCLUDE_DIRS
"${PROJECT_SOURCE_DIR}/include"
"${PROJECT_BINARY_DIR}/include")
set(JANSSON_INCLUDE_DIRS ${JANSSON__INCLUDE_DIRS} CACHE PATH "Jansson include directories")
configure_file(${PROJECT_SOURCE_DIR}/cmake/JanssonConfig.cmake.in
${PROJECT_BINARY_DIR}/JanssonConfig.cmake
@ONLY)
# Generate the config file for the installation tree.
file(RELATIVE_PATH
REL_INCLUDE_DIR
"${JANSSON_INSTALL_CMAKE_DIR}"
"${JANSSON_INSTALL_INCLUDE_DIR}") # Calculate the relative directory from the Cmake dir.
# Note the EVENT_CMAKE_DIR is defined in JanssonConfig.cmake.in,
# we escape it here so it's evaluated when it is included instead
# so that the include dirs are given relative to where the
# config file is located.
set(JANSSON__INCLUDE_DIRS
"\${JANSSON_CMAKE_DIR}/${REL_INCLUDE_DIR}")
configure_file(${PROJECT_SOURCE_DIR}/cmake/JanssonConfig.cmake.in
${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/JanssonConfig.cmake
@ONLY)
# Generate version info for both build-tree and install-tree.
configure_file(${PROJECT_SOURCE_DIR}/cmake/JanssonConfigVersion.cmake.in
${PROJECT_BINARY_DIR}/JanssonConfigVersion.cmake
@ONLY)
# Define the public headers.
set_target_properties(jansson PROPERTIES PUBLIC_HEADER "${JANSSON_HDR_PUBLIC}")
#TODO: fix this.
#
# Install targets.
#
install(TARGETS jansson
EXPORT JanssonTargets
LIBRARY DESTINATION "${JANSSON_INSTALL_LIB_DIR}" COMPONENT lib
ARCHIVE DESTINATION "${JANSSON_INSTALL_LIB_DIR}" COMPONENT lib
RUNTIME DESTINATION "${JANSSON_INSTALL_BIN_DIR}" COMPONENT lib # Windows DLLs
PUBLIC_HEADER DESTINATION "${JANSSON_INSTALL_INCLUDE_DIR}" COMPONENT dev)
# Install the pkg-config.
install (FILES
${CMAKE_CURRENT_BINARY_DIR}/jansson.pc
DESTINATION ${JANSSON_INSTALL_LIB_DIR}/pkgconfig COMPONENT dev)
# Install the configs.
install(FILES
${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/JanssonConfig.cmake
${PROJECT_BINARY_DIR}/JanssonConfigVersion.cmake
DESTINATION "${JANSSON_INSTALL_CMAKE_DIR}" COMPONENT dev)
# Install exports for the install-tree.
install(EXPORT JanssonTargets
DESTINATION "${JANSSON_INSTALL_CMAKE_DIR}" COMPONENT dev)
# For use when simply using add_library from a parent project to build jansson.
set(JANSSON_LIBRARIES jansson CACHE STRING "Jansson libraries")

49
CleanSpec.mk Normal file
View File

@@ -0,0 +1,49 @@
# Copyright (C) 2007 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# If you don't need to do a full clean build but would like to touch
# a file or delete some intermediate files, add a clean step to the end
# of the list. These steps will only be run once, if they haven't been
# run before.
#
# E.g.:
# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
#
# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
# files that are missing or have been moved.
#
# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
# Use $(OUT_DIR) to refer to the "out" directory.
#
# If you need to re-do something that's already mentioned, just copy
# the command and add it to the bottom of the list. E.g., if a change
# that you made last week required touching a file and a change you
# made today requires touching the same file, just copy the old
# touch step and add it to the end of the list.
#
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
# For example:
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************

View File

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

View File

@@ -1,4 +1,4 @@
EXTRA_DIST = CHANGES LICENSE README.rst
EXTRA_DIST = CHANGES LICENSE README.rst CMakeLists.txt cmake android examples
SUBDIRS = doc src test
# "make distcheck" builds the dvi target, so use it to check that the

View File

@@ -1,12 +1,21 @@
Jansson README
==============
.. image:: https://travis-ci.org/akheron/jansson.png
:target: https://travis-ci.org/akheron/jansson
.. image:: https://ci.appveyor.com/api/projects/status/lmhkkc4q8cwc65ko
:target: https://ci.appveyor.com/project/akheron/jansson
.. image:: https://coveralls.io/repos/akheron/jansson/badge.png?branch=master
:target: https://coveralls.io/r/akheron/jansson?branch=master
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
- `Comprehensive documentation`_
- No dependencies on other libraries
@@ -33,8 +42,8 @@ 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
autoreconf::
./configure script has to be generated first. The easiest way is to
use autoreconf::
$ autoreconf -i
@@ -42,8 +51,7 @@ autoreconf::
Documentation
-------------
Prebuilt HTML documentation is available at
http://www.digip.org/jansson/doc/.
Documentation is available at http://jansson.readthedocs.io/en/latest/.
The documentation source is in the ``doc/`` subdirectory. To generate
HTML documentation, invoke::
@@ -55,5 +63,6 @@ Then, point your browser to ``doc/_build/html/index.html``. Sphinx_
.. _Jansson: http://www.digip.org/jansson/
.. _`Comprehensive documentation`: http://jansson.readthedocs.io/en/latest/
.. _`MIT license`: http://www.opensource.org/licenses/mit-license.php
.. _Sphinx: http://sphinx.pocoo.org/

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2010-2014 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.
@@ -24,11 +24,20 @@
#ifdef __cplusplus
#define JSON_INLINE inline
#else
#define JSON_INLINE
#define JSON_INLINE inline
#endif
/* If your compiler supports the `long long` type,
JSON_INTEGER_IS_LONG_LONG is defined to 1, otherwise to 0. */
/* If your compiler supports the `long long` type and the strtoll()
library function, JSON_INTEGER_IS_LONG_LONG is defined to 1,
otherwise to 0. */
#define JSON_INTEGER_IS_LONG_LONG 1
/* If locale.h and localeconv() are available, define to 1,
otherwise to 0. */
#define JSON_HAVE_LOCALECONV 0
/* Maximum recursion depth for parsing JSON input.
This limits the depth of e.g. array-within-array constructions. */
#define JSON_PARSER_MAX_DEPTH 2048
#endif

14
appveyor.yml Normal file
View File

@@ -0,0 +1,14 @@
environment:
matrix:
- VS: Visual Studio 9 2008
- VS: Visual Studio 10 2010
- VS: Visual Studio 11 2012
- VS: Visual Studio 12 2013
- VS: Visual Studio 14 2015
build_script:
- md build
- cd build
- cmake -G "%VS%" ..
- cmake --build . --config Release
- ctest --output-on-failure

View File

@@ -0,0 +1,15 @@
include(CheckCSourceCompiles)
macro(check_function_keywords _wordlist)
set(${_result} "")
foreach(flag ${_wordlist})
string(REGEX REPLACE "[-+/ ()]" "_" flagname "${flag}")
string(TOUPPER "${flagname}" flagname)
set(have_flag "HAVE_${flagname}")
check_c_source_compiles("${flag} void func(); void func() { } int main() { func(); return 0; }" ${have_flag})
if(${have_flag} AND NOT ${_result})
set(${_result} "${flag}")
# break()
endif(${have_flag} AND NOT ${_result})
endforeach(flag)
endmacro(check_function_keywords)

163
cmake/CodeCoverage.cmake Normal file
View File

@@ -0,0 +1,163 @@
#
# Boost Software License - Version 1.0 - August 17th, 2003
#
# Permission is hereby granted, free of charge, to any person or organization
# obtaining a copy of the software and accompanying documentation covered by
# this license (the "Software") to use, reproduce, display, distribute,
# execute, and transmit the Software, and to prepare derivative works of the
# Software, and to permit third-parties to whom the Software is furnished to
# do so, all subject to the following:
#
# The copyright notices in the Software and this entire statement, including
# the above license grant, this restriction and the following disclaimer,
# must be included in all copies of the Software, in whole or in part, and
# all derivative works of the Software, unless such copies or derivative
# works are solely in the form of machine-executable object code generated by
# a source language processor.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
# SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
# FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
# 2012-01-31, Lars Bilke
# - Enable Code Coverage
#
# 2013-09-17, Joakim Söderberg
# - Added support for Clang.
# - Some additional usage instructions.
#
# USAGE:
# 1. Copy this file into your cmake modules path.
#
# 2. Add the following line to your CMakeLists.txt:
# INCLUDE(CodeCoverage)
#
# 3. Set compiler flags to turn off optimization and enable coverage:
# SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
# SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
#
# 3. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target
# which runs your test executable and produces a lcov code coverage report:
# Example:
# SETUP_TARGET_FOR_COVERAGE(
# my_coverage_target # Name for custom target.
# test_driver # Name of the test driver executable that runs the tests.
# # NOTE! This should always have a ZERO as exit code
# # otherwise the coverage generation will not complete.
# coverage # Name of output directory.
# )
#
# 4. Build a Debug build:
# cmake -DCMAKE_BUILD_TYPE=Debug ..
# make
# make my_coverage_target
#
#
# Check prereqs
FIND_PROGRAM( GCOV_PATH gcov )
FIND_PROGRAM( LCOV_PATH lcov )
FIND_PROGRAM( GENHTML_PATH genhtml )
FIND_PROGRAM( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests)
IF(NOT GCOV_PATH)
MESSAGE(FATAL_ERROR "gcov not found! Aborting...")
ENDIF() # NOT GCOV_PATH
IF(NOT (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_GNUCC))
# Clang version 3.0.0 and greater now supports gcov as well.
MESSAGE(WARNING "Compiler is not GNU gcc! Clang Version 3.0.0 and greater supports gcov as well, but older versions don't.")
IF(NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang"))
MESSAGE(FATAL_ERROR "Compiler is not GNU gcc or Clang! Aborting...")
ENDIF()
ENDIF() # NOT CMAKE_COMPILER_IS_GNUCXX
IF ( NOT CMAKE_BUILD_TYPE STREQUAL "Debug" )
MESSAGE( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" )
ENDIF() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
# Param _targetname The name of new the custom make target
# Param _outputname lcov output is generated as _outputname.info
# HTML report is generated in _outputname/index.html
# Param _testrunner The name of the target which runs the tests.
# MUST return ZERO always, even on errors.
# If not, no coverage report will be created!
# Optional fourth parameter is passed as arguments to _testrunner
# Pass them in list form, e.g.: "-j;2" for -j 2
FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _outputname _testrunner)
IF(NOT LCOV_PATH)
MESSAGE(FATAL_ERROR "lcov not found! Aborting...")
ENDIF() # NOT LCOV_PATH
IF(NOT GENHTML_PATH)
MESSAGE(FATAL_ERROR "genhtml not found! Aborting...")
ENDIF() # NOT GENHTML_PATH
# Setup target
ADD_CUSTOM_TARGET(${_targetname}
# Cleanup lcov
${LCOV_PATH} --directory . --zerocounters
# Run tests
COMMAND ${_testrunner} ${ARGV3}
# Capturing lcov counters and generating report
COMMAND ${LCOV_PATH} --directory . --capture --output-file ${_outputname}.info
COMMAND ${LCOV_PATH} --remove ${_outputname}.info 'tests/*' '/usr/*' --output-file ${_outputname}.info.cleaned
COMMAND ${GENHTML_PATH} -o ${_outputname} ${_outputname}.info.cleaned
COMMAND ${CMAKE_COMMAND} -E remove ${_outputname}.info ${_outputname}.info.cleaned
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
)
# Show info where to find the report
ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
COMMAND ;
COMMENT "Open ./${_outputname}/index.html in your browser to view the coverage report."
)
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE
# Param _targetname The name of new the custom make target
# Param _testrunner The name of the target which runs the tests
# Param _outputname cobertura output is generated as _outputname.xml
# Optional fourth parameter is passed as arguments to _testrunner
# Pass them in list form, e.g.: "-j;2" for -j 2
FUNCTION(SETUP_TARGET_FOR_COVERAGE_COBERTURA _targetname _testrunner _outputname)
IF(NOT PYTHON_EXECUTABLE)
MESSAGE(FATAL_ERROR "Python not found! Aborting...")
ENDIF() # NOT PYTHON_EXECUTABLE
IF(NOT GCOVR_PATH)
MESSAGE(FATAL_ERROR "gcovr not found! Aborting...")
ENDIF() # NOT GCOVR_PATH
ADD_CUSTOM_TARGET(${_targetname}
# Run tests
${_testrunner} ${ARGV3}
# Running gcovr
COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} -e '${CMAKE_SOURCE_DIR}/tests/' -o ${_outputname}.xml
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Running gcovr to produce Cobertura code coverage report."
)
# Show info where to find the report
ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
COMMAND ;
COMMENT "Cobertura code coverage report saved in ${_outputname}.xml."
)
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE_COBERTURA

111
cmake/Coveralls.cmake Normal file
View File

@@ -0,0 +1,111 @@
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# Copyright (C) 2014 Joakim Söderberg <joakim.soderberg@gmail.com>
#
#
# Param _COVERAGE_SRCS A list of source files that coverage should be collected for.
# Param _COVERALLS_UPLOAD Upload the result to coveralls?
#
function(coveralls_setup _COVERAGE_SRCS _COVERALLS_UPLOAD)
# When passing a CMake list to an external process, the list
# will be converted from the format "1;2;3" to "1 2 3".
# This means the script we're calling won't see it as a list
# of sources, but rather just one long path. We remedy this
# by replacing ";" with "*" and then reversing that in the script
# that we're calling.
# http://cmake.3232098.n2.nabble.com/Passing-a-CMake-list-quot-as-is-quot-to-a-custom-target-td6505681.html
set(COVERAGE_SRCS_TMP ${_COVERAGE_SRCS})
set(COVERAGE_SRCS "")
foreach (COVERAGE_SRC ${COVERAGE_SRCS_TMP})
set(COVERAGE_SRCS "${COVERAGE_SRCS}*${COVERAGE_SRC}")
endforeach()
#message("Coverage sources: ${COVERAGE_SRCS}")
set(COVERALLS_FILE ${PROJECT_BINARY_DIR}/coveralls.json)
add_custom_target(coveralls_generate
# Zero the coverage counters.
COMMAND ${CMAKE_COMMAND}
-P "${PROJECT_SOURCE_DIR}/cmake/CoverallsClear.cmake"
# Run regress tests.
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
# Generate Gcov and translate it into coveralls JSON.
# We do this by executing an external CMake script.
# (We don't want this to run at CMake generation time, but after compilation and everything has run).
COMMAND ${CMAKE_COMMAND}
-DCOVERAGE_SRCS="${COVERAGE_SRCS}" # TODO: This is passed like: "a b c", not "a;b;c"
-DCOVERALLS_OUTPUT_FILE="${COVERALLS_FILE}"
-DCOV_PATH="${PROJECT_BINARY_DIR}"
-DPROJECT_ROOT="${PROJECT_SOURCE_DIR}"
-P "${PROJECT_SOURCE_DIR}/cmake/CoverallsGenerateGcov.cmake"
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
COMMENT "Generating coveralls output..."
)
if (_COVERALLS_UPLOAD)
message("COVERALLS UPLOAD: ON")
find_program(CURL_EXECUTABLE curl)
if (NOT CURL_EXECUTABLE)
message(FATAL_ERROR "Coveralls: curl not found! Aborting")
endif()
add_custom_target(coveralls_upload
# Upload the JSON to coveralls.
COMMAND ${CURL_EXECUTABLE}
-S -F json_file=@${COVERALLS_FILE}
https://coveralls.io/api/v1/jobs
DEPENDS coveralls_generate
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
COMMENT "Uploading coveralls output...")
add_custom_target(coveralls DEPENDS coveralls_upload)
else()
message("COVERALLS UPLOAD: OFF")
add_custom_target(coveralls DEPENDS coveralls_generate)
endif()
endfunction()
macro(coveralls_turn_on_coverage)
if(NOT (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
AND (NOT "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang"))
message(FATAL_ERROR "Coveralls: Compiler ${CMAKE_C_COMPILER_ID} is not GNU gcc! Aborting... You can set this on the command line using CC=/usr/bin/gcc CXX=/usr/bin/g++ cmake <options> ..")
endif()
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
message(FATAL_ERROR "Coveralls: Code coverage results with an optimised (non-Debug) build may be misleading! Add -DCMAKE_BUILD_TYPE=Debug")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
endmacro()

View File

@@ -0,0 +1,24 @@
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# Copyright (C) 2014 Joakim Söderberg <joakim.soderberg@gmail.com>
#
file(REMOVE_RECURSE ${PROJECT_BINARY_DIR}/*.gcda)

View File

@@ -0,0 +1,380 @@
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# Copyright (C) 2014 Joakim Söderberg <joakim.soderberg@gmail.com>
#
# This is intended to be run by a custom target in a CMake project like this.
# 0. Compile program with coverage support.
# 1. Clear coverage data. (Recursively delete *.gcda in build dir)
# 2. Run the unit tests.
# 3. Run this script specifying which source files the coverage should be performed on.
#
# This script will then use gcov to generate .gcov files in the directory specified
# via the COV_PATH var. This should probably be the same as your cmake build dir.
#
# It then parses the .gcov files to convert them into the Coveralls JSON format:
# https://coveralls.io/docs/api
#
# Example for running as standalone CMake script from the command line:
# (Note it is important the -P is at the end...)
# $ cmake -DCOV_PATH=$(pwd)
# -DCOVERAGE_SRCS="catcierge_rfid.c;catcierge_timer.c"
# -P ../cmake/CoverallsGcovUpload.cmake
#
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
#
# Make sure we have the needed arguments.
#
if (NOT COVERALLS_OUTPUT_FILE)
message(FATAL_ERROR "Coveralls: No coveralls output file specified. Please set COVERALLS_OUTPUT_FILE")
endif()
if (NOT COV_PATH)
message(FATAL_ERROR "Coveralls: Missing coverage directory path where gcov files will be generated. Please set COV_PATH")
endif()
if (NOT COVERAGE_SRCS)
message(FATAL_ERROR "Coveralls: Missing the list of source files that we should get the coverage data for COVERAGE_SRCS")
endif()
if (NOT PROJECT_ROOT)
message(FATAL_ERROR "Coveralls: Missing PROJECT_ROOT.")
endif()
# Since it's not possible to pass a CMake list properly in the
# "1;2;3" format to an external process, we have replaced the
# ";" with "*", so reverse that here so we get it back into the
# CMake list format.
string(REGEX REPLACE "\\*" ";" COVERAGE_SRCS ${COVERAGE_SRCS})
find_program(GCOV_EXECUTABLE gcov)
if (NOT GCOV_EXECUTABLE)
message(FATAL_ERROR "gcov not found! Aborting...")
endif()
find_package(Git)
# TODO: Add these git things to the coveralls json.
if (GIT_FOUND)
# Branch.
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_BRANCH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
macro (git_log_format FORMAT_CHARS VAR_NAME)
execute_process(
COMMAND ${GIT_EXECUTABLE} log -1 --pretty=format:%${FORMAT_CHARS}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE ${VAR_NAME}
OUTPUT_STRIP_TRAILING_WHITESPACE
)
endmacro()
git_log_format(an GIT_AUTHOR_EMAIL)
git_log_format(ae GIT_AUTHOR_EMAIL)
git_log_format(cn GIT_COMMITTER_NAME)
git_log_format(ce GIT_COMMITTER_EMAIL)
git_log_format(B GIT_COMMIT_MESSAGE)
message("Git exe: ${GIT_EXECUTABLE}")
message("Git branch: ${GIT_BRANCH}")
message("Git author: ${GIT_AUTHOR_NAME}")
message("Git e-mail: ${GIT_AUTHOR_EMAIL}")
message("Git commiter name: ${GIT_COMMITTER_NAME}")
message("Git commiter e-mail: ${GIT_COMMITTER_EMAIL}")
message("Git commit message: ${GIT_COMMIT_MESSAGE}")
endif()
############################# Macros #########################################
#
# This macro converts from the full path format gcov outputs:
#
# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
#
# to the original source file path the .gcov is for:
#
# /path/to/project/root/subdir/the_file.c
#
macro(get_source_path_from_gcov_filename _SRC_FILENAME _GCOV_FILENAME)
# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
# ->
# #path#to#project#root#subdir#the_file.c.gcov
get_filename_component(_GCOV_FILENAME_WEXT ${_GCOV_FILENAME} NAME)
# #path#to#project#root#subdir#the_file.c.gcov -> /path/to/project/root/subdir/the_file.c
string(REGEX REPLACE "\\.gcov$" "" SRC_FILENAME_TMP ${_GCOV_FILENAME_WEXT})
string(REGEX REPLACE "\#" "/" SRC_FILENAME_TMP ${SRC_FILENAME_TMP})
set(${_SRC_FILENAME} "${SRC_FILENAME_TMP}")
endmacro()
##############################################################################
# Get the coverage data.
file(GLOB_RECURSE GCDA_FILES "${COV_PATH}/*.gcda")
message("GCDA files:")
# Get a list of all the object directories needed by gcov
# (The directories the .gcda files and .o files are found in)
# and run gcov on those.
foreach(GCDA ${GCDA_FILES})
message("Process: ${GCDA}")
message("------------------------------------------------------------------------------")
get_filename_component(GCDA_DIR ${GCDA} PATH)
#
# The -p below refers to "Preserve path components",
# This means that the generated gcov filename of a source file will
# keep the original files entire filepath, but / is replaced with #.
# Example:
#
# /path/to/project/root/build/CMakeFiles/the_file.dir/subdir/the_file.c.gcda
# ------------------------------------------------------------------------------
# File '/path/to/project/root/subdir/the_file.c'
# Lines executed:68.34% of 199
# /path/to/project/root/subdir/the_file.c:creating '#path#to#project#root#subdir#the_file.c.gcov'
#
# If -p is not specified then the file is named only "the_file.c.gcov"
#
execute_process(
COMMAND ${GCOV_EXECUTABLE} -p -o ${GCDA_DIR} ${GCDA}
WORKING_DIRECTORY ${COV_PATH}
)
endforeach()
# TODO: Make these be absolute path
file(GLOB ALL_GCOV_FILES ${COV_PATH}/*.gcov)
# Get only the filenames to use for filtering.
#set(COVERAGE_SRCS_NAMES "")
#foreach (COVSRC ${COVERAGE_SRCS})
# get_filename_component(COVSRC_NAME ${COVSRC} NAME)
# message("${COVSRC} -> ${COVSRC_NAME}")
# list(APPEND COVERAGE_SRCS_NAMES "${COVSRC_NAME}")
#endforeach()
#
# Filter out all but the gcov files we want.
#
# We do this by comparing the list of COVERAGE_SRCS filepaths that the
# user wants the coverage data for with the paths of the generated .gcov files,
# so that we only keep the relevant gcov files.
#
# Example:
# COVERAGE_SRCS =
# /path/to/project/root/subdir/the_file.c
#
# ALL_GCOV_FILES =
# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
# /path/to/project/root/build/#path#to#project#root#subdir#other_file.c.gcov
#
# Result should be:
# GCOV_FILES =
# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
#
set(GCOV_FILES "")
#message("Look in coverage sources: ${COVERAGE_SRCS}")
message("\nFilter out unwanted GCOV files:")
message("===============================")
set(COVERAGE_SRCS_REMAINING ${COVERAGE_SRCS})
foreach (GCOV_FILE ${ALL_GCOV_FILES})
#
# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
# ->
# /path/to/project/root/subdir/the_file.c
get_source_path_from_gcov_filename(GCOV_SRC_PATH ${GCOV_FILE})
# Is this in the list of source files?
# TODO: We want to match against relative path filenames from the source file root...
list(FIND COVERAGE_SRCS ${GCOV_SRC_PATH} WAS_FOUND)
if (NOT WAS_FOUND EQUAL -1)
message("YES: ${GCOV_FILE}")
list(APPEND GCOV_FILES ${GCOV_FILE})
# We remove it from the list, so we don't bother searching for it again.
# Also files left in COVERAGE_SRCS_REMAINING after this loop ends should
# have coverage data generated from them (no lines are covered).
list(REMOVE_ITEM COVERAGE_SRCS_REMAINING ${GCOV_SRC_PATH})
else()
message("NO: ${GCOV_FILE}")
endif()
endforeach()
# TODO: Enable setting these
set(JSON_SERVICE_NAME "travis-ci")
set(JSON_SERVICE_JOB_ID $ENV{TRAVIS_JOB_ID})
set(JSON_TEMPLATE
"{
\"service_name\": \"\@JSON_SERVICE_NAME\@\",
\"service_job_id\": \"\@JSON_SERVICE_JOB_ID\@\",
\"source_files\": \@JSON_GCOV_FILES\@
}"
)
set(SRC_FILE_TEMPLATE
"{
\"name\": \"\@GCOV_SRC_REL_PATH\@\",
\"source\": \"\@GCOV_FILE_SOURCE\@\",
\"coverage\": \@GCOV_FILE_COVERAGE\@
}"
)
message("\nGenerate JSON for files:")
message("=========================")
set(JSON_GCOV_FILES "[")
# Read the GCOV files line by line and get the coverage data.
foreach (GCOV_FILE ${GCOV_FILES})
get_source_path_from_gcov_filename(GCOV_SRC_PATH ${GCOV_FILE})
file(RELATIVE_PATH GCOV_SRC_REL_PATH "${PROJECT_ROOT}" "${GCOV_SRC_PATH}")
# Loads the gcov file as a list of lines.
file(STRINGS ${GCOV_FILE} GCOV_LINES)
# Instead of trying to parse the source from the
# gcov file, simply read the file contents from the source file.
# (Parsing it from the gcov is hard because C-code uses ; in many places
# which also happens to be the same as the CMake list delimeter).
file(READ ${GCOV_SRC_PATH} GCOV_FILE_SOURCE)
string(REPLACE "\\" "\\\\" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
string(REGEX REPLACE "\"" "\\\\\"" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
string(REPLACE "\t" "\\\\t" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
string(REPLACE "\r" "\\\\r" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
string(REPLACE "\n" "\\\\n" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
# According to http://json.org/ these should be escaped as well.
# Don't know how to do that in CMake however...
#string(REPLACE "\b" "\\\\b" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
#string(REPLACE "\f" "\\\\f" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
#string(REGEX REPLACE "\u([a-fA-F0-9]{4})" "\\\\u\\1" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
# We want a json array of coverage data as a single string
# start building them from the contents of the .gcov
set(GCOV_FILE_COVERAGE "[")
foreach (GCOV_LINE ${GCOV_LINES})
# Example of what we're parsing:
# Hitcount |Line | Source
# " 8: 26: if (!allowed || (strlen(allowed) == 0))"
string(REGEX REPLACE
"^([^:]*):([^:]*):(.*)$"
"\\1;\\2;\\3"
RES
"${GCOV_LINE}")
list(LENGTH RES RES_COUNT)
if (RES_COUNT GREATER 2)
list(GET RES 0 HITCOUNT)
list(GET RES 1 LINE)
list(GET RES 2 SOURCE)
string(STRIP ${HITCOUNT} HITCOUNT)
string(STRIP ${LINE} LINE)
# Lines with 0 line numbers are metadata and can be ignored.
if (NOT ${LINE} EQUAL 0)
# Translate the hitcount into valid JSON values.
if (${HITCOUNT} STREQUAL "#####")
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}0, ")
elseif (${HITCOUNT} STREQUAL "-")
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}null, ")
else()
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}${HITCOUNT}, ")
endif()
# TODO: Look for LCOV_EXCL_LINE in SOURCE to get rid of false positives.
endif()
else()
message(WARNING "Failed to properly parse line --> ${GCOV_LINE}")
endif()
endforeach()
# Advanced way of removing the trailing comma in the JSON array.
# "[1, 2, 3, " -> "[1, 2, 3"
string(REGEX REPLACE ",[ ]*$" "" GCOV_FILE_COVERAGE ${GCOV_FILE_COVERAGE})
# Append the trailing ] to complete the JSON array.
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}]")
# Generate the final JSON for this file.
message("Generate JSON for file: ${GCOV_SRC_REL_PATH}...")
string(CONFIGURE ${SRC_FILE_TEMPLATE} FILE_JSON)
set(JSON_GCOV_FILES "${JSON_GCOV_FILES}${FILE_JSON}, ")
endforeach()
# Loop through all files we couldn't find any coverage for
# as well, and generate JSON for those as well with 0% coverage.
foreach(NOT_COVERED_SRC ${COVERAGE_SRCS_REMAINING})
# Loads the source file as a list of lines.
file(STRINGS ${NOT_COVERED_SRC} SRC_LINES)
set(GCOV_FILE_COVERAGE "[")
set(GCOV_FILE_SOURCE "")
foreach (SOURCE ${SRC_LINES})
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}0, ")
string(REPLACE "\\" "\\\\" SOURCE "${SOURCE}")
string(REGEX REPLACE "\"" "\\\\\"" SOURCE "${SOURCE}")
string(REPLACE "\t" "\\\\t" SOURCE "${SOURCE}")
string(REPLACE "\r" "\\\\r" SOURCE "${SOURCE}")
set(GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}${SOURCE}\\n")
endforeach()
# Remove trailing comma, and complete JSON array with ]
string(REGEX REPLACE ",[ ]*$" "" GCOV_FILE_COVERAGE ${GCOV_FILE_COVERAGE})
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}]")
# Generate the final JSON for this file.
message("Generate JSON for non-gcov file: ${NOT_COVERED_SRC}...")
string(CONFIGURE ${SRC_FILE_TEMPLATE} FILE_JSON)
set(JSON_GCOV_FILES "${JSON_GCOV_FILES}${FILE_JSON}, ")
endforeach()
# Get rid of trailing comma.
string(REGEX REPLACE ",[ ]*$" "" JSON_GCOV_FILES ${JSON_GCOV_FILES})
set(JSON_GCOV_FILES "${JSON_GCOV_FILES}]")
# Generate the final complete JSON!
message("Generate final JSON...")
string(CONFIGURE ${JSON_TEMPLATE} JSON)
file(WRITE "${COVERALLS_OUTPUT_FILE}" "${JSON}")
message("###########################################################################")
message("Generated coveralls JSON containing coverage data:")
message("${COVERALLS_OUTPUT_FILE}")
message("###########################################################################")

301
cmake/FindSphinx.cmake Normal file
View File

@@ -0,0 +1,301 @@
#
# PART B. DOWNLOADING AGREEMENT - LICENSE FROM SBIA WITH RIGHT TO SUBLICENSE ("SOFTWARE LICENSE").
# ------------------------------------------------------------------------------------------------
#
# 1. As used in this Software License, "you" means the individual downloading and/or
# using, reproducing, modifying, displaying and/or distributing the Software and
# the institution or entity which employs or is otherwise affiliated with such
# individual in connection therewith. The Section of Biomedical Image Analysis,
# Department of Radiology at the Universiy of Pennsylvania ("SBIA") hereby grants
# you, with right to sublicense, with respect to SBIA's rights in the software,
# and data, if any, which is the subject of this Software License (collectively,
# the "Software"), a royalty-free, non-exclusive license to use, reproduce, make
# derivative works of, display and distribute the Software, provided that:
# (a) you accept and adhere to all of the terms and conditions of this Software
# License; (b) in connection with any copy of or sublicense of all or any portion
# of the Software, all of the terms and conditions in this Software License shall
# appear in and shall apply to such copy and such sublicense, including without
# limitation all source and executable forms and on any user documentation,
# prefaced with the following words: "All or portions of this licensed product
# (such portions are the "Software") have been obtained under license from the
# Section of Biomedical Image Analysis, Department of Radiology at the University
# of Pennsylvania and are subject to the following terms and conditions:"
# (c) you preserve and maintain all applicable attributions, copyright notices
# and licenses included in or applicable to the Software; (d) modified versions
# of the Software must be clearly identified and marked as such, and must not
# be misrepresented as being the original Software; and (e) you consider making,
# but are under no obligation to make, the source code of any of your modifications
# to the Software freely available to others on an open source basis.
#
# 2. The license granted in this Software License includes without limitation the
# right to (i) incorporate the Software into proprietary programs (subject to
# any restrictions applicable to such programs), (ii) add your own copyright
# statement to your modifications of the Software, and (iii) provide additional
# or different license terms and conditions in your sublicenses of modifications
# of the Software; provided that in each case your use, reproduction or
# distribution of such modifications otherwise complies with the conditions
# stated in this Software License.
#
# 3. This Software License does not grant any rights with respect to third party
# software, except those rights that SBIA has been authorized by a third
# party to grant to you, and accordingly you are solely responsible for
# (i) obtaining any permissions from third parties that you need to use,
# reproduce, make derivative works of, display and distribute the Software,
# and (ii) informing your sublicensees, including without limitation your
# end-users, of their obligations to secure any such required permissions.
#
# 4. The Software has been designed for research purposes only and has not been
# reviewed or approved by the Food and Drug Administration or by any other
# agency. YOU ACKNOWLEDGE AND AGREE THAT CLINICAL APPLICATIONS ARE NEITHER
# RECOMMENDED NOR ADVISED. Any commercialization of the Software is at the
# sole risk of the party or parties engaged in such commercialization.
# You further agree to use, reproduce, make derivative works of, display
# and distribute the Software in compliance with all applicable governmental
# laws, regulations and orders, including without limitation those relating
# to export and import control.
#
# 5. The Software is provided "AS IS" and neither SBIA nor any contributor to
# the software (each a "Contributor") shall have any obligation to provide
# maintenance, support, updates, enhancements or modifications thereto.
# SBIA AND ALL CONTRIBUTORS SPECIFICALLY DISCLAIM ALL EXPRESS AND IMPLIED
# WARRANTIES OF ANY KIND INCLUDING, BUT NOT LIMITED TO, ANY WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
# IN NO EVENT SHALL SBIA OR ANY CONTRIBUTOR BE LIABLE TO ANY PARTY FOR
# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY ARISING IN ANY WAY RELATED
# TO THE SOFTWARE, EVEN IF SBIA OR ANY CONTRIBUTOR HAS BEEN ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGES. TO THE MAXIMUM EXTENT NOT PROHIBITED BY LAW OR
# REGULATION, YOU FURTHER ASSUME ALL LIABILITY FOR YOUR USE, REPRODUCTION,
# MAKING OF DERIVATIVE WORKS, DISPLAY, LICENSE OR DISTRIBUTION OF THE SOFTWARE
# AND AGREE TO INDEMNIFY AND HOLD HARMLESS SBIA AND ALL CONTRIBUTORS FROM
# AND AGAINST ANY AND ALL CLAIMS, SUITS, ACTIONS, DEMANDS AND JUDGMENTS ARISING
# THEREFROM.
#
# 6. None of the names, logos or trademarks of SBIA or any of SBIA's affiliates
# or any of the Contributors, or any funding agency, may be used to endorse
# or promote products produced in whole or in part by operation of the Software
# or derived from or based on the Software without specific prior written
# permission from the applicable party.
#
# 7. Any use, reproduction or distribution of the Software which is not in accordance
# with this Software License shall automatically revoke all rights granted to you
# under this Software License and render Paragraphs 1 and 2 of this Software
# License null and void.
#
# 8. This Software License does not grant any rights in or to any intellectual
# property owned by SBIA or any Contributor except those rights expressly
# granted hereunder.
#
#
# PART C. MISCELLANEOUS
# ---------------------
#
# This Agreement shall be governed by and construed in accordance with the laws
# of The Commonwealth of Pennsylvania without regard to principles of conflicts
# of law. This Agreement shall supercede and replace any license terms that you
# may have agreed to previously with respect to Software from SBIA.
#
##############################################################################
# @file FindSphinx.cmake
# @brief Find Sphinx documentation build tools.
#
# @par Input variables:
# <table border="0">
# <tr>
# @tp @b Sphinx_DIR @endtp
# <td>Installation directory of Sphinx tools. Can also be set as environment variable.</td>
# </tr>
# <tr>
# @tp @b SPHINX_DIR @endtp
# <td>Alternative environment variable for @c Sphinx_DIR.</td>
# </tr>
# <tr>
# @tp @b Sphinx_FIND_COMPONENTS @endtp
# <td>Sphinx build tools to look for, i.e., 'apidoc' and/or 'build'.</td>
# </tr>
# </table>
#
# @par Output variables:
# <table border="0">
# <tr>
# @tp @b Sphinx_FOUND @endtp
# <td>Whether all or only the requested Sphinx build tools were found.</td>
# </tr>
# <tr>
# @tp @b SPHINX_FOUND @endtp
# <td>Alias for @c Sphinx_FOUND.<td>
# </tr>
# <tr>
# @tp @b SPHINX_EXECUTABLE @endtp
# <td>Non-cached alias for @c Sphinx-build_EXECUTABLE.</td>
# </tr>
# <tr>
# @tp @b Sphinx_PYTHON_EXECUTABLE @endtp
# <td>Python executable used to run sphinx-build. This is either the
# by default found Python interpreter or a specific version as
# specified by the shebang (#!) of the sphinx-build script.</td>
# </tr>
# <tr>
# @tp @b Sphinx_PYTHON_OPTIONS @endtp
# <td>A list of Python options extracted from the shebang (#!) of the
# sphinx-build script. The -E option is added by this module
# if the Python executable is not the system default to avoid
# problems with a differing setting of the @c PYTHONHOME.</td>
# </tr>
# <tr>
# @tp @b Sphinx-build_EXECUTABLE @endtp
# <td>Absolute path of the found sphinx-build tool.</td>
# </tr>
# <tr>
# @tp @b Sphinx-apidoc_EXECUTABLE @endtp
# <td>Absolute path of the found sphinx-apidoc tool.</td>
# </tr>
# <tr>
# @tp @b Sphinx_VERSION_STRING @endtp
# <td>Sphinx version found e.g. 1.1.2.</td>
# </tr>
# <tr>
# @tp @b Sphinx_VERSION_MAJOR @endtp
# <td>Sphinx major version found e.g. 1.</td>
# </tr>
# <tr>
# @tp @b Sphinx_VERSION_MINOR @endtp
# <td>Sphinx minor version found e.g. 1.</td>
# </tr>
# <tr>
# @tp @b Sphinx_VERSION_PATCH @endtp
# <td>Sphinx patch version found e.g. 2.</td>
# </tr>
# </table>
#
# @ingroup CMakeFindModules
##############################################################################
set (_Sphinx_REQUIRED_VARS)
# ----------------------------------------------------------------------------
# initialize search
if (NOT Sphinx_DIR)
if (NOT $ENV{Sphinx_DIR} STREQUAL "")
set (Sphinx_DIR "$ENV{Sphinx_DIR}" CACHE PATH "Installation prefix of Sphinx (docutils)." FORCE)
else ()
set (Sphinx_DIR "$ENV{SPHINX_DIR}" CACHE PATH "Installation prefix of Sphinx (docutils)." FORCE)
endif ()
endif ()
# ----------------------------------------------------------------------------
# default components to look for
if (NOT Sphinx_FIND_COMPONENTS)
set (Sphinx_FIND_COMPONENTS "build")
elseif (NOT Sphinx_FIND_COMPONENTS MATCHES "^(build|apidoc)$")
message (FATAL_ERROR "Invalid Sphinx component in: ${Sphinx_FIND_COMPONENTS}")
endif ()
# ----------------------------------------------------------------------------
# find components, i.e., build tools
foreach (_Sphinx_TOOL IN LISTS Sphinx_FIND_COMPONENTS)
if (Sphinx_DIR)
find_program (
Sphinx-${_Sphinx_TOOL}_EXECUTABLE
NAMES sphinx-${_Sphinx_TOOL} sphinx-${_Sphinx_TOOL}.py
HINTS "${Sphinx_DIR}"
PATH_SUFFIXES bin
DOC "The sphinx-${_Sphinx_TOOL} Python script."
NO_DEFAULT_PATH
)
else ()
find_program (
Sphinx-${_Sphinx_TOOL}_EXECUTABLE
NAMES sphinx-${_Sphinx_TOOL} sphinx-${_Sphinx_TOOL}.py
DOC "The sphinx-${_Sphinx_TOOL} Python script."
)
endif ()
mark_as_advanced (Sphinx-${_Sphinx_TOOL}_EXECUTABLE)
list (APPEND _Sphinx_REQUIRED_VARS Sphinx-${_Sphinx_TOOL}_EXECUTABLE)
endforeach ()
# ----------------------------------------------------------------------------
# determine Python executable used by Sphinx
if (Sphinx-build_EXECUTABLE)
# extract python executable from shebang of sphinx-build
find_package (PythonInterp QUIET)
set (Sphinx_PYTHON_EXECUTABLE "${PYTHON_EXECUTABLE}")
set (Sphinx_PYTHON_OPTIONS)
file (STRINGS "${Sphinx-build_EXECUTABLE}" FIRST_LINE LIMIT_COUNT 1)
if (FIRST_LINE MATCHES "^#!(.*/python.*)") # does not match "#!/usr/bin/env python" !
string (REGEX REPLACE "^ +| +$" "" Sphinx_PYTHON_EXECUTABLE "${CMAKE_MATCH_1}")
if (Sphinx_PYTHON_EXECUTABLE MATCHES "([^ ]+) (.*)")
set (Sphinx_PYTHON_EXECUTABLE "${CMAKE_MATCH_1}")
string (REGEX REPLACE " +" ";" Sphinx_PYTHON_OPTIONS "${CMAKE_MATCH_2}")
endif ()
endif ()
# this is done to avoid problems with multiple Python versions being installed
# remember: CMake command if(STR EQUAL STR) is bad and may cause many troubles !
string (REGEX REPLACE "([.+*?^$])" "\\\\\\1" _Sphinx_PYTHON_EXECUTABLE_RE "${PYTHON_EXECUTABLE}")
list (FIND Sphinx_PYTHON_OPTIONS -E IDX)
if (IDX EQUAL -1 AND NOT Sphinx_PYTHON_EXECUTABLE MATCHES "^${_Sphinx_PYTHON_EXECUTABLE_RE}$")
list (INSERT Sphinx_PYTHON_OPTIONS 0 -E)
endif ()
unset (_Sphinx_PYTHON_EXECUTABLE_RE)
endif ()
# ----------------------------------------------------------------------------
# determine Sphinx version
if (Sphinx-build_EXECUTABLE)
# intentionally use invalid -h option here as the help that is shown then
# will include the Sphinx version information
if (Sphinx_PYTHON_EXECUTABLE)
execute_process (
COMMAND "${Sphinx_PYTHON_EXECUTABLE}" ${Sphinx_PYTHON_OPTIONS} "${Sphinx-build_EXECUTABLE}" -h
OUTPUT_VARIABLE _Sphinx_VERSION
ERROR_VARIABLE _Sphinx_VERSION
)
elseif (UNIX)
execute_process (
COMMAND "${Sphinx-build_EXECUTABLE}" -h
OUTPUT_VARIABLE _Sphinx_VERSION
ERROR_VARIABLE _Sphinx_VERSION
)
endif ()
# The sphinx version can also contain a "b" instead of the last dot.
# For example "Sphinx v1.2b1" so we cannot just split on "."
if (_Sphinx_VERSION MATCHES "Sphinx v([0-9]+\\.[0-9]+(\\.|b)[0-9]+)")
set (Sphinx_VERSION_STRING "${CMAKE_MATCH_1}")
string(REGEX REPLACE "([0-9]+)\\.[0-9]+(\\.|b)[0-9]+" "\\1" Sphinx_VERSION_MAJOR ${Sphinx_VERSION_STRING})
string(REGEX REPLACE "[0-9]+\\.([0-9]+)(\\.|b)[0-9]+" "\\1" Sphinx_VERSION_MINOR ${Sphinx_VERSION_STRING})
string(REGEX REPLACE "[0-9]+\\.[0-9]+(\\.|b)([0-9]+)" "\\1" Sphinx_VERSION_PATCH ${Sphinx_VERSION_STRING})
# v1.2.0 -> v1.2
if (Sphinx_VERSION_PATCH EQUAL 0)
string (REGEX REPLACE "\\.0$" "" Sphinx_VERSION_STRING "${Sphinx_VERSION_STRING}")
endif ()
endif()
endif ()
# ----------------------------------------------------------------------------
# compatibility with FindPythonInterp.cmake and FindPerl.cmake
set (SPHINX_EXECUTABLE "${Sphinx-build_EXECUTABLE}")
# ----------------------------------------------------------------------------
# handle the QUIETLY and REQUIRED arguments and set SPHINX_FOUND to TRUE if
# all listed variables are TRUE
include (FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS (
Sphinx
REQUIRED_VARS
${_Sphinx_REQUIRED_VARS}
# VERSION_VAR # This isn't available until CMake 2.8.8 so don't use it.
Sphinx_VERSION_STRING
)
# ----------------------------------------------------------------------------
# set Sphinx_DIR
if (NOT Sphinx_DIR AND Sphinx-build_EXECUTABLE)
get_filename_component (Sphinx_DIR "${Sphinx-build_EXECUTABLE}" PATH)
string (REGEX REPLACE "/bin/?" "" Sphinx_DIR "${Sphinx_DIR}")
set (Sphinx_DIR "${Sphinx_DIR}" CACHE PATH "Installation directory of Sphinx tools." FORCE)
endif ()
unset (_Sphinx_VERSION)
unset (_Sphinx_REQUIRED_VARS)

View File

@@ -0,0 +1,17 @@
# - Config file for the jansson package
# It defines the following variables
# JANSSON_INCLUDE_DIRS - include directories for FooBar
# JANSSON_LIBRARIES - libraries to link against
# Get the path of the current file.
get_filename_component(JANSSON_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
# Set the include directories.
set(JANSSON_INCLUDE_DIRS "@JANSSON__INCLUDE_DIRS@")
# Include the project Targets file, this contains definitions for IMPORTED targets.
include(${JANSSON_CMAKE_DIR}/JanssonTargets.cmake)
# IMPORTED targets from JanssonTargets.cmake
set(JANSSON_LIBRARIES jansson)

View File

@@ -0,0 +1,11 @@
set(PACKAGE_VERSION "@JANSSON_DISPLAY_VERSION@")
# Check whether the requested PACKAGE_FIND_VERSION is compatible
if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2010-2014 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 CMake system will generate the jansson_config.h file and
* copy it to the build and install directories.
*/
#ifndef JANSSON_CONFIG_H
#define JANSSON_CONFIG_H
/* Define this so that we can disable scattered automake configuration in source files */
#ifndef JANSSON_USING_CMAKE
#define JANSSON_USING_CMAKE
#endif
/* Note: when using cmake, JSON_INTEGER_IS_LONG_LONG is not defined nor used,
* as we will also check for __int64 etc types.
* (the definition was used in the automake system) */
/* Bring in the cmake-detected defines */
#cmakedefine HAVE_STDINT_H 1
#cmakedefine HAVE_INTTYPES_H 1
#cmakedefine HAVE_SYS_TYPES_H 1
/* Include our standard type header for the integer typedef */
#if defined(HAVE_STDINT_H)
# include <stdint.h>
#elif defined(HAVE_INTTYPES_H)
# include <inttypes.h>
#elif defined(HAVE_SYS_TYPES_H)
# include <sys/types.h>
#endif
/* 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
#define json_int_t @JSON_INT_T@
#define json_strtoint @JSON_STRTOINT@
#define JSON_INTEGER_FORMAT @JSON_INTEGER_FORMAT@
/* If locale.h and localeconv() are available, define to 1, otherwise to 0. */
#define JSON_HAVE_LOCALECONV @JSON_HAVE_LOCALECONV@
/* Maximum recursion depth for parsing JSON input.
This limits the depth of e.g. array-within-array constructions. */
#define JSON_PARSER_MAX_DEPTH 2048
#endif

View File

@@ -0,0 +1,53 @@
#cmakedefine HAVE_ENDIAN_H 1
#cmakedefine HAVE_FCNTL_H 1
#cmakedefine HAVE_SCHED_H 1
#cmakedefine HAVE_UNISTD_H 1
#cmakedefine HAVE_SYS_PARAM_H 1
#cmakedefine HAVE_SYS_STAT_H 1
#cmakedefine HAVE_SYS_TIME_H 1
#cmakedefine HAVE_SYS_TYPES_H 1
#cmakedefine HAVE_STDINT_H 1
#cmakedefine HAVE_CLOSE 1
#cmakedefine HAVE_GETPID 1
#cmakedefine HAVE_GETTIMEOFDAY 1
#cmakedefine HAVE_OPEN 1
#cmakedefine HAVE_READ 1
#cmakedefine HAVE_SCHED_YIELD 1
#cmakedefine HAVE_SYNC_BUILTINS 1
#cmakedefine HAVE_ATOMIC_BUILTINS 1
#cmakedefine HAVE_LOCALE_H 1
#cmakedefine HAVE_SETLOCALE 1
#cmakedefine HAVE_INT32_T 1
#ifndef HAVE_INT32_T
# define int32_t @JSON_INT32@
#endif
#cmakedefine HAVE_UINT32_T 1
#ifndef HAVE_UINT32_T
# define uint32_t @JSON_UINT32@
#endif
#cmakedefine HAVE_UINT16_T 1
#ifndef HAVE_UINT16_T
# define uint16_t @JSON_UINT16@
#endif
#cmakedefine HAVE_UINT8_T 1
#ifndef HAVE_UINT8_T
# define uint8_t @JSON_UINT8@
#endif
#cmakedefine HAVE_SSIZE_T 1
#ifndef HAVE_SSIZE_T
# define ssize_t @JSON_SSIZE@
#endif
#cmakedefine USE_URANDOM 1
#cmakedefine USE_WINDOWS_CRYPTOAPI 1
#define INITIAL_HASHTABLE_ORDER @JANSSON_INITIAL_HASHTABLE_ORDER@

View File

@@ -1,10 +1,11 @@
AC_PREREQ([2.60])
AC_INIT([jansson], [2.1], [petri@digip.org])
AC_INIT([jansson], [2.8], [petri@digip.org])
AC_CONFIG_AUX_DIR([.])
AM_INIT_AUTOMAKE([1.10 foreign])
AC_CONFIG_SRCDIR([src/value.c])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_HEADERS([jansson_private_config.h])
# Checks for programs.
AC_PROG_CC
@@ -14,16 +15,14 @@ AM_CONDITIONAL([GCC], [test x$GCC = xyes])
# Checks for libraries.
# Checks for header files.
AC_CHECK_HEADERS([endian.h fcntl.h locale.h sched.h unistd.h sys/param.h sys/stat.h sys/time.h sys/types.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_INT32_T
AC_TYPE_UINT32_T
AC_TYPE_UINT16_T
AC_TYPE_UINT8_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
@@ -34,6 +33,76 @@ esac
AC_SUBST([json_inline])
# Checks for library functions.
AC_CHECK_FUNCS([close getpid gettimeofday localeconv open read sched_yield strtoll])
AC_MSG_CHECKING([for gcc __sync builtins])
have_sync_builtins=no
AC_TRY_LINK(
[], [unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1);],
[have_sync_builtins=yes],
)
if test "x$have_sync_builtins" = "xyes"; then
AC_DEFINE([HAVE_SYNC_BUILTINS], [1],
[Define to 1 if gcc's __sync builtins are available])
fi
AC_MSG_RESULT([$have_sync_builtins])
AC_MSG_CHECKING([for gcc __atomic builtins])
have_atomic_builtins=no
AC_TRY_LINK(
[], [char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_RELEASE); __atomic_load_n(&v, __ATOMIC_ACQUIRE);],
[have_atomic_builtins=yes],
)
if test "x$have_atomic_builtins" = "xyes"; then
AC_DEFINE([HAVE_ATOMIC_BUILTINS], [1],
[Define to 1 if gcc's __atomic builtins are available])
fi
AC_MSG_RESULT([$have_atomic_builtins])
case "$ac_cv_type_long_long_int$ac_cv_func_strtoll" in
yesyes) json_have_long_long=1;;
*) json_have_long_long=0;;
esac
AC_SUBST([json_have_long_long])
case "$ac_cv_header_locale_h$ac_cv_func_localeconv" in
yesyes) json_have_localeconv=1;;
*) json_have_localeconv=0;;
esac
AC_SUBST([json_have_localeconv])
# Features
AC_ARG_ENABLE([urandom],
[AS_HELP_STRING([--disable-urandom],
[Don't use /dev/urandom to seed the hash function])],
[use_urandom=$enableval], [use_urandom=yes])
if test "x$use_urandom" = xyes; then
AC_DEFINE([USE_URANDOM], [1],
[Define to 1 if /dev/urandom should be used for seeding the hash function])
fi
AC_ARG_ENABLE([windows-cryptoapi],
[AS_HELP_STRING([--disable-windows-cryptoapi],
[Don't use CryptGenRandom to seed the hash function])],
[use_windows_cryptoapi=$enableval], [use_windows_cryptoapi=yes])
if test "x$use_windows_cryptoapi" = xyes; then
AC_DEFINE([USE_WINDOWS_CRYPTOAPI], [1],
[Define to 1 if CryptGenRandom should be used for seeding the hash function])
fi
AC_ARG_ENABLE([initial-hashtable-order],
[AS_HELP_STRING([--enable-initial-hashtable-order=VAL],
[Number of buckets new object hashtables contain is 2 raised to this power. The default is 3, so empty hashtables contain 2^3 = 8 buckets.])],
[initial_hashtable_order=$enableval], [initial_hashtable_order=3])
AC_DEFINE_UNQUOTED([INITIAL_HASHTABLE_ORDER], [$initial_hashtable_order],
[Number of buckets new object hashtables contain is 2 raised to this power. E.g. 3 -> 2^3 = 8.])
if test x$GCC = xyes; then
AM_CFLAGS="-Wall -Wextra -Wdeclaration-after-statement"
fi
AC_SUBST([AM_CFLAGS])
AC_CONFIG_FILES([
jansson.pc

View File

@@ -1,6 +1,6 @@
EXTRA_DIST = conf.py apiref.rst changes.rst conformance.rst \
gettingstarted.rst github_commits.c index.rst tutorial.rst \
upgrading.rst ext/refcounting.py
gettingstarted.rst github_commits.c index.rst portability.rst \
tutorial.rst upgrading.rst ext/refcounting.py
SPHINXBUILD = sphinx-build
SPHINXOPTS = -d _build/doctrees $(SPHINXOPTS_EXTRA)

File diff suppressed because it is too large Load Diff

View File

@@ -41,16 +41,16 @@ master_doc = 'index'
# General information about the project.
project = u'Jansson'
copyright = u'2009-2011, Petri Lehtinen'
copyright = u'2009-2014, 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 = '2.1'
version = '2.8-dev'
# The full version, including alpha/beta/rc tags.
release = '2.1'
release = version
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@@ -5,8 +5,7 @@ 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.
for JavaScript Object Notation (JSON)"*.
Character Encoding
==================
@@ -20,15 +19,11 @@ 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.
and UTF-8 encoding is used internally.
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.
All Unicode codepoints U+0000 through U+10FFFF are allowed in string
values. However, U+0000 is not allowed in object keys because of API
restrictions.
Unicode normalization or any other transformation is never performed
on any strings (string values or object keys). When checking for
@@ -39,6 +34,8 @@ strings.
Numbers
=======
.. _real-vs-integer:
Real vs. Integer
----------------
@@ -52,7 +49,8 @@ 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).
will be treated as real values). With the ``JSON_DECODE_INT_AS_REAL``
decoder flag set all numbers are interpreted as real.
All other JSON numbers are considered integers.
@@ -110,3 +108,13 @@ types, ``long double``, etc. Obviously, shorter types like ``short``,
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.
Depth of nested values
----------------------
To avoid stack exhaustion, Jansson currently limits the nesting depth
for arrays and objects to a certain value (default: 2048), defined as
a macro ``JSON_PARSER_MAX_DEPTH`` within ``jansson_config.h``.
The limit is allowed to be set by the RFC; there is no recommended value
or required minimum depth to be supported.

View File

@@ -19,7 +19,7 @@
<description of the json_object function>
:copyright: Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
:copyright: Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
:license: MIT, see LICENSE for details.
"""
@@ -55,5 +55,6 @@ def setup(app):
app.add_node(refcounting,
html=(html_visit, html_depart),
latex=(visit, depart),
text=(visit, depart))
text=(visit, depart),
man=(visit, depart))
app.add_directive('refcounting', refcounting_directive, 0, (1, 0, 0))

View File

@@ -10,8 +10,8 @@ Compiling and Installing Jansson
The Jansson source is available at
http://www.digip.org/jansson/releases/.
Unix-like systems
-----------------
Unix-like systems (including MinGW)
-----------------------------------
Unpack the source tarball and change to the source directory:
@@ -30,8 +30,7 @@ compiling and installing is extremely simple::
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.)
--help`` for the list of all possible configuration options.
The command ``make check`` runs the test suite distributed with
Jansson. This step is not strictly necessary, but it may find possible
@@ -44,7 +43,7 @@ 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
autoreconf -fi
This command creates the ``./configure`` script, which can then be
used as described above.
@@ -54,22 +53,126 @@ used as described above.
.. _libtool: http://www.gnu.org/software/libtool/
.. _build-cmake:
CMake (various platforms, including Windows)
--------------------------------------------
Jansson can be built using CMake_. Create a build directory for an
out-of-tree build, change to that directory, and run ``cmake`` (or ``ccmake``,
``cmake-gui``, or similar) to configure the project.
See the examples below for more detailed information.
.. note:: In the below examples ``..`` is used as an argument for ``cmake``.
This is simply the path to the jansson project root directory.
In the example it is assumed you've created a sub-directory ``build``
and are using that. You could use any path you want.
.. _build-cmake-unix:
Unix (Make files)
^^^^^^^^^^^^^^^^^
Generating make files on unix:
.. parsed-literal::
bunzip2 -c jansson-|release|.tar.bz2 | tar xf -
cd jansson-|release|
mkdir build
cd build
cmake .. # or ccmake .. for a GUI.
Then to build::
make
make check
make install
Windows (Visual Studio)
^^^^^^^^^^^^^^^^^^^^^^^
Creating Visual Studio project files from the command line:
.. parsed-literal::
<unpack>
cd jansson-|release|
md build
cd build
cmake -G "Visual Studio 10" ..
You will now have a *Visual Studio Solution* in your build directory.
To run the unit tests build the ``RUN_TESTS`` project.
If you prefer a GUI the ``cmake`` line in the above example can
be replaced with::
cmake-gui ..
For command line help (including a list of available generators)
for CMake_ simply run::
cmake
To list available CMake_ settings (and what they are currently set to)
for the project, run::
cmake -LH ..
Mac OSX (Xcode)
^^^^^^^^^^^^^^^
If you prefer using Xcode instead of make files on OSX,
do the following. (Use the same steps as
for :ref:`Unix <build-cmake-unix>`)::
...
cmake -G "Xcode" ..
Additional CMake settings
^^^^^^^^^^^^^^^^^^^^^^^^^
Shared library
""""""""""""""
By default the CMake_ project will generate build files for building the
static library. To build the shared version use::
...
cmake -DJANSSON_BUILD_SHARED_LIBS=1 ..
Changing install directory (same as autoconf --prefix)
""""""""""""""""""""""""""""""""""""""""""""""""""""""
Just as with the autoconf_ project you can change the destination directory
for ``make install``. The equivalent for autoconfs ``./configure --prefix``
in CMake_ is::
...
cmake -DCMAKE_INSTALL_PREFIX:PATH=/some/other/path ..
make install
.. _CMake: http://www.cmake.org
Android
-------
Jansson can be built for Android platforms. Android.mk is in the
source root directory. The configuration header file is located in the
``android`` directory in the source distribution.
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.
On 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
1. Create ``jansson_config.h`` (which 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``.
script). 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
@@ -116,7 +219,9 @@ link the program as follows::
cc -o prog prog.c -ljansson
Starting from version 1.2, there's also support for pkg-config_::
Starting from version 1.2, there's also support for pkg-config_:
.. code-block:: shell
cc -o prog prog.c `pkg-config --cflags --libs jansson`

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 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.
@@ -13,7 +13,7 @@
#define BUFFER_SIZE (256 * 1024) /* 256 KB */
#define URL_FORMAT "http://github.com/api/v2/json/commits/list/%s/%s/master"
#define URL_FORMAT "https://api.github.com/repos/%s/%s/commits"
#define URL_SIZE 256
/* Return the offset of the first newline in text or the length of
@@ -51,15 +51,20 @@ static size_t write_response(void *ptr, size_t size, size_t nmemb, void *stream)
static char *request(const char *url)
{
CURL *curl;
CURL *curl = NULL;
CURLcode status;
char *data;
struct curl_slist *headers = NULL;
char *data = NULL;
long code;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if(!curl)
goto error;
data = malloc(BUFFER_SIZE);
if(!curl || !data)
return NULL;
if(!data)
goto error;
struct write_result write_result = {
.data = data,
@@ -67,6 +72,11 @@ static char *request(const char *url)
};
curl_easy_setopt(curl, CURLOPT_URL, url);
/* GitHub commits API v3 requires a User-Agent header */
headers = curl_slist_append(headers, "User-Agent: Jansson-Tutorial");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_response);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_result);
@@ -75,23 +85,34 @@ static char *request(const char *url)
{
fprintf(stderr, "error: unable to request data from %s:\n", url);
fprintf(stderr, "%s\n", curl_easy_strerror(status));
return NULL;
goto error;
}
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
if(code != 200)
{
fprintf(stderr, "error: server responded with code %ld\n", code);
return NULL;
goto error;
}
curl_easy_cleanup(curl);
curl_slist_free_all(headers);
curl_global_cleanup();
/* zero-terminate the result */
data[write_result.pos] = '\0';
return data;
error:
if(data)
free(data);
if(curl)
curl_easy_cleanup(curl);
if(headers)
curl_slist_free_all(headers);
curl_global_cleanup();
return NULL;
}
int main(int argc, char *argv[])
@@ -102,7 +123,6 @@ int main(int argc, char *argv[])
json_t *root;
json_error_t error;
json_t *commits;
if(argc != 3)
{
@@ -126,42 +146,52 @@ int main(int argc, char *argv[])
return 1;
}
commits = json_object_get(root, "commits");
if(!json_is_array(commits))
if(!json_is_array(root))
{
fprintf(stderr, "error: commits is not an array\n");
fprintf(stderr, "error: root is not an array\n");
json_decref(root);
return 1;
}
for(i = 0; i < json_array_size(commits); i++)
for(i = 0; i < json_array_size(root); i++)
{
json_t *commit, *id, *message;
json_t *data, *sha, *commit, *message;
const char *message_text;
commit = json_array_get(commits, i);
if(!json_is_object(commit))
data = json_array_get(root, i);
if(!json_is_object(data))
{
fprintf(stderr, "error: commit %d is not an object\n", i + 1);
fprintf(stderr, "error: commit data %d is not an object\n", (int)(i + 1));
json_decref(root);
return 1;
}
id = json_object_get(commit, "id");
if(!json_is_string(id))
sha = json_object_get(data, "sha");
if(!json_is_string(sha))
{
fprintf(stderr, "error: commit %d: id is not a string\n", i + 1);
fprintf(stderr, "error: commit %d: sha is not a string\n", (int)(i + 1));
return 1;
}
commit = json_object_get(data, "commit");
if(!json_is_object(commit))
{
fprintf(stderr, "error: commit %d: commit is not an object\n", (int)(i + 1));
json_decref(root);
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);
fprintf(stderr, "error: commit %d: message is not a string\n", (int)(i + 1));
json_decref(root);
return 1;
}
message_text = json_string_value(message);
printf("%.8s %.*s\n",
json_string_value(id),
json_string_value(sha),
newline_offset(message_text),
message_text);
}

View File

@@ -22,6 +22,11 @@ data. Its main features and design principles are:
Jansson is licensed under the `MIT license`_; see LICENSE in the
source distribution for details.
Jansson is used in production and its API is stable. It works on
numerous platforms, including numerous Unix like systems and Windows.
It's suitable for use on any system, including desktop, server, and
small embedded systems.
.. _`MIT license`: http://www.opensource.org/licenses/mit-license.php
.. _Jansson: http://www.digip.org/jansson/
@@ -36,6 +41,7 @@ Contents
upgrading
tutorial
conformance
portability
apiref
changes

83
doc/portability.rst Normal file
View File

@@ -0,0 +1,83 @@
***********
Portability
***********
.. _portability-thread-safety:
Thread safety
-------------
Jansson is thread safe and has no mutable global state. The only
exceptions are the hash function seed and memory allocation functions,
see below.
There's no locking performed inside Jansson's code, so a multithreaded
program must perform its own locking if JSON values are shared by
multiple threads. Jansson's reference counting semantics may make this
a bit harder than it seems, as it's possible to have a reference to a
value that's also stored inside a list or object. Modifying the
container (adding or removing values) may trigger concurrent access to
such values, as containers manage the reference count of their
contained values. Bugs involving concurrent incrementing or
decrementing of deference counts may be hard to track.
The encoding functions (:func:`json_dumps()` and friends) track
reference loops by modifying the internal state of objects and arrays.
For this reason, encoding functions must not be run on the same JSON
values in two separate threads at the same time. As already noted
above, be especially careful if two arrays or objects share their
contained values with another array or object.
If you want to make sure that two JSON value hierarchies do not
contain shared values, use :func:`json_deep_copy()` to make copies.
Hash function seed
==================
To prevent an attacker from intentionally causing large JSON objects
with specially crafted keys to perform very slow, the hash function
used by Jansson is randomized using a seed value. The seed is
automatically generated on the first explicit or implicit call to
:func:`json_object()`, if :func:`json_object_seed()` has not been
called beforehand.
The seed is generated by using operating system's entropy sources if
they are available (``/dev/urandom``, ``CryptGenRandom()``). The
initialization is done in as thread safe manner as possible, by using
architecture specific lockless operations if provided by the platform
or the compiler.
If you're using threads, it's recommended to autoseed the hashtable
explicitly before spawning any threads by calling
``json_object_seed(0)`` , especially if you're unsure whether the
initialization is thread safe on your platform.
Memory allocation functions
===========================
Memory allocation functions should be set at most once, and only on
program startup. See :ref:`apiref-custom-memory-allocation`.
Locale
------
Jansson works fine under any locale.
However, if the host program is multithreaded and uses ``setlocale()``
to switch the locale in one thread while Jansson is currently encoding
or decoding JSON data in another thread, the result may be wrong or
the program may even crash.
Jansson uses locale specific functions for certain string conversions
in the encoder and decoder, and then converts the locale specific
values to/from the JSON representation. This fails if the locale
changes between the string conversion and the locale-to-JSON
conversion. This can only happen in multithreaded programs that use
``setlocale()``, because ``setlocale()`` switches the locale for all
running threads, not only the thread that calls ``setlocale()``.
If your program uses ``setlocale()`` as described above, consider
using the thread-safe ``uselocale()`` instead.

View File

@@ -7,9 +7,8 @@ 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.
of a repository in GitHub_ over the web. `GitHub API`_ uses 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
@@ -31,42 +30,44 @@ 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/
.. _GitHub: https://github.com/
.. _GitHub API: http://developer.github.com/
.. _libcurl: http://curl.haxx.se/
.. _tutorial-github-commits-api:
The GitHub Commits API
======================
The GitHub Repo 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.
The `GitHub Repo Commits API`_ is used by sending HTTP requests to
URLs like ``https://api.github.com/repos/USER/REPOSITORY/commits``,
where ``USER`` and ``REPOSITORY`` are the GitHub user ID and the name
of the repository whose commits are to be listed, respectively.
GitHub responds with a JSON object of the following form:
GitHub responds with a JSON array of the following form:
.. code-block:: none
{
"commits": [
{
"id": "<the commit ID>",
[
{
"sha": "<the commit ID>",
"commit": {
"message": "<the commit message>",
<more fields, not important to this tutorial>
<more fields, not important to this tutorial...>
},
{
"id": "<the commit ID>",
<more fields...>
},
{
"sha": "<the commit ID>",
"commit": {
"message": "<the commit message>",
<more fields, not important to this tutorial>
<more fields...>
},
<more commits...>
]
}
<more fields...>
},
<more commits...>
]
In our program, the HTTP request is sent using the following
function::
@@ -80,7 +81,7 @@ 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
.. _GitHub Repo Commits API: http://developer.github.com/v3/repos/commits/
.. _tutorial-the-program:
@@ -95,10 +96,10 @@ First the includes::
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::
The following definitions are used to build the GitHub API request
URL::
#define URL_FORMAT "http://github.com/api/v2/json/commits/list/%s/%s/master"
#define URL_FORMAT "https://api.github.com/repos/%s/%s/commits"
#define URL_SIZE 256
The following function is used when formatting the result to find the
@@ -118,20 +119,21 @@ first newline in the commit message::
The main function follows. In the beginning, we first declare a bunch
of variables and check the command line parameters::
size_t i;
char *text;
char url[URL_SIZE];
json_t *root;
json_error_t error;
json_t *commits;
if(argc != 3)
int main(int argc, char *argv[])
{
fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]);
fprintf(stderr, "List commits at USER's REPOSITORY.\n\n");
return 2;
}
size_t i;
char *text;
char url[URL_SIZE];
json_t *root;
json_error_t error;
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::
@@ -171,47 +173,53 @@ 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::
We check that the returned value really is an array::
commits = json_object_get(root, "commits");
if(!json_is_array(commits))
if(!json_is_array(root))
{
fprintf(stderr, "error: commits is not an array\n");
fprintf(stderr, "error: root is not an array\n");
json_decref(root);
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++)
for(i = 0; i < json_array_size(root); i++)
{
json_t *commit, *id, *message;
json_t *data, *sha, *commit, *message;
const char *message_text;
commit = json_array_get(commits, i);
if(!json_is_object(commit))
data = json_array_get(root, i);
if(!json_is_object(data))
{
fprintf(stderr, "error: commit %d is not an object\n", i + 1);
fprintf(stderr, "error: commit data %d is not an object\n", i + 1);
json_decref(root);
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()`.
i'th element of the ``root`` 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::
Next we'll extract the commit ID (a hexadecimal SHA-1 sum),
intermediate commit info object, and the commit message from that
object. We also do proper type checks::
id = json_object_get(commit, "id");
if(!json_is_string(id))
sha = json_object_get(data, "sha");
if(!json_is_string(sha))
{
fprintf(stderr, "error: commit %d: id is not a string\n", i + 1);
fprintf(stderr, "error: commit %d: sha is not a string\n", i + 1);
json_decref(root);
return 1;
}
commit = json_object_get(data, "commit");
if(!json_is_object(commit))
{
fprintf(stderr, "error: commit %d: commit is not an object\n", i + 1);
json_decref(root);
return 1;
}
@@ -219,6 +227,7 @@ they both are JSON strings::
if(!json_is_string(message))
{
fprintf(stderr, "error: commit %d: message is not a string\n", i + 1);
json_decref(root);
return 1;
}
...
@@ -247,28 +256,32 @@ 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::
Jansson's repository:
.. code-block:: shell
$ ./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...>
1581f26a Merge branch '2.3'
aabfd493 load: Change buffer_pos to be a size_t
bd72efbd load: Avoid unexpected behaviour in macro expansion
e8fd3e30 Document and tweak json_load_callback()
873eddaf Merge pull request #60 from rogerz/contrib
bd2c0c73 Ignore the binary test_load_callback
17a51a4b Merge branch '2.3'
09c39adc Add json_load_callback to the list of exported symbols
cbb80baf Merge pull request #57 from rogerz/contrib
040bd7b0 Add json_load_callback()
2637faa4 Make test stripping locale independent
<...>
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.
commits of a GitHub repository using the GitHub Repo 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

4
examples/README.rst Normal file
View File

@@ -0,0 +1,4 @@
Jansson examples
================
This directory contains simple example programs that use Jansson.

203
examples/simple_parse.c Normal file
View File

@@ -0,0 +1,203 @@
/*
* Simple example of parsing and printing JSON using jansson.
*
* SYNOPSIS:
* $ examples/simple_parse
* Type some JSON > [true, false, null, 1, 0.0, -0.0, "", {"name": "barney"}]
* JSON Array of 8 elements:
* JSON True
* JSON False
* JSON Null
* JSON Integer: "1"
* JSON Real: 0.000000
* JSON Real: -0.000000
* JSON String: ""
* JSON Object of 1 pair:
* JSON Key: "name"
* JSON String: "barney"
*
* Copyright (c) 2014 Robert Poor <rdpoor@gmail.com>
*
* 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>
/* forward refs */
void print_json(json_t *root);
void print_json_aux(json_t *element, int indent);
void print_json_indent(int indent);
const char *json_plural(int count);
void print_json_object(json_t *element, int indent);
void print_json_array(json_t *element, int indent);
void print_json_string(json_t *element, int indent);
void print_json_integer(json_t *element, int indent);
void print_json_real(json_t *element, int indent);
void print_json_true(json_t *element, int indent);
void print_json_false(json_t *element, int indent);
void print_json_null(json_t *element, int indent);
void print_json(json_t *root) {
print_json_aux(root, 0);
}
void print_json_aux(json_t *element, int indent) {
switch (json_typeof(element)) {
case JSON_OBJECT:
print_json_object(element, indent);
break;
case JSON_ARRAY:
print_json_array(element, indent);
break;
case JSON_STRING:
print_json_string(element, indent);
break;
case JSON_INTEGER:
print_json_integer(element, indent);
break;
case JSON_REAL:
print_json_real(element, indent);
break;
case JSON_TRUE:
print_json_true(element, indent);
break;
case JSON_FALSE:
print_json_false(element, indent);
break;
case JSON_NULL:
print_json_null(element, indent);
break;
default:
fprintf(stderr, "unrecognized JSON type %d\n", json_typeof(element));
}
}
void print_json_indent(int indent) {
int i;
for (i = 0; i < indent; i++) { putchar(' '); }
}
const char *json_plural(int count) {
return count == 1 ? "" : "s";
}
void print_json_object(json_t *element, int indent) {
size_t size;
const char *key;
json_t *value;
print_json_indent(indent);
size = json_object_size(element);
printf("JSON Object of %ld pair%s:\n", size, json_plural(size));
json_object_foreach(element, key, value) {
print_json_indent(indent + 2);
printf("JSON Key: \"%s\"\n", key);
print_json_aux(value, indent + 2);
}
}
void print_json_array(json_t *element, int indent) {
size_t i;
size_t size = json_array_size(element);
print_json_indent(indent);
printf("JSON Array of %ld element%s:\n", size, json_plural(size));
for (i = 0; i < size; i++) {
print_json_aux(json_array_get(element, i), indent + 2);
}
}
void print_json_string(json_t *element, int indent) {
print_json_indent(indent);
printf("JSON String: \"%s\"\n", json_string_value(element));
}
void print_json_integer(json_t *element, int indent) {
print_json_indent(indent);
printf("JSON Integer: \"%" JSON_INTEGER_FORMAT "\"\n", json_integer_value(element));
}
void print_json_real(json_t *element, int indent) {
print_json_indent(indent);
printf("JSON Real: %f\n", json_real_value(element));
}
void print_json_true(json_t *element, int indent) {
(void)element;
print_json_indent(indent);
printf("JSON True\n");
}
void print_json_false(json_t *element, int indent) {
(void)element;
print_json_indent(indent);
printf("JSON False\n");
}
void print_json_null(json_t *element, int indent) {
(void)element;
print_json_indent(indent);
printf("JSON Null\n");
}
/*
* Parse text into a JSON object. If text is valid JSON, returns a
* json_t structure, otherwise prints and error and returns null.
*/
json_t *load_json(const char *text) {
json_t *root;
json_error_t error;
root = json_loads(text, 0, &error);
if (root) {
return root;
} else {
fprintf(stderr, "json error on line %d: %s\n", error.line, error.text);
return (json_t *)0;
}
}
/*
* Print a prompt and return (by reference) a null-terminated line of
* text. Returns NULL on eof or some error.
*/
char *read_line(char *line, int max_chars) {
printf("Type some JSON > ");
fflush(stdout);
return fgets(line, max_chars, stdin);
}
/* ================================================================
* main
*/
#define MAX_CHARS 4096
int main(int argc, char *argv[]) {
char line[MAX_CHARS];
if (argc != 1) {
fprintf(stderr, "Usage: %s\n", argv[0]);
exit(-1);
}
while (read_line(line, MAX_CHARS) != (char *)NULL) {
/* parse text into JSON structure */
json_t *root = load_json(line);
if (root) {
/* print and release the JSON structure */
print_json(root);
json_decref(root);
}
}
return 0;
}

70
release.sh Executable file
View File

@@ -0,0 +1,70 @@
#!/bin/sh
#
# Use this script to easily make releases of Jansson. It configures
# the source tree, and builds and signs all tarballs.
die() {
echo $1 >&2
exit 1
}
confirm() {
local answer
read -p "$1 [yN]: " answer
[ "$answer" = "Y" -o "$answer" = "y" ] || exit 0
}
set -e
[ -f configure.ac ] || die "Must be run at project root directory"
# Determine version
v=$(grep AC_INIT configure.ac | sed -r 's/.*, \[(.+?)\],.*/\1/')
[ -n "$v" ] || die "Unable to determine version"
confirm "Version is $v, proceed?"
# Sanity checks
vi=$(grep version-info src/Makefile.am | sed 's/^[ \t]*//g' | cut -d" " -f2)
confirm "Libtool version-info is $vi, proceed?"
r=$(grep 'Released ' CHANGES | head -n 1)
confirm "Last CHANGES entry says \"$r\", proceed??"
dv=$(grep ^version doc/conf.py | sed -r "s/.*'(.*)'.*/\1/")
if [ "$dv" != "$v" ]; then
die "Documentation version ($dv) doesn't match library version"
fi
[ -f Makefile ] && make distclean || true
rm -f jansson-$v.tar.*
rm -rf jansson-$v-doc
rm -f jansson-$v-doc.tar.*
autoreconf -fi
./configure
# Run tests and make gz source tarball
: ${VALGRIND:=1}
export VALGRIND
make distcheck
# Make bzip2 source tarball
make dist-bzip2
# Sign source tarballs
for s in gz bz2; do
gpg --detach-sign --armor jansson-$v.tar.$s
done
# Build documentation
make html
mv doc/_build/html jansson-$v-doc
# Make and sign documentation tarballs
for s in gz bz2; do
[ $s = gz ] && compress=gzip
[ $s = bz2 ] && compress=bzip2
tar cf - jansson-$v-doc | $compress -9 -c > jansson-$v-doc.tar.$s
gpg --detach-sign --armor jansson-$v-doc.tar.$s
done
echo "All done"

View File

@@ -1,3 +1,5 @@
EXTRA_DIST = jansson.def
include_HEADERS = jansson.h jansson_config.h
lib_LTLIBRARIES = libjansson.la
@@ -6,20 +8,19 @@ libjansson_la_SOURCES = \
error.c \
hashtable.c \
hashtable.h \
hashtable_seed.c \
jansson_private.h \
load.c \
lookup3.h \
memory.c \
pack_unpack.c \
strbuffer.c \
strbuffer.h \
strconv.c \
utf.c \
utf.h \
value.c
libjansson_la_LDFLAGS = \
-no-undefined \
-export-symbols-regex '^json_' \
-version-info 5:0:1
if GCC
# These flags are gcc specific
AM_CFLAGS = -Wall -Wextra -Wdeclaration-after-statement -Werror
endif
-version-info 12:0:8

View File

@@ -1,17 +1,20 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 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 _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <jansson.h>
#include "jansson.h"
#include "jansson_private.h"
#include "strbuffer.h"
#include "utf.h"
@@ -19,21 +22,15 @@
#define MAX_INTEGER_STR_LENGTH 100
#define MAX_REAL_STR_LENGTH 100
typedef int (*dump_func)(const char *buffer, int size, void *data);
#define FLAGS_TO_INDENT(f) ((f) & 0x1F)
#define FLAGS_TO_PRECISION(f) (((f) >> 11) & 0x1F)
struct string
{
char *buffer;
int length;
int size;
};
static int dump_to_strbuffer(const char *buffer, int size, void *data)
static int dump_to_strbuffer(const char *buffer, size_t size, void *data)
{
return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
}
static int dump_to_file(const char *buffer, int size, void *data)
static int dump_to_file(const char *buffer, size_t size, void *data)
{
FILE *dest = (FILE *)data;
if(fwrite(buffer, size, 1, dest) != 1)
@@ -42,21 +39,25 @@ static int dump_to_file(const char *buffer, int size, void *data)
}
/* 32 spaces (the maximum indentation size) */
static char whitespace[] = " ";
static const char whitespace[] = " ";
static int dump_indent(size_t flags, int depth, int space, dump_func dump, void *data)
static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data)
{
if(JSON_INDENT(flags) > 0)
if(FLAGS_TO_INDENT(flags) > 0)
{
int i, ws_count = JSON_INDENT(flags);
unsigned int ws_count = FLAGS_TO_INDENT(flags), n_spaces = depth * ws_count;
if(dump("\n", 1, data))
return -1;
for(i = 0; i < depth; i++)
while(n_spaces > 0)
{
if(dump(whitespace, ws_count, data))
int cur_n = n_spaces < sizeof whitespace - 1 ? n_spaces : sizeof whitespace - 1;
if(dump(whitespace, cur_n, data))
return -1;
n_spaces -= cur_n;
}
}
else if(space && !(flags & JSON_COMPACT))
@@ -66,24 +67,25 @@ static int dump_indent(size_t flags, int depth, int space, dump_func dump, void
return 0;
}
static int dump_string(const char *str, int ascii, dump_func dump, void *data)
static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data, size_t flags)
{
const char *pos, *end;
const char *pos, *end, *lim;
int32_t codepoint;
if(dump("\"", 1, data))
return -1;
end = pos = str;
lim = str + len;
while(1)
{
const char *text;
char seq[13];
int length;
while(*end)
while(end < lim)
{
end = utf8_iterate(pos, &codepoint);
end = utf8_iterate(pos, lim - pos, &codepoint);
if(!end)
return -1;
@@ -91,8 +93,12 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data)
if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
break;
/* slash */
if((flags & JSON_ESCAPE_SLASH) && codepoint == '/')
break;
/* non-ASCII */
if(ascii && codepoint > 0x7F)
if((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F)
break;
pos = end;
@@ -106,7 +112,7 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data)
if(end == pos)
break;
/* handle \, ", and control codes */
/* handle \, /, ", and control codes */
length = 2;
switch(codepoint)
{
@@ -117,12 +123,13 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data)
case '\n': text = "\\n"; break;
case '\r': text = "\\r"; break;
case '\t': text = "\\t"; break;
case '/': text = "\\/"; break;
default:
{
/* codepoint is in BMP */
if(codepoint < 0x10000)
{
sprintf(seq, "\\u%04x", codepoint);
snprintf(seq, sizeof(seq), "\\u%04X", (unsigned int)codepoint);
length = 6;
}
@@ -135,7 +142,7 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data)
first = 0xD800 | ((codepoint & 0xffc00) >> 10);
last = 0xDC00 | (codepoint & 0x003ff);
sprintf(seq, "\\u%04x\\u%04x", first, last);
snprintf(seq, sizeof(seq), "\\u%04X\\u%04X", (unsigned int)first, (unsigned int)last);
length = 12;
}
@@ -153,22 +160,16 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data)
return dump("\"", 1, data);
}
static int object_key_compare_keys(const void *key1, const void *key2)
static int 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;
return strcmp(*(const char **)key1, *(const char **)key2);
}
static int do_dump(const json_t *json, size_t flags, int depth,
dump_func dump, void *data)
json_dump_callback_t dump, void *data)
{
int ascii = flags & JSON_ENSURE_ASCII ? 1 : 0;
if(!json)
return -1;
switch(json_typeof(json)) {
case JSON_NULL:
@@ -188,7 +189,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
size = snprintf(buffer, MAX_INTEGER_STR_LENGTH,
"%" JSON_INTEGER_FORMAT,
json_integer_value(json));
if(size >= MAX_INTEGER_STR_LENGTH)
if(size < 0 || size >= MAX_INTEGER_STR_LENGTH)
return -1;
return dump(buffer, size, data);
@@ -198,36 +199,24 @@ static int do_dump(const json_t *json, size_t flags, int depth,
{
char buffer[MAX_REAL_STR_LENGTH];
int size;
double value = json_real_value(json);
size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%.17g",
json_real_value(json));
if(size >= MAX_REAL_STR_LENGTH)
size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value,
FLAGS_TO_PRECISION(flags));
if(size < 0)
return -1;
/* 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), ascii, dump, data);
return dump_string(json_string_value(json), json_string_length(json), dump, data, flags);
case JSON_ARRAY:
{
int i;
int n;
size_t n;
size_t i;
json_array_t *array;
/* detect circular references */
@@ -306,43 +295,37 @@ static int do_dump(const json_t *json, size_t flags, int depth,
if(dump_indent(flags, depth + 1, 0, dump, data))
goto object_error;
if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER)
if(flags & JSON_SORT_KEYS)
{
const object_key_t **keys;
const char **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 *));
keys = jsonp_malloc(size * sizeof(const char *));
if(!keys)
goto object_error;
i = 0;
while(iter)
{
keys[i] = jsonp_object_iter_fullkey(iter);
keys[i] = json_object_iter_key(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);
qsort(keys, size, sizeof(const char *), compare_keys);
for(i = 0; i < size; i++)
{
const char *key;
json_t *value;
key = keys[i]->key;
key = keys[i];
value = json_object_get(json, key);
assert(value);
dump_string(key, ascii, dump, data);
dump_string(key, strlen(key), dump, data, flags);
if(dump(separator, separator_length, data) ||
do_dump(value, flags, depth + 1, dump, data))
{
@@ -378,8 +361,9 @@ static int do_dump(const json_t *json, size_t flags, int depth,
while(iter)
{
void *next = json_object_iter_next((json_t *)json, iter);
const char *key = json_object_iter_key(iter);
dump_string(json_object_iter_key(iter), ascii, dump, data);
dump_string(key, strlen(key), dump, data, flags);
if(dump(separator, separator_length, data) ||
do_dump(json_object_iter_value(iter), flags, depth + 1,
dump, data))
@@ -415,39 +399,26 @@ static int do_dump(const json_t *json, size_t flags, int depth,
}
}
char *json_dumps(const json_t *json, size_t flags)
{
strbuffer_t strbuff;
char *result;
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)) {
strbuffer_close(&strbuff);
return NULL;
}
if(json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags))
result = NULL;
else
result = jsonp_strdup(strbuffer_value(&strbuff));
result = jsonp_strdup(strbuffer_value(&strbuff));
strbuffer_close(&strbuff);
return result;
}
int json_dumpf(const json_t *json, FILE *output, size_t flags)
{
if(!(flags & JSON_ENCODE_ANY)) {
if(!json_is_array(json) && !json_is_object(json))
return -1;
}
return do_dump(json, flags, 0, dump_to_file, (void *)output);
return json_dump_callback(json, dump_to_file, (void *)output, flags);
}
int json_dump_file(const json_t *json, const char *path, size_t flags)
@@ -463,3 +434,13 @@ int json_dump_file(const json_t *json, const char *path, size_t flags)
fclose(output);
return result;
}
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
{
if(!(flags & JSON_ENCODE_ANY)) {
if(!json_is_array(json) && !json_is_object(json))
return -1;
}
return do_dump(json, flags, 0, callback, data);
}

View File

@@ -25,11 +25,11 @@ void jsonp_error_set_source(json_error_t *error, const char *source)
length = strlen(source);
if(length < JSON_ERROR_SOURCE_LENGTH)
strcpy(error->source, source);
strncpy(error->source, source, length + 1);
else {
size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4;
strcpy(error->source, "...");
strcpy(error->source + 3, source + extra);
strncpy(error->source, "...", 3);
strncpy(error->source + 3, source + extra, length - extra + 1);
}
}
@@ -56,7 +56,8 @@ void jsonp_error_vset(json_error_t *error, int line, int column,
error->line = line;
error->column = column;
error->position = position;
error->position = (int)position;
vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH, msg, ap);
error->text[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
}

View File

@@ -1,20 +1,41 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 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.
*/
#if HAVE_CONFIG_H
#include <jansson_private_config.h>
#endif
#include <stdlib.h>
#include <string.h>
#if HAVE_STDINT_H
#include <stdint.h>
#endif
#include <jansson_config.h> /* for JSON_INLINE */
#include "jansson_private.h" /* for container_of() */
#include "hashtable.h"
#ifndef INITIAL_HASHTABLE_ORDER
#define INITIAL_HASHTABLE_ORDER 3
#endif
typedef struct hashtable_list list_t;
typedef struct hashtable_pair pair_t;
typedef struct hashtable_bucket bucket_t;
extern volatile uint32_t hashtable_seed;
/* Implementation of the hash function */
#include "lookup3.h"
#define list_to_pair(list_) container_of(list_, pair_t, list)
#define ordered_list_to_pair(list_) container_of(list_, pair_t, ordered_list)
#define hash_str(key) ((size_t)hashlittle((key), strlen(key), hashtable_seed))
static JSON_INLINE void list_init(list_t *list)
{
@@ -56,22 +77,8 @@ static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket,
}
}
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 size_t num_primes = sizeof(primes) / sizeof(size_t);
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, size_t hash)
const char *key, size_t hash)
{
list_t *list;
pair_t *pair;
@@ -83,7 +90,7 @@ static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
while(1)
{
pair = list_to_pair(list);
if(pair->hash == hash && hashtable->cmp_keys(pair->key, key))
if(pair->hash == hash && strcmp(pair->key, key) == 0)
return pair;
if(list == bucket->last)
@@ -97,13 +104,13 @@ 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, size_t hash)
const char *key, size_t hash)
{
pair_t *pair;
bucket_t *bucket;
size_t index;
index = hash % num_buckets(hashtable);
index = hash & hashmask(hashtable->order);
bucket = &hashtable->buckets[index];
pair = hashtable_find_pair(hashtable, bucket, key, hash);
@@ -120,11 +127,8 @@ static int hashtable_do_del(hashtable_t *hashtable,
bucket->last = pair->list.prev;
list_remove(&pair->list);
if(hashtable->free_key)
hashtable->free_key(pair->key);
if(hashtable->free_value)
hashtable->free_value(pair->value);
list_remove(&pair->ordered_list);
json_decref(pair->value);
jsonp_free(pair);
hashtable->size--;
@@ -141,10 +145,7 @@ static void hashtable_do_clear(hashtable_t *hashtable)
{
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);
json_decref(pair->value);
jsonp_free(pair);
}
}
@@ -153,18 +154,21 @@ static int hashtable_do_rehash(hashtable_t *hashtable)
{
list_t *list, *next;
pair_t *pair;
size_t i, index, new_size;
size_t i, index, new_size, new_order;
struct hashtable_bucket *new_buckets;
jsonp_free(hashtable->buckets);
new_order = hashtable->order + 1;
new_size = hashsize(new_order);
hashtable->num_buckets++;
new_size = num_buckets(hashtable);
hashtable->buckets = jsonp_malloc(new_size * sizeof(bucket_t));
if(!hashtable->buckets)
new_buckets = jsonp_malloc(new_size * sizeof(bucket_t));
if(!new_buckets)
return -1;
for(i = 0; i < num_buckets(hashtable); i++)
jsonp_free(hashtable->buckets);
hashtable->buckets = new_buckets;
hashtable->order = new_order;
for(i = 0; i < hashsize(hashtable->order); i++)
{
hashtable->buckets[i].first = hashtable->buckets[i].last =
&hashtable->list;
@@ -184,48 +188,20 @@ 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 = jsonp_malloc(sizeof(hashtable_t));
if(!hashtable)
return NULL;
if(hashtable_init(hashtable, hash_key, cmp_keys, free_key, free_value))
{
jsonp_free(hashtable);
return NULL;
}
return hashtable;
}
void hashtable_destroy(hashtable_t *hashtable)
{
hashtable_close(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)
int hashtable_init(hashtable_t *hashtable)
{
size_t i;
hashtable->size = 0;
hashtable->num_buckets = 0; /* index to primes[] */
hashtable->buckets = jsonp_malloc(num_buckets(hashtable) * sizeof(bucket_t));
hashtable->order = INITIAL_HASHTABLE_ORDER;
hashtable->buckets = jsonp_malloc(hashsize(hashtable->order) * sizeof(bucket_t));
if(!hashtable->buckets)
return -1;
list_init(&hashtable->list);
list_init(&hashtable->ordered_list);
hashtable->hash_key = hash_key;
hashtable->cmp_keys = cmp_keys;
hashtable->free_key = free_key;
hashtable->free_value = free_value;
for(i = 0; i < num_buckets(hashtable); i++)
for(i = 0; i < hashsize(hashtable->order); i++)
{
hashtable->buckets[i].first = hashtable->buckets[i].last =
&hashtable->list;
@@ -240,56 +216,65 @@ void hashtable_close(hashtable_t *hashtable)
jsonp_free(hashtable->buckets);
}
int hashtable_set(hashtable_t *hashtable, void *key, void *value)
int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value)
{
pair_t *pair;
bucket_t *bucket;
size_t hash, index;
/* rehash if the load ratio exceeds 1 */
if(hashtable->size >= num_buckets(hashtable))
if(hashtable->size >= hashsize(hashtable->order))
if(hashtable_do_rehash(hashtable))
return -1;
hash = hashtable->hash_key(key);
index = hash % num_buckets(hashtable);
hash = hash_str(key);
index = hash & hashmask(hashtable->order);
bucket = &hashtable->buckets[index];
pair = hashtable_find_pair(hashtable, bucket, key, hash);
if(pair)
{
if(hashtable->free_key)
hashtable->free_key(key);
if(hashtable->free_value)
hashtable->free_value(pair->value);
json_decref(pair->value);
pair->value = value;
}
else
{
pair = jsonp_malloc(sizeof(pair_t));
/* offsetof(...) returns the size of pair_t without the last,
flexible member. This way, the correct amount is
allocated. */
size_t len = strlen(key);
if(len >= (size_t)-1 - offsetof(pair_t, key)) {
/* Avoid an overflow if the key is very long */
return -1;
}
pair = jsonp_malloc(offsetof(pair_t, key) + len + 1);
if(!pair)
return -1;
pair->key = key;
pair->value = value;
pair->hash = hash;
strncpy(pair->key, key, len + 1);
pair->value = value;
list_init(&pair->list);
list_init(&pair->ordered_list);
insert_to_bucket(hashtable, bucket, &pair->list);
list_insert(&hashtable->ordered_list, &pair->ordered_list);
hashtable->size++;
}
return 0;
}
void *hashtable_get(hashtable_t *hashtable, const void *key)
void *hashtable_get(hashtable_t *hashtable, const char *key)
{
pair_t *pair;
size_t hash;
bucket_t *bucket;
hash = hashtable->hash_key(key);
bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
hash = hash_str(key);
bucket = &hashtable->buckets[hash & hashmask(hashtable->order)];
pair = hashtable_find_pair(hashtable, bucket, key, hash);
if(!pair)
@@ -298,9 +283,9 @@ void *hashtable_get(hashtable_t *hashtable, const void *key)
return pair->value;
}
int hashtable_del(hashtable_t *hashtable, const void *key)
int hashtable_del(hashtable_t *hashtable, const char *key)
{
size_t hash = hashtable->hash_key(key);
size_t hash = hash_str(key);
return hashtable_do_del(hashtable, key, hash);
}
@@ -310,63 +295,62 @@ void hashtable_clear(hashtable_t *hashtable)
hashtable_do_clear(hashtable);
for(i = 0; i < num_buckets(hashtable); i++)
for(i = 0; i < hashsize(hashtable->order); i++)
{
hashtable->buckets[i].first = hashtable->buckets[i].last =
&hashtable->list;
}
list_init(&hashtable->list);
list_init(&hashtable->ordered_list);
hashtable->size = 0;
}
void *hashtable_iter(hashtable_t *hashtable)
{
return hashtable_iter_next(hashtable, &hashtable->list);
return hashtable_iter_next(hashtable, &hashtable->ordered_list);
}
void *hashtable_iter_at(hashtable_t *hashtable, const void *key)
void *hashtable_iter_at(hashtable_t *hashtable, const char *key)
{
pair_t *pair;
size_t hash;
bucket_t *bucket;
hash = hashtable->hash_key(key);
bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
hash = hash_str(key);
bucket = &hashtable->buckets[hash & hashmask(hashtable->order)];
pair = hashtable_find_pair(hashtable, bucket, key, hash);
if(!pair)
return NULL;
return &pair->list;
return &pair->ordered_list;
}
void *hashtable_iter_next(hashtable_t *hashtable, void *iter)
{
list_t *list = (list_t *)iter;
if(list->next == &hashtable->list)
if(list->next == &hashtable->ordered_list)
return NULL;
return list->next;
}
void *hashtable_iter_key(void *iter)
{
pair_t *pair = list_to_pair((list_t *)iter);
pair_t *pair = ordered_list_to_pair((list_t *)iter);
return pair->key;
}
void *hashtable_iter_value(void *iter)
{
pair_t *pair = list_to_pair((list_t *)iter);
pair_t *pair = ordered_list_to_pair((list_t *)iter);
return pair->value;
}
void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value)
void hashtable_iter_set(void *iter, json_t *value)
{
pair_t *pair = list_to_pair((list_t *)iter);
if(hashtable->free_value)
hashtable->free_value(pair->value);
pair_t *pair = ordered_list_to_pair((list_t *)iter);
json_decref(pair->value);
pair->value = value;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 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,20 +8,23 @@
#ifndef HASHTABLE_H
#define HASHTABLE_H
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);
#include <stdlib.h>
#include "jansson.h"
struct hashtable_list {
struct hashtable_list *prev;
struct hashtable_list *next;
};
/* "pair" may be a bit confusing a name, but think of it as a
key-value pair. In this case, it just encodes some extra data,
too */
struct hashtable_pair {
void *key;
void *value;
size_t hash;
struct hashtable_list list;
struct hashtable_list ordered_list;
size_t hash;
json_t *value;
char key[1];
};
struct hashtable_bucket {
@@ -32,58 +35,27 @@ struct hashtable_bucket {
typedef struct hashtable {
size_t size;
struct hashtable_bucket *buckets;
size_t num_buckets; /* index to primes[] */
size_t order; /* hashtable has pow(2, order) buckets */
struct hashtable_list list;
key_hash_fn hash_key;
key_cmp_fn cmp_keys; /* returns non-zero for equal keys */
free_fn free_key;
free_fn free_value;
struct hashtable_list ordered_list;
} hashtable_t;
/**
* hashtable_create - Create a hashtable object
*
* @hash_key: The key hashing function
* @cmp_keys: The key compare function. Returns non-zero for equal and
* zero for unequal unequal keys
* @free_key: If non-NULL, called for a key that is no longer referenced.
* @free_value: If non-NULL, called for a value that is no longer referenced.
*
* Returns a new hashtable object that should be freed with
* hashtable_destroy when it's no longer used, or NULL on failure (out
* of memory).
*/
hashtable_t *hashtable_create(key_hash_fn hash_key, key_cmp_fn cmp_keys,
free_fn free_key, free_fn free_value);
/**
* hashtable_destroy - Destroy a hashtable object
*
* @hashtable: The hashtable
*
* Destroys a hashtable created with hashtable_create().
*/
void hashtable_destroy(hashtable_t *hashtable);
#define hashtable_key_to_iter(key_) \
(&(container_of(key_, struct hashtable_pair, key)->ordered_list))
/**
* hashtable_init - Initialize a hashtable object
*
* @hashtable: The (statically allocated) hashtable object
* @hash_key: The key hashing function
* @cmp_keys: The key compare function. Returns non-zero for equal and
* zero for unequal unequal keys
* @free_key: If non-NULL, called for a key that is no longer referenced.
* @free_value: If non-NULL, called for a value that is no longer referenced.
*
* Initializes a statically allocated hashtable object. The object
* should be cleared with hashtable_close when it's no longer used.
*
* Returns 0 on success, -1 on error (out of memory).
*/
int hashtable_init(hashtable_t *hashtable,
key_hash_fn hash_key, key_cmp_fn cmp_keys,
free_fn free_key, free_fn free_value);
int hashtable_init(hashtable_t *hashtable);
/**
* hashtable_close - Release all resources used by a hashtable object
@@ -99,20 +71,17 @@ void hashtable_close(hashtable_t *hashtable);
*
* @hashtable: The hashtable object
* @key: The key
* @serial: For addition order of keys
* @value: The value
*
* If a value with the given key already exists, its value is replaced
* with the new value.
*
* Key and value are "stealed" in the sense that hashtable frees them
* automatically when they are no longer used. The freeing is
* accomplished by calling free_key and free_value functions that were
* supplied to hashtable_new. In case one or both of the free
* functions is NULL, the corresponding item is not "stealed".
* with the new value. Value is "stealed" in the sense that hashtable
* doesn't increment its refcount but decreases the refcount when the
* value is no longer needed.
*
* Returns 0 on success, -1 on failure (out of memory).
*/
int hashtable_set(hashtable_t *hashtable, void *key, void *value);
int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value);
/**
* hashtable_get - Get a value associated with a key
@@ -122,7 +91,7 @@ int hashtable_set(hashtable_t *hashtable, void *key, void *value);
*
* Returns value if it is found, or NULL otherwise.
*/
void *hashtable_get(hashtable_t *hashtable, const void *key);
void *hashtable_get(hashtable_t *hashtable, const char *key);
/**
* hashtable_del - Remove a value from the hashtable
@@ -132,7 +101,7 @@ void *hashtable_get(hashtable_t *hashtable, const void *key);
*
* Returns 0 on success, or -1 if the key was not found.
*/
int hashtable_del(hashtable_t *hashtable, const void *key);
int hashtable_del(hashtable_t *hashtable, const char *key);
/**
* hashtable_clear - Clear hashtable
@@ -169,7 +138,7 @@ void *hashtable_iter(hashtable_t *hashtable);
* Like hashtable_iter() but returns an iterator pointing to a
* specific key.
*/
void *hashtable_iter_at(hashtable_t *hashtable, const void *key);
void *hashtable_iter_at(hashtable_t *hashtable, const char *key);
/**
* hashtable_iter_next - Advance an iterator
@@ -202,6 +171,6 @@ void *hashtable_iter_value(void *iter);
* @iter: The iterator
* @value: The value to set
*/
void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value);
void hashtable_iter_set(void *iter, json_t *value);
#endif

277
src/hashtable_seed.c Normal file
View File

@@ -0,0 +1,277 @@
/* Generate sizeof(uint32_t) bytes of as random data as possible to seed
the hash function.
*/
#ifdef HAVE_CONFIG_H
#include <jansson_private_config.h>
#endif
#include <stdio.h>
#include <time.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_SCHED_H
#include <sched.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if defined(_WIN32)
/* For GetModuleHandle(), GetProcAddress() and GetCurrentProcessId() */
#include <windows.h>
#endif
#include "jansson.h"
static uint32_t buf_to_uint32(char *data) {
size_t i;
uint32_t result = 0;
for (i = 0; i < sizeof(uint32_t); i++)
result = (result << 8) | (unsigned char)data[i];
return result;
}
/* /dev/urandom */
#if !defined(_WIN32) && defined(USE_URANDOM)
static int seed_from_urandom(uint32_t *seed) {
/* Use unbuffered I/O if we have open(), close() and read(). Otherwise
fall back to fopen() */
char data[sizeof(uint32_t)];
int ok;
#if defined(HAVE_OPEN) && defined(HAVE_CLOSE) && defined(HAVE_READ)
int urandom;
urandom = open("/dev/urandom", O_RDONLY);
if (urandom == -1)
return 1;
ok = read(urandom, data, sizeof(uint32_t)) == sizeof(uint32_t);
close(urandom);
#else
FILE *urandom;
urandom = fopen("/dev/urandom", "rb");
if (!urandom)
return 1;
ok = fread(data, 1, sizeof(uint32_t), urandom) == sizeof(uint32_t);
fclose(urandom);
#endif
if (!ok)
return 1;
*seed = buf_to_uint32(data);
return 0;
}
#endif
/* Windows Crypto API */
#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI)
#include <wincrypt.h>
typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv, LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType, DWORD dwFlags);
typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer);
typedef BOOL (WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV hProv, DWORD dwFlags);
static int seed_from_windows_cryptoapi(uint32_t *seed)
{
HINSTANCE hAdvAPI32 = NULL;
CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL;
CRYPTGENRANDOM pCryptGenRandom = NULL;
CRYPTRELEASECONTEXT pCryptReleaseContext = NULL;
HCRYPTPROV hCryptProv = 0;
BYTE data[sizeof(uint32_t)];
int ok;
hAdvAPI32 = GetModuleHandle(TEXT("advapi32.dll"));
if(hAdvAPI32 == NULL)
return 1;
pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress(hAdvAPI32, "CryptAcquireContextA");
if (!pCryptAcquireContext)
return 1;
pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32, "CryptGenRandom");
if (!pCryptGenRandom)
return 1;
pCryptReleaseContext = (CRYPTRELEASECONTEXT)GetProcAddress(hAdvAPI32, "CryptReleaseContext");
if (!pCryptReleaseContext)
return 1;
if (!pCryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
return 1;
ok = pCryptGenRandom(hCryptProv, sizeof(uint32_t), data);
pCryptReleaseContext(hCryptProv, 0);
if (!ok)
return 1;
*seed = buf_to_uint32((char *)data);
return 0;
}
#endif
/* gettimeofday() and getpid() */
static int seed_from_timestamp_and_pid(uint32_t *seed) {
#ifdef HAVE_GETTIMEOFDAY
/* XOR of seconds and microseconds */
struct timeval tv;
gettimeofday(&tv, NULL);
*seed = (uint32_t)tv.tv_sec ^ (uint32_t)tv.tv_usec;
#else
/* Seconds only */
*seed = (uint32_t)time(NULL);
#endif
/* XOR with PID for more randomness */
#if defined(_WIN32)
*seed ^= (uint32_t)GetCurrentProcessId();
#elif defined(HAVE_GETPID)
*seed ^= (uint32_t)getpid();
#endif
return 0;
}
static uint32_t generate_seed() {
uint32_t seed;
int done = 0;
#if !defined(_WIN32) && defined(USE_URANDOM)
if (!done && seed_from_urandom(&seed) == 0)
done = 1;
#endif
#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI)
if (!done && seed_from_windows_cryptoapi(&seed) == 0)
done = 1;
#endif
if (!done) {
/* Fall back to timestamp and PID if no better randomness is
available */
seed_from_timestamp_and_pid(&seed);
}
/* Make sure the seed is never zero */
if (seed == 0)
seed = 1;
return seed;
}
volatile uint32_t hashtable_seed = 0;
#if defined(HAVE_ATOMIC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32))
static volatile char seed_initialized = 0;
void json_object_seed(size_t seed) {
uint32_t new_seed = (uint32_t)seed;
if (hashtable_seed == 0) {
if (__atomic_test_and_set(&seed_initialized, __ATOMIC_RELAXED) == 0) {
/* Do the seeding ourselves */
if (new_seed == 0)
new_seed = generate_seed();
__atomic_store_n(&hashtable_seed, new_seed, __ATOMIC_RELEASE);
} else {
/* Wait for another thread to do the seeding */
do {
#ifdef HAVE_SCHED_YIELD
sched_yield();
#endif
} while(__atomic_load_n(&hashtable_seed, __ATOMIC_ACQUIRE) == 0);
}
}
}
#elif defined(HAVE_SYNC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32))
void json_object_seed(size_t seed) {
uint32_t new_seed = (uint32_t)seed;
if (hashtable_seed == 0) {
if (new_seed == 0) {
/* Explicit synchronization fences are not supported by the
__sync builtins, so every thread getting here has to
generate the seed value.
*/
new_seed = generate_seed();
}
do {
if (__sync_bool_compare_and_swap(&hashtable_seed, 0, new_seed)) {
/* We were the first to seed */
break;
} else {
/* Wait for another thread to do the seeding */
#ifdef HAVE_SCHED_YIELD
sched_yield();
#endif
}
} while(hashtable_seed == 0);
}
}
#elif defined(_WIN32)
static long seed_initialized = 0;
void json_object_seed(size_t seed) {
uint32_t new_seed = (uint32_t)seed;
if (hashtable_seed == 0) {
if (InterlockedIncrement(&seed_initialized) == 1) {
/* Do the seeding ourselves */
if (new_seed == 0)
new_seed = generate_seed();
hashtable_seed = new_seed;
} else {
/* Wait for another thread to do the seeding */
do {
SwitchToThread();
} while (hashtable_seed == 0);
}
}
}
#else
/* Fall back to a thread-unsafe version */
void json_object_seed(size_t seed) {
uint32_t new_seed = (uint32_t)seed;
if (hashtable_seed == 0) {
if (new_seed == 0)
new_seed = generate_seed();
hashtable_seed = new_seed;
}
}
#endif

70
src/jansson.def Normal file
View File

@@ -0,0 +1,70 @@
EXPORTS
json_delete
json_true
json_false
json_null
json_string
json_stringn
json_string_nocheck
json_stringn_nocheck
json_string_value
json_string_length
json_string_set
json_string_setn
json_string_set_nocheck
json_string_setn_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_update_existing
json_object_update_missing
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_object_key_to_iter
json_object_seed
json_dumps
json_dumpf
json_dump_file
json_dump_callback
json_loads
json_loadb
json_loadf
json_load_file
json_load_callback
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
json_get_alloc_funcs

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -12,7 +12,7 @@
#include <stdlib.h> /* for size_t */
#include <stdarg.h>
#include <jansson_config.h>
#include "jansson_config.h"
#ifdef __cplusplus
extern "C" {
@@ -21,11 +21,11 @@ extern "C" {
/* version */
#define JANSSON_MAJOR_VERSION 2
#define JANSSON_MINOR_VERSION 1
#define JANSSON_MINOR_VERSION 8
#define JANSSON_MICRO_VERSION 0
/* Micro version is omitted if it's 0 */
#define JANSSON_VERSION "2.1"
#define JANSSON_VERSION "2.8"
/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this
for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */
@@ -47,41 +47,51 @@ typedef enum {
JSON_NULL
} json_type;
typedef struct {
typedef struct json_t {
json_type type;
size_t refcount;
} json_t;
#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */
#if JSON_INTEGER_IS_LONG_LONG
#ifdef _WIN32
#define JSON_INTEGER_FORMAT "I64d"
#else
#define JSON_INTEGER_FORMAT "lld"
#endif
typedef long long json_int_t;
#else
#define JSON_INTEGER_FORMAT "ld"
typedef long json_int_t;
#endif /* JSON_INTEGER_IS_LONG_LONG */
#endif
#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)
#define json_is_string(json) (json && json_typeof(json) == JSON_STRING)
#define json_is_integer(json) (json && json_typeof(json) == JSON_INTEGER)
#define json_is_real(json) (json && json_typeof(json) == JSON_REAL)
#define json_is_object(json) ((json) && json_typeof(json) == JSON_OBJECT)
#define json_is_array(json) ((json) && json_typeof(json) == JSON_ARRAY)
#define json_is_string(json) ((json) && json_typeof(json) == JSON_STRING)
#define json_is_integer(json) ((json) && json_typeof(json) == JSON_INTEGER)
#define json_is_real(json) ((json) && json_typeof(json) == JSON_REAL)
#define json_is_number(json) (json_is_integer(json) || json_is_real(json))
#define json_is_true(json) (json && json_typeof(json) == JSON_TRUE)
#define json_is_false(json) (json && json_typeof(json) == JSON_FALSE)
#define json_is_true(json) ((json) && json_typeof(json) == JSON_TRUE)
#define json_is_false(json) ((json) && json_typeof(json) == JSON_FALSE)
#define json_boolean_value json_is_true
#define json_is_boolean(json) (json_is_true(json) || json_is_false(json))
#define json_is_null(json) (json && json_typeof(json) == JSON_NULL)
#define json_is_null(json) ((json) && json_typeof(json) == JSON_NULL)
/* construction, destruction, reference counting */
json_t *json_object(void);
json_t *json_array(void);
json_t *json_string(const char *value);
json_t *json_stringn(const char *value, size_t len);
json_t *json_string_nocheck(const char *value);
json_t *json_stringn_nocheck(const char *value, size_t len);
json_t *json_integer(json_int_t value);
json_t *json_real(double value);
json_t *json_true(void);
json_t *json_false(void);
#define json_boolean(val) ((val) ? json_true() : json_false())
json_t *json_null(void);
static JSON_INLINE
@@ -119,6 +129,7 @@ typedef struct {
/* getters, setters, manipulation */
void json_object_seed(size_t seed);
size_t json_object_size(const json_t *object);
json_t *json_object_get(const json_t *object, const char *key);
int json_object_set_new(json_t *object, const char *key, json_t *value);
@@ -126,13 +137,33 @@ 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);
int json_object_update_existing(json_t *object, json_t *other);
int json_object_update_missing(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_key_to_iter(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);
#define json_object_foreach(object, key, value) \
for(key = json_object_iter_key(json_object_iter(object)); \
key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key))))
#define json_object_foreach_safe(object, n, key, value) \
for(key = json_object_iter_key(json_object_iter(object)), \
n = json_object_iter_next(object, json_object_key_to_iter(key)); \
key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
key = json_object_iter_key(n), \
n = json_object_iter_next(object, json_object_key_to_iter(key)))
#define json_array_foreach(array, index, value) \
for(index = 0; \
index < json_array_size(array) && (value = json_array_get(array, index)); \
index++)
static JSON_INLINE
int json_object_set(json_t *object, const char *key, json_t *value)
{
@@ -161,9 +192,9 @@ 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)
int json_array_set(json_t *array, size_t ind, json_t *value)
{
return json_array_set_new(array, index, json_incref(value));
return json_array_set_new(array, ind, json_incref(value));
}
static JSON_INLINE
@@ -173,22 +204,24 @@ int json_array_append(json_t *array, json_t *value)
}
static JSON_INLINE
int json_array_insert(json_t *array, size_t index, json_t *value)
int json_array_insert(json_t *array, size_t ind, json_t *value)
{
return json_array_insert_new(array, index, json_incref(value));
return json_array_insert_new(array, ind, json_incref(value));
}
const char *json_string_value(const json_t *string);
size_t json_string_length(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_setn(json_t *string, const char *value, size_t len);
int json_string_set_nocheck(json_t *string, const char *value);
int json_string_setn_nocheck(json_t *string, const char *value, size_t len);
int json_integer_set(json_t *integer, json_int_t value);
int json_real_set(json_t *real, double value);
/* pack, unpack */
json_t *json_pack(const char *fmt, ...);
@@ -211,33 +244,44 @@ 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);
json_t *json_deep_copy(const json_t *value);
/* decoding */
#define JSON_REJECT_DUPLICATES 0x1
#define JSON_DISABLE_EOF_CHECK 0x2
#define JSON_REJECT_DUPLICATES 0x1
#define JSON_DISABLE_EOF_CHECK 0x2
#define JSON_DECODE_ANY 0x4
#define JSON_DECODE_INT_AS_REAL 0x8
#define JSON_ALLOW_NUL 0x10
typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data);
json_t *json_loads(const char *input, size_t flags, json_error_t *error);
json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error);
json_t *json_loadf(FILE *input, size_t flags, json_error_t *error);
json_t *json_load_file(const char *path, size_t flags, json_error_t *error);
json_t *json_load_callback(json_load_callback_t callback, void *data, 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
#define JSON_MAX_INDENT 0x1F
#define JSON_INDENT(n) ((n) & JSON_MAX_INDENT)
#define JSON_COMPACT 0x20
#define JSON_ENSURE_ASCII 0x40
#define JSON_SORT_KEYS 0x80
#define JSON_PRESERVE_ORDER 0x100
#define JSON_ENCODE_ANY 0x200
#define JSON_ESCAPE_SLASH 0x400
#define JSON_REAL_PRECISION(n) (((n) & 0x1F) << 11)
typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);
char *json_dumps(const json_t *json, size_t flags);
int json_dumpf(const json_t *json, FILE *output, size_t flags);
int json_dump_file(const json_t *json, const char *path, size_t flags);
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags);
/* custom memory allocation */
@@ -245,6 +289,7 @@ 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);
void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn);
#ifdef __cplusplus
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2010-2014 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.
@@ -27,8 +27,17 @@
#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. */
/* If your compiler supports the `long long` type and the strtoll()
library function, JSON_INTEGER_IS_LONG_LONG is defined to 1,
otherwise to 0. */
#define JSON_INTEGER_IS_LONG_LONG @json_have_long_long@
/* If locale.h and localeconv() are available, define to 1,
otherwise to 0. */
#define JSON_HAVE_LOCALECONV @json_have_localeconv@
/* Maximum recursion depth for parsing JSON input.
This limits the depth of e.g. array-within-array constructions. */
#define JSON_PARSER_MAX_DEPTH 2048
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 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.
@@ -11,6 +11,7 @@
#include <stddef.h>
#include "jansson.h"
#include "hashtable.h"
#include "strbuffer.h"
#define container_of(ptr_, type_, member_) \
((type_ *)((char *)ptr_ - offsetof(type_, member_)))
@@ -33,7 +34,6 @@
typedef struct {
json_t json;
hashtable_t hashtable;
size_t serial;
int visited;
} json_object_t;
@@ -48,6 +48,7 @@ typedef struct {
typedef struct {
json_t json;
char *value;
size_t length;
} json_string_t;
typedef struct {
@@ -63,19 +64,13 @@ typedef struct {
#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_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);
/* Create a string by taking ownership of an existing buffer */
json_t *jsonp_stringn_nocheck_own(const char *value, size_t len);
/* Error message formatting */
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,
@@ -83,9 +78,31 @@ void jsonp_error_set(json_error_t *error, int line, int column,
void jsonp_error_vset(json_error_t *error, int line, int column,
size_t position, const char *msg, va_list ap);
/* Locale independent string<->double conversions */
int jsonp_strtod(strbuffer_t *strbuffer, double *out);
int jsonp_dtostr(char *buffer, size_t size, double value, int prec);
/* Wrappers for custom memory functions */
void* jsonp_malloc(size_t size);
void jsonp_free(void *ptr);
char *jsonp_strndup(const char *str, size_t length);
char *jsonp_strdup(const char *str);
char *jsonp_strndup(const char *str, size_t len);
/* Windows compatibility */
#if defined(_WIN32) || defined(WIN32)
# if defined(_MSC_VER) /* MS compiller */
# if (_MSC_VER < 1900) && !defined(snprintf) /* snprintf not defined yet & not introduced */
# define snprintf _snprintf
# endif
# if (_MSC_VER < 1500) && !defined(vsnprintf) /* vsnprintf not defined yet & not introduced */
# define vsnprintf(b,c,f,a) _vsnprintf(b,c,f,a)
# endif
# else /* Other Windows compiller, old definition */
# define snprintf _snprintf
# define vsnprintf _vsnprintf
# endif
#endif
#endif

View File

@@ -1,12 +1,14 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 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 _GNU_SOURCE
#define _GNU_SOURCE
#include <ctype.h>
#endif
#include <errno.h>
#include <limits.h>
#include <stdio.h>
@@ -14,7 +16,7 @@
#include <string.h>
#include <assert.h>
#include <jansson.h>
#include "jansson.h"
#include "jansson_private.h"
#include "strbuffer.h"
#include "utf.h"
@@ -32,6 +34,14 @@
#define TOKEN_FALSE 260
#define TOKEN_NULL 261
/* Locale independent versions of isxxx() functions */
#define l_isupper(c) ('A' <= (c) && (c) <= 'Z')
#define l_islower(c) ('a' <= (c) && (c) <= 'z')
#define l_isalpha(c) (l_isupper(c) || l_islower(c))
#define l_isdigit(c) ('0' <= (c) && (c) <= '9')
#define l_isxdigit(c) \
(l_isdigit(c) || ('A' <= (c) && (c) <= 'F') || ('a' <= (c) && (c) <= 'f'))
/* 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(). */
@@ -41,7 +51,7 @@ typedef struct {
get_func get;
void *data;
char buffer[5];
int buffer_pos;
size_t buffer_pos;
int state;
int line;
int column, last_column;
@@ -51,9 +61,14 @@ typedef struct {
typedef struct {
stream_t stream;
strbuffer_t saved_text;
size_t flags;
size_t depth;
int token;
union {
char *string;
struct {
char *val;
size_t len;
} string;
json_int_t integer;
double real;
} value;
@@ -69,6 +84,7 @@ static void error_set(json_error_t *error, const lex_t *lex,
{
va_list ap;
char msg_text[JSON_ERROR_TEXT_LENGTH];
char msg_with_context[JSON_ERROR_TEXT_LENGTH];
int line = -1, col = -1;
size_t pos = 0;
@@ -79,12 +95,12 @@ static void error_set(json_error_t *error, const lex_t *lex,
va_start(ap, msg);
vsnprintf(msg_text, JSON_ERROR_TEXT_LENGTH, msg, ap);
msg_text[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
va_end(ap);
if(lex)
{
const char *saved_text = strbuffer_value(&lex->saved_text);
char msg_with_context[JSON_ERROR_TEXT_LENGTH];
line = lex->stream.line;
col = lex->stream.column;
@@ -95,6 +111,7 @@ static void error_set(json_error_t *error, const lex_t *lex,
if(lex->saved_text.length <= 20) {
snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
"%s near '%s'", msg_text, saved_text);
msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
result = msg_with_context;
}
}
@@ -107,6 +124,7 @@ static void error_set(json_error_t *error, const lex_t *lex,
else {
snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
"%s near end of file", msg_text);
msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
result = msg_with_context;
}
}
@@ -153,7 +171,7 @@ static int stream_get(stream_t *stream, json_error_t *error)
if(0x80 <= c && c <= 0xFF)
{
/* multi-byte UTF-8 sequence */
int i, count;
size_t i, count;
count = utf8_check_first(c);
if(!count)
@@ -240,9 +258,18 @@ static void lex_unget(lex_t *lex, int c)
static void lex_unget_unsave(lex_t *lex, int c)
{
if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR) {
/* Since we treat warnings as errors, when assertions are turned
* off the "d" variable would be set but never used. Which is
* treated as an error by GCC.
*/
#ifndef NDEBUG
char d;
#endif
stream_unget(&lex->stream, c);
d = strbuffer_pop(&lex->saved_text);
#ifndef NDEBUG
d =
#endif
strbuffer_pop(&lex->saved_text);
assert(c == d);
}
}
@@ -257,6 +284,13 @@ static void lex_save_cached(lex_t *lex)
}
}
static void lex_free_string(lex_t *lex)
{
jsonp_free(lex->value.string.val);
lex->value.string.val = NULL;
lex->value.string.len = 0;
}
/* assumes that str points to 'u' plus at least 4 valid hex digits */
static int32_t decode_unicode_escape(const char *str)
{
@@ -268,14 +302,14 @@ static int32_t decode_unicode_escape(const char *str)
for(i = 1; i <= 4; i++) {
char c = str[i];
value <<= 4;
if(isdigit(c))
if(l_isdigit(c))
value += c - '0';
else if(islower(c))
else if(l_islower(c))
value += c - 'a' + 10;
else if(isupper(c))
else if(l_isupper(c))
value += c - 'A' + 10;
else
assert(0);
return -1;
}
return value;
@@ -288,7 +322,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
char *t;
int i;
lex->value.string = NULL;
lex->value.string.val = NULL;
lex->token = TOKEN_INVALID;
c = lex_get_save(lex, error);
@@ -317,7 +351,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
if(c == 'u') {
c = lex_get_save(lex, error);
for(i = 0; i < 4; i++) {
if(!isxdigit(c)) {
if(!l_isxdigit(c)) {
error_set(error, lex, "invalid escape");
goto out;
}
@@ -343,14 +377,12 @@ 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 = jsonp_malloc(lex->saved_text.length + 1);
if(!lex->value.string) {
t = jsonp_malloc(lex->saved_text.length + 1);
if(!t) {
/* this is not very nice, since TOKEN_INVALID is returned */
goto out;
}
/* the target */
t = lex->value.string;
lex->value.string.val = t;
/* + 1 to skip the " */
p = strbuffer_value(&lex->saved_text) + 1;
@@ -359,17 +391,24 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
if(*p == '\\') {
p++;
if(*p == 'u') {
char buffer[4];
int length;
size_t length;
int32_t value;
value = decode_unicode_escape(p);
if(value < 0) {
error_set(error, lex, "invalid Unicode escape '%.6s'", p - 1);
goto out;
}
p += 5;
if(0xD800 <= value && value <= 0xDBFF) {
/* surrogate pair */
if(*p == '\\' && *(p + 1) == 'u') {
int32_t value2 = decode_unicode_escape(++p);
if(value2 < 0) {
error_set(error, lex, "invalid Unicode escape '%.6s'", p - 1);
goto out;
}
p += 5;
if(0xDC00 <= value2 && value2 <= 0xDFFF) {
@@ -398,16 +437,9 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
error_set(error, lex, "invalid Unicode '\\u%04X'", value);
goto out;
}
else if(value == 0)
{
error_set(error, lex, "\\u0000 is not allowed");
goto out;
}
if(utf8_encode(value, buffer, &length))
if(utf8_encode(value, t, &length))
assert(0);
memcpy(t, buffer, length);
t += length;
}
else {
@@ -429,24 +461,31 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
*(t++) = *(p++);
}
*t = '\0';
lex->value.string.len = t - lex->value.string.val;
lex->token = TOKEN_STRING;
return;
out:
jsonp_free(lex->value.string);
lex_free_string(lex);
}
#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */
#if JSON_INTEGER_IS_LONG_LONG
#ifdef _MSC_VER /* Microsoft Visual Studio */
#define json_strtoint _strtoi64
#else
#define json_strtoint strtoll
#endif
#else
#define json_strtoint strtol
#endif
#endif
static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
{
const char *saved_text;
char *end;
double value;
double doubleval;
lex->token = TOKEN_INVALID;
@@ -455,32 +494,34 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
if(c == '0') {
c = lex_get_save(lex, error);
if(isdigit(c)) {
if(l_isdigit(c)) {
lex_unget_unsave(lex, c);
goto out;
}
}
else if(isdigit(c)) {
c = lex_get_save(lex, error);
while(isdigit(c))
else if(l_isdigit(c)) {
do
c = lex_get_save(lex, error);
while(l_isdigit(c));
}
else {
lex_unget_unsave(lex, c);
goto out;
}
if(c != '.' && c != 'E' && c != 'e') {
json_int_t value;
if(!(lex->flags & JSON_DECODE_INT_AS_REAL) &&
c != '.' && c != 'E' && c != 'e')
{
json_int_t intval;
lex_unget_unsave(lex, c);
saved_text = strbuffer_value(&lex->saved_text);
errno = 0;
value = json_strtoint(saved_text, &end, 10);
intval = json_strtoint(saved_text, &end, 10);
if(errno == ERANGE) {
if(value < 0)
if(intval < 0)
error_set(error, lex, "too big negative integer");
else
error_set(error, lex, "too big integer");
@@ -490,21 +531,21 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
assert(end == saved_text + lex->saved_text.length);
lex->token = TOKEN_INTEGER;
lex->value.integer = value;
lex->value.integer = intval;
return 0;
}
if(c == '.') {
c = lex_get(lex, error);
if(!isdigit(c)) {
if(!l_isdigit(c)) {
lex_unget(lex, c);
goto out;
}
lex_save(lex, c);
c = lex_get_save(lex, error);
while(isdigit(c))
do
c = lex_get_save(lex, error);
while(l_isdigit(c));
}
if(c == 'E' || c == 'e') {
@@ -512,30 +553,25 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
if(c == '+' || c == '-')
c = lex_get_save(lex, error);
if(!isdigit(c)) {
if(!l_isdigit(c)) {
lex_unget_unsave(lex, c);
goto out;
}
c = lex_get_save(lex, error);
while(isdigit(c))
do
c = lex_get_save(lex, error);
while(l_isdigit(c));
}
lex_unget_unsave(lex, c);
saved_text = strbuffer_value(&lex->saved_text);
errno = 0;
value = strtod(saved_text, &end);
assert(end == saved_text + lex->saved_text.length);
if(errno == ERANGE && value != 0) {
if(jsonp_strtod(&lex->saved_text, &doubleval)) {
error_set(error, lex, "real number overflow");
goto out;
}
lex->token = TOKEN_REAL;
lex->value.real = value;
lex->value.real = doubleval;
return 0;
out:
@@ -548,14 +584,12 @@ static int lex_scan(lex_t *lex, json_error_t *error)
strbuffer_clear(&lex->saved_text);
if(lex->token == TOKEN_STRING) {
jsonp_free(lex->value.string);
lex->value.string = NULL;
}
if(lex->token == TOKEN_STRING)
lex_free_string(lex);
c = lex_get(lex, error);
while(c == ' ' || c == '\t' || c == '\n' || c == '\r')
do
c = lex_get(lex, error);
while(c == ' ' || c == '\t' || c == '\n' || c == '\r');
if(c == STREAM_STATE_EOF) {
lex->token = TOKEN_EOF;
@@ -575,18 +609,18 @@ static int lex_scan(lex_t *lex, json_error_t *error)
else if(c == '"')
lex_scan_string(lex, error);
else if(isdigit(c) || c == '-') {
else if(l_isdigit(c) || c == '-') {
if(lex_scan_number(lex, c, error))
goto out;
}
else if(isupper(c) || islower(c)) {
else if(l_isalpha(c)) {
/* eat up the whole identifier for clearer error messages */
const char *saved_text;
c = lex_get_save(lex, error);
while(isupper(c) || islower(c))
do
c = lex_get_save(lex, error);
while(l_isalpha(c));
lex_unget_unsave(lex, c);
saved_text = strbuffer_value(&lex->saved_text);
@@ -612,23 +646,25 @@ out:
return lex->token;
}
static char *lex_steal_string(lex_t *lex)
static char *lex_steal_string(lex_t *lex, size_t *out_len)
{
char *result = NULL;
if(lex->token == TOKEN_STRING)
{
result = lex->value.string;
lex->value.string = NULL;
if(lex->token == TOKEN_STRING) {
result = lex->value.string.val;
*out_len = lex->value.string.len;
lex->value.string.val = NULL;
lex->value.string.len = 0;
}
return result;
}
static int lex_init(lex_t *lex, get_func get, void *data)
static int lex_init(lex_t *lex, get_func get, size_t flags, void *data)
{
stream_init(&lex->stream, get, data);
if(strbuffer_init(&lex->saved_text))
return -1;
lex->flags = flags;
lex->token = TOKEN_INVALID;
return 0;
}
@@ -636,7 +672,7 @@ static int lex_init(lex_t *lex, get_func get, void *data)
static void lex_close(lex_t *lex)
{
if(lex->token == TOKEN_STRING)
jsonp_free(lex->value.string);
lex_free_string(lex);
strbuffer_close(&lex->saved_text);
}
@@ -657,6 +693,7 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
while(1) {
char *key;
size_t len;
json_t *value;
if(lex->token != TOKEN_STRING) {
@@ -664,9 +701,14 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
goto error;
}
key = lex_steal_string(lex);
key = lex_steal_string(lex, &len);
if(!key)
return NULL;
if (memchr(key, '\0', len)) {
jsonp_free(key);
error_set(error, lex, "NUL byte in object key not supported");
goto error;
}
if(flags & JSON_REJECT_DUPLICATES) {
if(json_object_get(object, key)) {
@@ -762,9 +804,29 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
{
json_t *json;
lex->depth++;
if(lex->depth > JSON_PARSER_MAX_DEPTH) {
error_set(error, lex, "maximum parsing depth reached");
return NULL;
}
switch(lex->token) {
case TOKEN_STRING: {
json = json_string_nocheck(lex->value.string);
const char *value = lex->value.string.val;
size_t len = lex->value.string.len;
if(!(flags & JSON_ALLOW_NUL)) {
if(memchr(value, '\0', len)) {
error_set(error, lex, "\\u0000 is not allowed without JSON_ALLOW_NUL");
return NULL;
}
}
json = jsonp_stringn_nocheck_own(value, len);
if(json) {
lex->value.string.val = NULL;
lex->value.string.len = 0;
}
break;
}
@@ -810,6 +872,7 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
if(!json)
return NULL;
lex->depth--;
return json;
}
@@ -817,10 +880,14 @@ static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
{
json_t *result;
lex->depth = 0;
lex_scan(lex, error);
if(lex->token != '[' && lex->token != '{') {
error_set(error, lex, "'[' or '{' expected");
return NULL;
if(!(flags & JSON_DECODE_ANY)) {
if(lex->token != '[' && lex->token != '{') {
error_set(error, lex, "'[' or '{' expected");
return NULL;
}
}
result = parse_value(lex, flags, error);
@@ -832,10 +899,15 @@ static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
if(lex->token != TOKEN_EOF) {
error_set(error, lex, "end of file expected");
json_decref(result);
result = NULL;
return NULL;
}
}
if(error) {
/* Save the position even though there was no error */
error->position = (int)lex->stream.position;
}
return result;
}
@@ -865,13 +937,19 @@ json_t *json_loads(const char *string, size_t flags, json_error_t *error)
json_t *result;
string_data_t stream_data;
jsonp_error_init(error, "<string>");
if (string == NULL) {
error_set(error, NULL, "wrong arguments");
return NULL;
}
stream_data.data = string;
stream_data.pos = 0;
if(lex_init(&lex, string_get, (void *)&stream_data))
if(lex_init(&lex, string_get, flags, (void *)&stream_data))
return NULL;
jsonp_error_init(error, "<string>");
result = parse_json(&lex, flags, error);
lex_close(&lex);
@@ -903,14 +981,20 @@ json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t
json_t *result;
buffer_data_t stream_data;
jsonp_error_init(error, "<buffer>");
if (buffer == NULL) {
error_set(error, NULL, "wrong arguments");
return NULL;
}
stream_data.data = buffer;
stream_data.pos = 0;
stream_data.len = buflen;
if(lex_init(&lex, buffer_get, (void *)&stream_data))
if(lex_init(&lex, buffer_get, flags, (void *)&stream_data))
return NULL;
jsonp_error_init(error, "<buffer>");
result = parse_json(&lex, flags, error);
lex_close(&lex);
@@ -923,15 +1007,21 @@ json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
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);
if (input == NULL) {
error_set(error, NULL, "wrong arguments");
return NULL;
}
if(lex_init(&lex, (get_func)fgetc, flags, input))
return NULL;
result = parse_json(&lex, flags, error);
lex_close(&lex);
@@ -945,7 +1035,12 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
jsonp_error_init(error, path);
fp = fopen(path, "r");
if (path == NULL) {
error_set(error, NULL, "wrong arguments");
return NULL;
}
fp = fopen(path, "rb");
if(!fp)
{
error_set(error, NULL, "unable to open %s: %s",
@@ -958,3 +1053,58 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
fclose(fp);
return result;
}
#define MAX_BUF_LEN 1024
typedef struct
{
char data[MAX_BUF_LEN];
size_t len;
size_t pos;
json_load_callback_t callback;
void *arg;
} callback_data_t;
static int callback_get(void *data)
{
char c;
callback_data_t *stream = data;
if(stream->pos >= stream->len) {
stream->pos = 0;
stream->len = stream->callback(stream->data, MAX_BUF_LEN, stream->arg);
if(stream->len == 0 || stream->len == (size_t)-1)
return EOF;
}
c = stream->data[stream->pos];
stream->pos++;
return (unsigned char)c;
}
json_t *json_load_callback(json_load_callback_t callback, void *arg, size_t flags, json_error_t *error)
{
lex_t lex;
json_t *result;
callback_data_t stream_data;
memset(&stream_data, 0, sizeof(stream_data));
stream_data.callback = callback;
stream_data.arg = arg;
jsonp_error_init(error, "<callback>");
if (callback == NULL) {
error_set(error, NULL, "wrong arguments");
return NULL;
}
if(lex_init(&lex, (get_func)callback_get, flags, &stream_data))
return NULL;
result = parse_json(&lex, flags, error);
lex_close(&lex);
return result;
}

381
src/lookup3.h Normal file
View File

@@ -0,0 +1,381 @@
/*
-------------------------------------------------------------------------------
lookup3.c, by Bob Jenkins, May 2006, Public Domain.
These are functions for producing 32-bit hashes for hash table lookup.
hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
are externally useful functions. Routines to test the hash are included
if SELF_TEST is defined. You can use this free for any purpose. It's in
the public domain. It has no warranty.
You probably want to use hashlittle(). hashlittle() and hashbig()
hash byte arrays. hashlittle() is is faster than hashbig() on
little-endian machines. Intel and AMD are little-endian machines.
On second thought, you probably want hashlittle2(), which is identical to
hashlittle() except it returns two 32-bit hashes for the price of one.
You could implement hashbig2() if you wanted but I haven't bothered here.
If you want to find a hash of, say, exactly 7 integers, do
a = i1; b = i2; c = i3;
mix(a,b,c);
a += i4; b += i5; c += i6;
mix(a,b,c);
a += i7;
final(a,b,c);
then use c as the hash value. If you have a variable length array of
4-byte integers to hash, use hashword(). If you have a byte array (like
a character string), use hashlittle(). If you have several byte arrays, or
a mix of things, see the comments above hashlittle().
Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
then mix those integers. This is fast (you can do a lot more thorough
mixing with 12*3 instructions on 3 integers than you can with 3 instructions
on 1 byte), but shoehorning those bytes into integers efficiently is messy.
-------------------------------------------------------------------------------
*/
#include <stdlib.h>
#ifdef HAVE_CONFIG_H
#include <jansson_private_config.h>
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h> /* defines uint32_t etc */
#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h> /* attempt to define endianness */
#endif
#ifdef HAVE_ENDIAN_H
# include <endian.h> /* attempt to define endianness */
#endif
/*
* My best guess at if you are big-endian or little-endian. This may
* need adjustment.
*/
#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
__BYTE_ORDER == __LITTLE_ENDIAN) || \
(defined(i386) || defined(__i386__) || defined(__i486__) || \
defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL))
# define HASH_LITTLE_ENDIAN 1
# define HASH_BIG_ENDIAN 0
#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
__BYTE_ORDER == __BIG_ENDIAN) || \
(defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel))
# define HASH_LITTLE_ENDIAN 0
# define HASH_BIG_ENDIAN 1
#else
# define HASH_LITTLE_ENDIAN 0
# define HASH_BIG_ENDIAN 0
#endif
#define hashsize(n) ((uint32_t)1<<(n))
#define hashmask(n) (hashsize(n)-1)
#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
/*
-------------------------------------------------------------------------------
mix -- mix 3 32-bit values reversibly.
This is reversible, so any information in (a,b,c) before mix() is
still in (a,b,c) after mix().
If four pairs of (a,b,c) inputs are run through mix(), or through
mix() in reverse, there are at least 32 bits of the output that
are sometimes the same for one pair and different for another pair.
This was tested for:
* pairs that differed by one bit, by two bits, in any combination
of top bits of (a,b,c), or in any combination of bottom bits of
(a,b,c).
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
is commonly produced by subtraction) look like a single 1-bit
difference.
* the base values were pseudorandom, all zero but one bit set, or
all zero plus a counter that starts at zero.
Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
satisfy this are
4 6 8 16 19 4
9 15 3 18 27 15
14 9 3 7 17 3
Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
for "differ" defined as + with a one-bit base and a two-bit delta. I
used http://burtleburtle.net/bob/hash/avalanche.html to choose
the operations, constants, and arrangements of the variables.
This does not achieve avalanche. There are input bits of (a,b,c)
that fail to affect some output bits of (a,b,c), especially of a. The
most thoroughly mixed value is c, but it doesn't really even achieve
avalanche in c.
This allows some parallelism. Read-after-writes are good at doubling
the number of bits affected, so the goal of mixing pulls in the opposite
direction as the goal of parallelism. I did what I could. Rotates
seem to cost as much as shifts on every machine I could lay my hands
on, and rotates are much kinder to the top and bottom bits, so I used
rotates.
-------------------------------------------------------------------------------
*/
#define mix(a,b,c) \
{ \
a -= c; a ^= rot(c, 4); c += b; \
b -= a; b ^= rot(a, 6); a += c; \
c -= b; c ^= rot(b, 8); b += a; \
a -= c; a ^= rot(c,16); c += b; \
b -= a; b ^= rot(a,19); a += c; \
c -= b; c ^= rot(b, 4); b += a; \
}
/*
-------------------------------------------------------------------------------
final -- final mixing of 3 32-bit values (a,b,c) into c
Pairs of (a,b,c) values differing in only a few bits will usually
produce values of c that look totally different. This was tested for
* pairs that differed by one bit, by two bits, in any combination
of top bits of (a,b,c), or in any combination of bottom bits of
(a,b,c).
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
is commonly produced by subtraction) look like a single 1-bit
difference.
* the base values were pseudorandom, all zero but one bit set, or
all zero plus a counter that starts at zero.
These constants passed:
14 11 25 16 4 14 24
12 14 25 16 4 14 24
and these came close:
4 8 15 26 3 22 24
10 8 15 26 3 22 24
11 8 15 26 3 22 24
-------------------------------------------------------------------------------
*/
#define final(a,b,c) \
{ \
c ^= b; c -= rot(b,14); \
a ^= c; a -= rot(c,11); \
b ^= a; b -= rot(a,25); \
c ^= b; c -= rot(b,16); \
a ^= c; a -= rot(c,4); \
b ^= a; b -= rot(a,14); \
c ^= b; c -= rot(b,24); \
}
/*
-------------------------------------------------------------------------------
hashlittle() -- hash a variable-length key into a 32-bit value
k : the key (the unaligned variable-length array of bytes)
length : the length of the key, counting by bytes
initval : can be any 4-byte value
Returns a 32-bit value. Every bit of the key affects every bit of
the return value. Two keys differing by one or two bits will have
totally different hash values.
The best hash table sizes are powers of 2. There is no need to do
mod a prime (mod is sooo slow!). If you need less than 32 bits,
use a bitmask. For example, if you need only 10 bits, do
h = (h & hashmask(10));
In which case, the hash table should have hashsize(10) elements.
If you are hashing n strings (uint8_t **)k, do it like this:
for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);
By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this
code any way you wish, private, educational, or commercial. It's free.
Use for hash table lookup, or anything where one collision in 2^^32 is
acceptable. Do NOT use for cryptographic purposes.
-------------------------------------------------------------------------------
*/
static uint32_t hashlittle(const void *key, size_t length, uint32_t initval)
{
uint32_t a,b,c; /* internal state */
union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
/* Set up the internal state */
a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
u.ptr = key;
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
/* Detect Valgrind or AddressSanitizer */
#ifdef VALGRIND
# define NO_MASKING_TRICK 1
#else
# if defined(__has_feature) /* Clang */
# if __has_feature(address_sanitizer) /* is ASAN enabled? */
# define NO_MASKING_TRICK 1
# endif
# else
# if defined(__SANITIZE_ADDRESS__) /* GCC 4.8.x, is ASAN enabled? */
# define NO_MASKING_TRICK 1
# endif
# endif
#endif
#ifdef NO_MASKING_TRICK
const uint8_t *k8;
#endif
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
while (length > 12)
{
a += k[0];
b += k[1];
c += k[2];
mix(a,b,c);
length -= 12;
k += 3;
}
/*----------------------------- handle the last (probably partial) block */
/*
* "k[2]&0xffffff" actually reads beyond the end of the string, but
* then masks off the part it's not allowed to read. Because the
* string is aligned, the masked-off tail is in the same word as the
* rest of the string. Every machine with memory protection I've seen
* does it on word boundaries, so is OK with this. But VALGRIND will
* still catch it and complain. The masking trick does make the hash
* noticably faster for short strings (like English words).
*/
#ifndef NO_MASKING_TRICK
switch(length)
{
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
case 8 : b+=k[1]; a+=k[0]; break;
case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
case 6 : b+=k[1]&0xffff; a+=k[0]; break;
case 5 : b+=k[1]&0xff; a+=k[0]; break;
case 4 : a+=k[0]; break;
case 3 : a+=k[0]&0xffffff; break;
case 2 : a+=k[0]&0xffff; break;
case 1 : a+=k[0]&0xff; break;
case 0 : return c; /* zero length strings require no mixing */
}
#else /* make valgrind happy */
k8 = (const uint8_t *)k;
switch(length)
{
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
case 9 : c+=k8[8]; /* fall through */
case 8 : b+=k[1]; a+=k[0]; break;
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
case 5 : b+=k8[4]; /* fall through */
case 4 : a+=k[0]; break;
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
case 1 : a+=k8[0]; break;
case 0 : return c;
}
#endif /* !valgrind */
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
const uint8_t *k8;
/*--------------- all but last block: aligned reads and different mixing */
while (length > 12)
{
a += k[0] + (((uint32_t)k[1])<<16);
b += k[2] + (((uint32_t)k[3])<<16);
c += k[4] + (((uint32_t)k[5])<<16);
mix(a,b,c);
length -= 12;
k += 6;
}
/*----------------------------- handle the last (probably partial) block */
k8 = (const uint8_t *)k;
switch(length)
{
case 12: c+=k[4]+(((uint32_t)k[5])<<16);
b+=k[2]+(((uint32_t)k[3])<<16);
a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
case 10: c+=k[4];
b+=k[2]+(((uint32_t)k[3])<<16);
a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 9 : c+=k8[8]; /* fall through */
case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
case 6 : b+=k[2];
a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 5 : b+=k8[4]; /* fall through */
case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
case 2 : a+=k[0];
break;
case 1 : a+=k8[0];
break;
case 0 : return c; /* zero length requires no mixing */
}
} else { /* need to read the key one byte at a time */
const uint8_t *k = (const uint8_t *)key;
/*--------------- all but the last block: affect some 32 bits of (a,b,c) */
while (length > 12)
{
a += k[0];
a += ((uint32_t)k[1])<<8;
a += ((uint32_t)k[2])<<16;
a += ((uint32_t)k[3])<<24;
b += k[4];
b += ((uint32_t)k[5])<<8;
b += ((uint32_t)k[6])<<16;
b += ((uint32_t)k[7])<<24;
c += k[8];
c += ((uint32_t)k[9])<<8;
c += ((uint32_t)k[10])<<16;
c += ((uint32_t)k[11])<<24;
mix(a,b,c);
length -= 12;
k += 12;
}
/*-------------------------------- last block: affect all 32 bits of (c) */
switch(length) /* all the case statements fall through */
{
case 12: c+=((uint32_t)k[11])<<24;
case 11: c+=((uint32_t)k[10])<<16;
case 10: c+=((uint32_t)k[9])<<8;
case 9 : c+=k[8];
case 8 : b+=((uint32_t)k[7])<<24;
case 7 : b+=((uint32_t)k[6])<<16;
case 6 : b+=((uint32_t)k[5])<<8;
case 5 : b+=k[4];
case 4 : a+=((uint32_t)k[3])<<24;
case 3 : a+=((uint32_t)k[2])<<16;
case 2 : a+=((uint32_t)k[1])<<8;
case 1 : a+=k[0];
break;
case 0 : return c;
}
}
final(a,b,c);
return c;
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2011 Basile Starynkevitch <basile@starynkevitch.net>
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2011-2012 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.
@@ -9,9 +9,13 @@
#include <stdlib.h>
#include <string.h>
#include <jansson.h>
#include "jansson.h"
#include "jansson_private.h"
/* C89 allows these to be macros */
#undef malloc
#undef free
/* memory function pointers */
static json_malloc_t do_malloc = malloc;
static json_free_t do_free = free;
@@ -33,14 +37,20 @@ void jsonp_free(void *ptr)
}
char *jsonp_strdup(const char *str)
{
return jsonp_strndup(str, strlen(str));
}
char *jsonp_strndup(const char *str, size_t len)
{
char *new_str;
new_str = jsonp_malloc(strlen(str) + 1);
new_str = jsonp_malloc(len + 1);
if(!new_str)
return NULL;
strcpy(new_str, str);
memcpy(new_str, str, len);
new_str[len] = '\0';
return new_str;
}
@@ -49,3 +59,11 @@ void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn)
do_malloc = malloc_fn;
do_free = free_fn;
}
void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn)
{
if (malloc_fn)
*malloc_fn = do_malloc;
if (free_fn)
*free_fn = do_free;
}

View File

@@ -1,27 +1,39 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2011 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2011-2012 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.h"
#include "jansson_private.h"
#include "utf.h"
typedef struct {
int line;
int column;
size_t pos;
char token;
} token_t;
typedef struct {
const char *start;
const char *fmt;
char token;
token_t prev_token;
token_t token;
token_t next_token;
json_error_t *error;
size_t flags;
int line;
int column;
size_t pos;
} scanner_t;
static const char *type_names[] = {
#define token(scanner) ((scanner)->token.token)
static const char * const type_names[] = {
"object",
"array",
"string",
@@ -34,8 +46,7 @@ static const char *type_names[] = {
#define type_name(x) type_names[json_typeof(x)]
static const char *unpack_value_starters = "{[siIbfFOon";
static const char unpack_value_starters[] = "{[siIbfFOon";
static void scanner_init(scanner_t *s, json_error_t *error,
size_t flags, const char *fmt)
@@ -43,14 +54,28 @@ static void scanner_init(scanner_t *s, json_error_t *error,
s->error = error;
s->flags = flags;
s->fmt = s->start = fmt;
memset(&s->prev_token, 0, sizeof(token_t));
memset(&s->token, 0, sizeof(token_t));
memset(&s->next_token, 0, sizeof(token_t));
s->line = 1;
s->column = 0;
s->pos = 0;
}
static void next_token(scanner_t *s)
{
const char *t = s->fmt;
const char *t;
s->prev_token = s->token;
if(s->next_token.line) {
s->token = s->next_token;
s->next_token.line = 0;
return;
}
t = s->fmt;
s->column++;
s->pos++;
/* skip space and ignored chars */
while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') {
@@ -61,23 +86,32 @@ static void next_token(scanner_t *s)
else
s->column++;
s->pos++;
t++;
}
s->token = *t;
s->token.token = *t;
s->token.line = s->line;
s->token.column = s->column;
s->token.pos = s->pos;
t++;
s->fmt = t;
}
static void prev_token(scanner_t *s)
{
s->next_token = s->token;
s->token = s->prev_token;
}
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_vset(s->error, s->token.line, s->token.column, s->token.pos,
fmt, ap);
jsonp_error_set_source(s->error, source);
@@ -86,47 +120,135 @@ static void set_error(scanner_t *s, const char *source, const char *fmt, ...)
static json_t *pack(scanner_t *s, va_list *ap);
/* ours will be set to 1 if jsonp_free() must be called for the result
afterwards */
static char *read_string(scanner_t *s, va_list *ap,
const char *purpose, size_t *out_len, int *ours)
{
char t;
strbuffer_t strbuff;
const char *str;
size_t length;
next_token(s);
t = token(s);
prev_token(s);
if(t != '#' && t != '%' && t != '+') {
/* Optimize the simple case */
str = va_arg(*ap, const char *);
if(!str) {
set_error(s, "<args>", "NULL string argument");
return NULL;
}
length = strlen(str);
if(!utf8_check_string(str, length)) {
set_error(s, "<args>", "Invalid UTF-8 %s", purpose);
return NULL;
}
*out_len = length;
*ours = 0;
return (char *)str;
}
strbuffer_init(&strbuff);
while(1) {
str = va_arg(*ap, const char *);
if(!str) {
set_error(s, "<args>", "NULL string argument");
strbuffer_close(&strbuff);
return NULL;
}
next_token(s);
if(token(s) == '#') {
length = va_arg(*ap, int);
}
else if(token(s) == '%') {
length = va_arg(*ap, size_t);
}
else {
prev_token(s);
length = strlen(str);
}
if(strbuffer_append_bytes(&strbuff, str, length) == -1) {
set_error(s, "<internal>", "Out of memory");
strbuffer_close(&strbuff);
return NULL;
}
next_token(s);
if(token(s) != '+') {
prev_token(s);
break;
}
}
if(!utf8_check_string(strbuff.value, strbuff.length)) {
set_error(s, "<args>", "Invalid UTF-8 %s", purpose);
strbuffer_close(&strbuff);
return NULL;
}
*out_len = strbuff.length;
*ours = 1;
return strbuffer_steal_value(&strbuff);
}
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;
while(token(s) != '}') {
char *key;
size_t len;
int ours;
json_t *value;
if(!s->token) {
if(!token(s)) {
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);
if(token(s) != 's') {
set_error(s, "<format>", "Expected format 's', got '%c'", token(s));
goto error;
}
key = va_arg(*ap, const char *);
if(!key) {
set_error(s, "<args>", "NULL object key");
key = read_string(s, ap, "object key", &len, &ours);
if(!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)
if(!value) {
if(ours)
jsonp_free(key);
goto error;
}
if(json_object_set_new_nocheck(object, key, value)) {
set_error(s, "<internal>", "Unable to add key \"%s\"", key);
if(ours)
jsonp_free(key);
goto error;
}
if(ours)
jsonp_free(key);
next_token(s);
}
@@ -142,10 +264,10 @@ static json_t *pack_array(scanner_t *s, va_list *ap)
json_t *array = json_array();
next_token(s);
while(s->token != ']') {
while(token(s) != ']') {
json_t *value;
if(!s->token) {
if(!token(s)) {
set_error(s, "<format>", "Unexpected end of format string");
goto error;
}
@@ -168,9 +290,31 @@ error:
return NULL;
}
static json_t *pack_string(scanner_t *s, va_list *ap)
{
char *str;
size_t len;
int ours;
int nullable;
next_token(s);
nullable = token(s) == '?';
if (!nullable)
prev_token(s);
str = read_string(s, ap, "string", &len, &ours);
if (!str) {
return nullable ? json_null() : NULL;
} else if (ours) {
return jsonp_stringn_nocheck_own(str, len);
} else {
return json_stringn_nocheck(str, len);
}
}
static json_t *pack(scanner_t *s, va_list *ap)
{
switch(s->token) {
switch(token(s)) {
case '{':
return pack_object(s, ap);
@@ -178,18 +322,7 @@ static json_t *pack(scanner_t *s, va_list *ap)
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);
}
return pack_string(s, ap);
case 'n': /* null */
return json_null();
@@ -207,14 +340,44 @@ static json_t *pack(scanner_t *s, va_list *ap)
return json_real(va_arg(*ap, double));
case 'O': /* a json_t object; increments refcount */
return json_incref(va_arg(*ap, json_t *));
{
int nullable;
json_t *json;
next_token(s);
nullable = token(s) == '?';
if (!nullable)
prev_token(s);
json = va_arg(*ap, json_t *);
if (!json && nullable) {
return json_null();
} else {
return json_incref(json);
}
}
case 'o': /* a json_t object; doesn't increment refcount */
return va_arg(*ap, json_t *);
{
int nullable;
json_t *json;
next_token(s);
nullable = token(s) == '?';
if (!nullable)
prev_token(s);
json = va_arg(*ap, json_t *);
if (!json && nullable) {
return json_null();
} else {
return json;
}
}
default:
set_error(s, "<format>", "Unexpected format character '%c'",
s->token);
token(s));
return NULL;
}
}
@@ -225,6 +388,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
{
int ret = -1;
int strict = 0;
int gotopt = 0;
/* Use a set (emulated by a hashtable) to check that all object
keys are accessed. Checking that the correct number of keys
@@ -233,41 +397,42 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
*/
hashtable_t key_set;
if(hashtable_init(&key_set, jsonp_hash_str, jsonp_str_equal, NULL, NULL)) {
if(hashtable_init(&key_set)) {
set_error(s, "<internal>", "Out of memory");
return -1;
}
if(!json_is_object(root)) {
if(root && !json_is_object(root)) {
set_error(s, "<validation>", "Expected object, got %s",
type_name(root));
goto out;
}
next_token(s);
while(s->token != '}') {
while(token(s) != '}') {
const char *key;
json_t *value;
int opt = 0;
if(strict != 0) {
set_error(s, "<format>", "Expected '}' after '%c', got '%c'",
(strict == 1 ? '!' : '*'), s->token);
(strict == 1 ? '!' : '*'), token(s));
goto out;
}
if(!s->token) {
if(!token(s)) {
set_error(s, "<format>", "Unexpected end of format string");
goto out;
}
if(s->token == '!' || s->token == '*') {
strict = (s->token == '!' ? 1 : -1);
if(token(s) == '!' || token(s) == '*') {
strict = (token(s) == '!' ? 1 : -1);
next_token(s);
continue;
}
if(s->token != 's') {
set_error(s, "<format>", "Expected format 's', got '%c'", s->token);
if(token(s) != 's') {
set_error(s, "<format>", "Expected format 's', got '%c'", token(s));
goto out;
}
@@ -279,26 +444,81 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
next_token(s);
value = json_object_get(root, key);
if(!value) {
set_error(s, "<validation>", "Object item not found: %s", key);
goto out;
if(token(s) == '?') {
opt = gotopt = 1;
next_token(s);
}
if(!root) {
/* skipping */
value = NULL;
}
else {
value = json_object_get(root, key);
if(!value && !opt) {
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);
hashtable_set(&key_set, key, json_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;
if(root && strict == 1) {
/* We need to check that all non optional items have been parsed */
const char *key;
int have_unrecognized_keys = 0;
strbuffer_t unrecognized_keys;
json_t *value;
long unpacked = 0;
if (gotopt) {
/* We have optional keys, we need to iter on each key */
json_object_foreach(root, key, value) {
if(!hashtable_get(&key_set, key)) {
unpacked++;
/* Save unrecognized keys for the error message */
if (!have_unrecognized_keys) {
strbuffer_init(&unrecognized_keys);
have_unrecognized_keys = 1;
} else {
strbuffer_append_bytes(&unrecognized_keys, ", ", 2);
}
strbuffer_append_bytes(&unrecognized_keys, key, strlen(key));
}
}
} else {
/* No optional keys, we can just compare the number of items */
unpacked = (long)json_object_size(root) - (long)key_set.size;
}
if (unpacked) {
if (!gotopt) {
/* Save unrecognized keys for the error message */
json_object_foreach(root, key, value) {
if(!hashtable_get(&key_set, key)) {
if (!have_unrecognized_keys) {
strbuffer_init(&unrecognized_keys);
have_unrecognized_keys = 1;
} else {
strbuffer_append_bytes(&unrecognized_keys, ", ", 2);
}
strbuffer_append_bytes(&unrecognized_keys, key, strlen(key));
}
}
}
set_error(s, "<validation>",
"%li object item(s) left unpacked: %s",
unpacked, strbuffer_value(&unrecognized_keys));
strbuffer_close(&unrecognized_keys);
goto out;
}
}
ret = 0;
@@ -313,44 +533,50 @@ 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)) {
if(root && !json_is_array(root)) {
set_error(s, "<validation>", "Expected array, got %s", type_name(root));
return -1;
}
next_token(s);
while(s->token != ']') {
while(token(s) != ']') {
json_t *value;
if(strict != 0) {
set_error(s, "<format>", "Expected ']' after '%c', got '%c'",
(strict == 1 ? '!' : '*'),
s->token);
token(s));
return -1;
}
if(!s->token) {
if(!token(s)) {
set_error(s, "<format>", "Unexpected end of format string");
return -1;
}
if(s->token == '!' || s->token == '*') {
strict = (s->token == '!' ? 1 : -1);
if(token(s) == '!' || token(s) == '*') {
strict = (token(s) == '!' ? 1 : -1);
next_token(s);
continue;
}
if(!strchr(unpack_value_starters, s->token)) {
if(!strchr(unpack_value_starters, token(s))) {
set_error(s, "<format>", "Unexpected format character '%c'",
s->token);
token(s));
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(!root) {
/* skipping */
value = NULL;
}
else {
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))
@@ -363,7 +589,7 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
if(strict == 0 && (s->flags & JSON_STRICT))
strict = 1;
if(strict == 1 && i != json_array_size(root)) {
if(root && 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;
@@ -374,7 +600,7 @@ static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
static int unpack(scanner_t *s, json_t *root, va_list *ap)
{
switch(s->token)
switch(token(s))
{
case '{':
return unpack_object(s, root, ap);
@@ -383,99 +609,134 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
return unpack_array(s, root, ap);
case 's':
if(!json_is_string(root)) {
if(root && !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;
const char **str_target;
size_t *len_target = NULL;
str = va_arg(*ap, const char **);
if(!str) {
str_target = va_arg(*ap, const char **);
if(!str_target) {
set_error(s, "<args>", "NULL string argument");
return -1;
}
*str = json_string_value(root);
next_token(s);
if(token(s) == '%') {
len_target = va_arg(*ap, size_t *);
if(!len_target) {
set_error(s, "<args>", "NULL string length argument");
return -1;
}
}
else
prev_token(s);
if(root) {
*str_target = json_string_value(root);
if(len_target)
*len_target = json_string_length(root);
}
}
return 0;
case 'i':
if(!json_is_integer(root)) {
if(root && !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);
if(!(s->flags & JSON_VALIDATE_ONLY)) {
int *target = va_arg(*ap, int*);
if(root)
*target = (int)json_integer_value(root);
}
return 0;
case 'I':
if(!json_is_integer(root)) {
if(root && !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);
if(!(s->flags & JSON_VALIDATE_ONLY)) {
json_int_t *target = va_arg(*ap, json_int_t*);
if(root)
*target = json_integer_value(root);
}
return 0;
case 'b':
if(!json_is_boolean(root)) {
if(root && !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);
if(!(s->flags & JSON_VALIDATE_ONLY)) {
int *target = va_arg(*ap, int*);
if(root)
*target = json_is_true(root);
}
return 0;
case 'f':
if(!json_is_real(root)) {
if(root && !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);
if(!(s->flags & JSON_VALIDATE_ONLY)) {
double *target = va_arg(*ap, double*);
if(root)
*target = json_real_value(root);
}
return 0;
case 'F':
if(!json_is_number(root)) {
if(root && !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);
if(!(s->flags & JSON_VALIDATE_ONLY)) {
double *target = va_arg(*ap, double*);
if(root)
*target = json_number_value(root);
}
return 0;
case 'O':
if(!(s->flags & JSON_VALIDATE_ONLY))
if(root && !(s->flags & JSON_VALIDATE_ONLY))
json_incref(root);
/* Fall through */
case 'o':
if(!(s->flags & JSON_VALIDATE_ONLY))
*va_arg(*ap, json_t**) = root;
if(!(s->flags & JSON_VALIDATE_ONLY)) {
json_t **target = va_arg(*ap, json_t**);
if(root)
*target = root;
}
return 0;
case 'n':
/* Never assign, just validate */
if(!json_is_null(root)) {
if(root && !json_is_null(root)) {
set_error(s, "<validation>", "Expected null, got %s",
type_name(root));
return -1;
@@ -484,7 +745,7 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
default:
set_error(s, "<format>", "Unexpected format character '%c'",
s->token);
token(s));
return -1;
}
}
@@ -514,7 +775,7 @@ json_t *json_vpack_ex(json_error_t *error, size_t flags,
return NULL;
next_token(&s);
if(s.token) {
if(token(&s)) {
json_decref(value);
set_error(&s, "<format>", "Garbage after format string");
return NULL;
@@ -577,7 +838,7 @@ int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags,
va_end(ap_copy);
next_token(&s);
if(s.token) {
if(token(&s)) {
set_error(&s, "<format>", "Garbage after format string");
return -1;
}

View File

@@ -1,11 +1,14 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 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 _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdlib.h>
#include <string.h>
#include "jansson_private.h"
@@ -13,6 +16,7 @@
#define STRBUFFER_MIN_SIZE 16
#define STRBUFFER_FACTOR 2
#define STRBUFFER_SIZE_MAX ((size_t)-1)
int strbuffer_init(strbuffer_t *strbuff)
{
@@ -30,7 +34,9 @@ int strbuffer_init(strbuffer_t *strbuff)
void strbuffer_close(strbuffer_t *strbuff)
{
jsonp_free(strbuff->value);
if(strbuff->value)
jsonp_free(strbuff->value);
strbuff->size = 0;
strbuff->length = 0;
strbuff->value = NULL;
@@ -50,27 +56,28 @@ const char *strbuffer_value(const strbuffer_t *strbuff)
char *strbuffer_steal_value(strbuffer_t *strbuff)
{
char *result = strbuff->value;
strbuffer_init(strbuff);
strbuff->value = NULL;
return result;
}
int strbuffer_append(strbuffer_t *strbuff, const char *string)
{
return strbuffer_append_bytes(strbuff, string, strlen(string));
}
int strbuffer_append_byte(strbuffer_t *strbuff, char byte)
{
return strbuffer_append_bytes(strbuff, &byte, 1);
}
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, int size)
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size)
{
if(strbuff->length + size >= strbuff->size)
if(size >= strbuff->size - strbuff->length)
{
size_t new_size;
char *new_value;
/* avoid integer overflow */
if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR
|| size > STRBUFFER_SIZE_MAX - 1
|| strbuff->length > STRBUFFER_SIZE_MAX - 1 - size)
return -1;
new_size = max(strbuff->size * STRBUFFER_FACTOR,
strbuff->length + size + 1);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 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,12 @@
#ifndef STRBUFFER_H
#define STRBUFFER_H
#include <stdlib.h>
typedef struct {
char *value;
int length; /* bytes used */
int size; /* bytes allocated */
size_t length; /* bytes used */
size_t size; /* bytes allocated */
} strbuffer_t;
int strbuffer_init(strbuffer_t *strbuff);
@@ -20,11 +22,12 @@ void strbuffer_close(strbuffer_t *strbuff);
void strbuffer_clear(strbuffer_t *strbuff);
const char *strbuffer_value(const strbuffer_t *strbuff);
/* Steal the value and close the strbuffer */
char *strbuffer_steal_value(strbuffer_t *strbuff);
int strbuffer_append(strbuffer_t *strbuff, const char *string);
int strbuffer_append_byte(strbuffer_t *strbuff, char byte);
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, int size);
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size);
char strbuffer_pop(strbuffer_t *strbuff);

145
src/strconv.c Normal file
View File

@@ -0,0 +1,145 @@
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#ifdef __MINGW32__
#undef __NO_ISOCEXT /* ensure stdlib.h will declare prototypes for mingw own 'strtod' replacement, called '__strtod' */
#endif
#include "jansson_private.h"
#include "strbuffer.h"
/* need jansson_private_config.h to get the correct snprintf */
#ifdef HAVE_CONFIG_H
#include <jansson_private_config.h>
#endif
#ifdef __MINGW32__
#define strtod __strtod
#endif
#if JSON_HAVE_LOCALECONV
#include <locale.h>
/*
- This code assumes that the decimal separator is exactly one
character.
- If setlocale() is called by another thread between the call to
localeconv() and the call to sprintf() or strtod(), the result may
be wrong. setlocale() is not thread-safe and should not be used
this way. Multi-threaded programs should use uselocale() instead.
*/
static void to_locale(strbuffer_t *strbuffer)
{
const char *point;
char *pos;
point = localeconv()->decimal_point;
if(*point == '.') {
/* No conversion needed */
return;
}
pos = strchr(strbuffer->value, '.');
if(pos)
*pos = *point;
}
static void from_locale(char *buffer)
{
const char *point;
char *pos;
point = localeconv()->decimal_point;
if(*point == '.') {
/* No conversion needed */
return;
}
pos = strchr(buffer, *point);
if(pos)
*pos = '.';
}
#endif
int jsonp_strtod(strbuffer_t *strbuffer, double *out)
{
double value;
char *end;
#if JSON_HAVE_LOCALECONV
to_locale(strbuffer);
#endif
errno = 0;
value = strtod(strbuffer->value, &end);
assert(end == strbuffer->value + strbuffer->length);
if((value == HUGE_VAL || value == -HUGE_VAL) && errno == ERANGE) {
/* Overflow */
return -1;
}
*out = value;
return 0;
}
int jsonp_dtostr(char *buffer, size_t size, double value, int precision)
{
int ret;
char *start, *end;
size_t length;
if (precision == 0)
precision = 17;
ret = snprintf(buffer, size, "%.*g", precision, value);
if(ret < 0)
return -1;
length = (size_t)ret;
if(length >= size)
return -1;
#if JSON_HAVE_LOCALECONV
from_locale(buffer);
#endif
/* 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(length + 3 >= size) {
/* No space to append ".0" */
return -1;
}
buffer[length] = '.';
buffer[length + 1] = '0';
buffer[length + 2] = '\0';
length += 2;
}
/* Remove leading '+' from positive exponent. Also remove leading
zeros from exponents (added by some printf() implementations) */
start = strchr(buffer, 'e');
if(start) {
start++;
end = start + 1;
if(*start == '-')
start++;
while(*end == '0')
end++;
if(end != start) {
memmove(start, end, length - (size_t)(end - buffer));
length -= (size_t)(end - start);
}
}
return (int)length;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 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,7 +8,7 @@
#include <string.h>
#include "utf.h"
int utf8_encode(int32_t codepoint, char *buffer, int *size)
int utf8_encode(int32_t codepoint, char *buffer, size_t *size)
{
if(codepoint < 0)
return -1;
@@ -44,7 +44,7 @@ int utf8_encode(int32_t codepoint, char *buffer, int *size)
return 0;
}
int utf8_check_first(char byte)
size_t utf8_check_first(char byte)
{
unsigned char u = (unsigned char)byte;
@@ -80,9 +80,9 @@ int utf8_check_first(char byte)
}
}
int utf8_check_full(const char *buffer, int size, int32_t *codepoint)
size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint)
{
int i;
size_t i;
int32_t value = 0;
unsigned char u = (unsigned char)buffer[0];
@@ -136,12 +136,12 @@ int utf8_check_full(const char *buffer, int size, int32_t *codepoint)
return 1;
}
const char *utf8_iterate(const char *buffer, int32_t *codepoint)
const char *utf8_iterate(const char *buffer, size_t bufsize, int32_t *codepoint)
{
int count;
size_t count;
int32_t value;
if(!*buffer)
if(!bufsize)
return buffer;
count = utf8_check_first(buffer[0]);
@@ -152,7 +152,7 @@ const char *utf8_iterate(const char *buffer, int32_t *codepoint)
value = (unsigned char)buffer[0];
else
{
if(!utf8_check_full(buffer, count, &value))
if(count > bufsize || !utf8_check_full(buffer, count, &value))
return NULL;
}
@@ -162,21 +162,18 @@ const char *utf8_iterate(const char *buffer, int32_t *codepoint)
return buffer + count;
}
int utf8_check_string(const char *string, int length)
int utf8_check_string(const char *string, size_t length)
{
int i;
if(length == -1)
length = strlen(string);
size_t i;
for(i = 0; i < length; i++)
{
int count = utf8_check_first(string[i]);
size_t count = utf8_check_first(string[i]);
if(count == 0)
return 0;
else if(count > 1)
{
if(i + count > length)
if(count > length - i)
return 0;
if(!utf8_check_full(&string[i], count, NULL))

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 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,31 +9,19 @@
#define UTF_H
#ifdef HAVE_CONFIG_H
#include <config.h>
#include <jansson_private_config.h>
#endif
#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 */
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#else /* !HAVE_CONFIG_H */
#ifdef _WIN32
typedef int int32_t;
#else /* !_WIN32 */
/* Assume a standard environment */
#include <inttypes.h>
#endif /* _WIN32 */
int utf8_encode(int32_t codepoint, char *buffer, size_t *size);
#endif /* HAVE_CONFIG_H */
size_t utf8_check_first(char byte);
size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint);
const char *utf8_iterate(const char *buffer, size_t size, int32_t *codepoint);
int utf8_encode(int codepoint, char *buffer, int *size);
int utf8_check_first(char byte);
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);
int utf8_check_string(const char *string, size_t length);
#endif

View File

@@ -1,21 +1,41 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 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 _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifdef HAVE_CONFIG_H
#include <jansson_private_config.h>
#endif
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <jansson.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include "jansson.h"
#include "hashtable.h"
#include "jansson_private.h"
#include "utf.h"
/* Work around nonstandard isnan() and isinf() implementations */
#ifndef isnan
#ifndef __sun
static JSON_INLINE int isnan(double x) { return x != x; }
#endif
#endif
#ifndef isinf
static JSON_INLINE int isinf(double x) { return !isnan(x) && isnan(x - x); }
#endif
static JSON_INLINE void json_init(json_t *json, json_type type)
{
@@ -26,66 +46,27 @@ static JSON_INLINE void json_init(json_t *json, json_type type)
/*** object ***/
/* From http://www.cse.yorku.ca/~oz/hash.html */
size_t jsonp_hash_str(const void *ptr)
{
const char *str = (const char *)ptr;
size_t hash = 5381;
size_t c;
while((c = (size_t)*str))
{
hash = ((hash << 5) + hash) + c;
str++;
}
return hash;
}
int jsonp_str_equal(const void *ptr1, const void *ptr2)
{
return strcmp((const char *)ptr1, (const char *)ptr2) == 0;
}
/* This macro just returns a pointer that's a few bytes backwards from
string. This makes it possible to pass a pointer to object_key_t
when only the string inside it is used, without actually creating
an object_key_t instance. */
#define string_to_key(string) container_of(string, object_key_t, key)
static size_t hash_key(const void *ptr)
{
return jsonp_hash_str(((const object_key_t *)ptr)->key);
}
static int key_equal(const void *ptr1, const void *ptr2)
{
return jsonp_str_equal(((const object_key_t *)ptr1)->key,
((const object_key_t *)ptr2)->key);
}
static void value_decref(void *value)
{
json_decref((json_t *)value);
}
extern volatile uint32_t hashtable_seed;
json_t *json_object(void)
{
json_object_t *object = jsonp_malloc(sizeof(json_object_t));
if(!object)
return NULL;
if (!hashtable_seed) {
/* Autoseed */
json_object_seed(0);
}
json_init(&object->json, JSON_OBJECT);
if(hashtable_init(&object->hashtable,
hash_key, key_equal,
jsonp_free, value_decref))
if(hashtable_init(&object->hashtable))
{
jsonp_free(object);
return NULL;
}
object->serial = 0;
object->visited = 0;
return &object->json;
@@ -112,42 +93,28 @@ json_t *json_object_get(const json_t *json, const char *key)
{
json_object_t *object;
if(!json_is_object(json))
if(!key || !json_is_object(json))
return NULL;
object = json_to_object(json);
return hashtable_get(&object->hashtable, string_to_key(key));
return hashtable_get(&object->hashtable, key);
}
int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
{
json_object_t *object;
object_key_t *k;
if(!key || !value)
if(!value)
return -1;
if(!json_is_object(json) || json == value)
if(!key || !json_is_object(json) || json == value)
{
json_decref(value);
return -1;
}
object = json_to_object(json);
/* offsetof(...) returns the size of object_key_t without the
last, flexible member. This way, the correct amount is
allocated. */
k = jsonp_malloc(offsetof(object_key_t, key) + strlen(key) + 1);
if(!k)
{
json_decref(value);
return -1;
}
k->serial = object->serial++;
strcpy(k->key, key);
if(hashtable_set(&object->hashtable, k, value))
if(hashtable_set(&object->hashtable, key, value))
{
json_decref(value);
return -1;
@@ -158,7 +125,7 @@ int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
int json_object_set_new(json_t *json, const char *key, json_t *value)
{
if(!key || !utf8_check_string(key, -1))
if(!key || !utf8_check_string(key, strlen(key)))
{
json_decref(value);
return -1;
@@ -171,11 +138,11 @@ int json_object_del(json_t *json, const char *key)
{
json_object_t *object;
if(!json_is_object(json))
if(!key || !json_is_object(json))
return -1;
object = json_to_object(json);
return hashtable_del(&object->hashtable, string_to_key(key));
return hashtable_del(&object->hashtable, key);
}
int json_object_clear(json_t *json)
@@ -193,23 +160,47 @@ int json_object_clear(json_t *json)
int json_object_update(json_t *object, json_t *other)
{
void *iter;
const char *key;
json_t *value;
if(!json_is_object(object) || !json_is_object(other))
return -1;
iter = json_object_iter(other);
while(iter) {
const char *key;
json_t *value;
key = json_object_iter_key(iter);
value = json_object_iter_value(iter);
json_object_foreach(other, key, value) {
if(json_object_set_nocheck(object, key, value))
return -1;
}
iter = json_object_iter_next(other, iter);
return 0;
}
int json_object_update_existing(json_t *object, json_t *other)
{
const char *key;
json_t *value;
if(!json_is_object(object) || !json_is_object(other))
return -1;
json_object_foreach(other, key, value) {
if(json_object_get(object, key))
json_object_set_nocheck(object, key, value);
}
return 0;
}
int json_object_update_missing(json_t *object, json_t *other)
{
const char *key;
json_t *value;
if(!json_is_object(object) || !json_is_object(other))
return -1;
json_object_foreach(other, key, value) {
if(!json_object_get(object, key))
json_object_set_nocheck(object, key, value);
}
return 0;
@@ -234,7 +225,7 @@ void *json_object_iter_at(json_t *json, const char *key)
return NULL;
object = json_to_object(json);
return hashtable_iter_at(&object->hashtable, string_to_key(key));
return hashtable_iter_at(&object->hashtable, key);
}
void *json_object_iter_next(json_t *json, void *iter)
@@ -248,20 +239,12 @@ void *json_object_iter_next(json_t *json, void *iter)
return hashtable_iter_next(&object->hashtable, iter);
}
const object_key_t *jsonp_object_iter_fullkey(void *iter)
{
if(!iter)
return NULL;
return hashtable_iter_key(iter);
}
const char *json_object_iter_key(void *iter)
{
if(!iter)
return NULL;
return jsonp_object_iter_fullkey(iter)->key;
return hashtable_iter_key(iter);
}
json_t *json_object_iter_value(void *iter)
@@ -274,38 +257,34 @@ json_t *json_object_iter_value(void *iter)
int json_object_iter_set_new(json_t *json, void *iter, json_t *value)
{
json_object_t *object;
if(!json_is_object(json) || !iter || !value)
return -1;
object = json_to_object(json);
hashtable_iter_set(&object->hashtable, iter, value);
hashtable_iter_set(iter, value);
return 0;
}
void *json_object_key_to_iter(const char *key)
{
if(!key)
return NULL;
return hashtable_key_to_iter(key);
}
static int json_object_equal(json_t *object1, json_t *object2)
{
void *iter;
const char *key;
json_t *value1, *value2;
if(json_object_size(object1) != json_object_size(object2))
return 0;
iter = json_object_iter(object1);
while(iter)
{
const char *key;
json_t *value1, *value2;
key = json_object_iter_key(iter);
value1 = json_object_iter_value(iter);
json_object_foreach(object1, key, value1) {
value2 = json_object_get(object2, key);
if(!json_equal(value1, value2))
return 0;
iter = json_object_iter_next(object1, iter);
}
return 1;
@@ -314,29 +293,21 @@ static int json_object_equal(json_t *object1, json_t *object2)
static json_t *json_object_copy(json_t *object)
{
json_t *result;
void *iter;
const char *key;
json_t *value;
result = json_object();
if(!result)
return NULL;
iter = json_object_iter(object);
while(iter)
{
const char *key;
json_t *value;
key = json_object_iter_key(iter);
value = json_object_iter_value(iter);
json_object_foreach(object, key, value)
json_object_set_nocheck(result, key, value);
iter = json_object_iter_next(object, iter);
}
return result;
}
static json_t *json_object_deep_copy(json_t *object)
static json_t *json_object_deep_copy(const json_t *object)
{
json_t *result;
void *iter;
@@ -345,17 +316,17 @@ static json_t *json_object_deep_copy(json_t *object)
if(!result)
return NULL;
iter = json_object_iter(object);
while(iter)
{
/* Cannot use json_object_foreach because object has to be cast
non-const */
iter = json_object_iter((json_t *)object);
while(iter) {
const char *key;
json_t *value;
const json_t *value;
key = json_object_iter_key(iter);
value = json_object_iter_value(iter);
json_object_set_new_nocheck(result, key, json_deep_copy(value));
iter = json_object_iter_next(object, iter);
json_object_set_new_nocheck(result, key, json_deep_copy(value));
iter = json_object_iter_next((json_t *)object, iter);
}
return result;
@@ -563,7 +534,10 @@ int json_array_remove(json_t *json, size_t index)
json_decref(array->table[index]);
array_move(array, index, index + 1, array->entries - index);
/* If we're removing the last element, nothing has to be moved */
if(index < array->entries - 1)
array_move(array, index, index + 1, array->entries - index - 1);
array->entries--;
return 0;
@@ -644,7 +618,7 @@ static json_t *json_array_copy(json_t *array)
return result;
}
static json_t *json_array_deep_copy(json_t *array)
static json_t *json_array_deep_copy(const json_t *array)
{
json_t *result;
size_t i;
@@ -661,33 +635,68 @@ static json_t *json_array_deep_copy(json_t *array)
/*** string ***/
json_t *json_string_nocheck(const char *value)
static json_t *string_create(const char *value, size_t len, int own)
{
char *v;
json_string_t *string;
if(!value)
return NULL;
string = jsonp_malloc(sizeof(json_string_t));
if(!string)
return NULL;
json_init(&string->json, JSON_STRING);
if(own)
v = (char *)value;
else {
v = jsonp_strndup(value, len);
if(!v)
return NULL;
}
string->value = jsonp_strdup(value);
if(!string->value) {
jsonp_free(string);
string = jsonp_malloc(sizeof(json_string_t));
if(!string) {
if(!own)
jsonp_free(v);
return NULL;
}
json_init(&string->json, JSON_STRING);
string->value = v;
string->length = len;
return &string->json;
}
json_t *json_string(const char *value)
json_t *json_string_nocheck(const char *value)
{
if(!value || !utf8_check_string(value, -1))
if(!value)
return NULL;
return json_string_nocheck(value);
return string_create(value, strlen(value), 0);
}
json_t *json_stringn_nocheck(const char *value, size_t len)
{
return string_create(value, len, 0);
}
/* this is private; "steal" is not a public API concept */
json_t *jsonp_stringn_nocheck_own(const char *value, size_t len)
{
return string_create(value, len, 1);
}
json_t *json_string(const char *value)
{
if(!value)
return NULL;
return json_stringn(value, strlen(value));
}
json_t *json_stringn(const char *value, size_t len)
{
if(!value || !utf8_check_string(value, len))
return NULL;
return json_stringn_nocheck(value, len);
}
const char *json_string_value(const json_t *json)
@@ -698,28 +707,56 @@ const char *json_string_value(const json_t *json)
return json_to_string(json)->value;
}
size_t json_string_length(const json_t *json)
{
if(!json_is_string(json))
return 0;
return json_to_string(json)->length;
}
int json_string_set_nocheck(json_t *json, const char *value)
{
if(!value)
return -1;
return json_string_setn_nocheck(json, value, strlen(value));
}
int json_string_setn_nocheck(json_t *json, const char *value, size_t len)
{
char *dup;
json_string_t *string;
dup = jsonp_strdup(value);
if(!json_is_string(json) || !value)
return -1;
dup = jsonp_strndup(value, len);
if(!dup)
return -1;
string = json_to_string(json);
jsonp_free(string->value);
string->value = dup;
string->length = len;
return 0;
}
int json_string_set(json_t *json, const char *value)
{
if(!value || !utf8_check_string(value, -1))
if(!value)
return -1;
return json_string_set_nocheck(json, value);
return json_string_setn(json, value, strlen(value));
}
int json_string_setn(json_t *json, const char *value, size_t len)
{
if(!value || !utf8_check_string(value, len))
return -1;
return json_string_setn_nocheck(json, value, len);
}
static void json_delete_string(json_string_t *string)
@@ -730,12 +767,25 @@ static void json_delete_string(json_string_t *string)
static int json_string_equal(json_t *string1, json_t *string2)
{
return strcmp(json_string_value(string1), json_string_value(string2)) == 0;
json_string_t *s1, *s2;
if(!json_is_string(string1) || !json_is_string(string2))
return 0;
s1 = json_to_string(string1);
s2 = json_to_string(string2);
return s1->length == s2->length && !memcmp(s1->value, s2->value, s1->length);
}
static json_t *json_string_copy(json_t *string)
static json_t *json_string_copy(const json_t *string)
{
return json_string_nocheck(json_string_value(string));
json_string_t *s;
if(!json_is_string(string))
return NULL;
s = json_to_string(string);
return json_stringn_nocheck(s->value, s->length);
}
@@ -780,7 +830,7 @@ static int json_integer_equal(json_t *integer1, json_t *integer2)
return json_integer_value(integer1) == json_integer_value(integer2);
}
static json_t *json_integer_copy(json_t *integer)
static json_t *json_integer_copy(const json_t *integer)
{
return json_integer(json_integer_value(integer));
}
@@ -790,7 +840,12 @@ static json_t *json_integer_copy(json_t *integer)
json_t *json_real(double value)
{
json_real_t *real = jsonp_malloc(sizeof(json_real_t));
json_real_t *real;
if(isnan(value) || isinf(value))
return NULL;
real = jsonp_malloc(sizeof(json_real_t));
if(!real)
return NULL;
json_init(&real->json, JSON_REAL);
@@ -809,8 +864,8 @@ double json_real_value(const json_t *json)
int json_real_set(json_t *json, double value)
{
if(!json_is_real(json))
return 0;
if(!json_is_real(json) || isnan(value) || isinf(value))
return -1;
json_to_real(json)->value = value;
@@ -827,7 +882,7 @@ static int json_real_equal(json_t *real1, json_t *real2)
return json_real_value(real1) == json_real_value(real2);
}
static json_t *json_real_copy(json_t *real)
static json_t *json_real_copy(const json_t *real)
{
return json_real(json_real_value(real));
}
@@ -838,7 +893,7 @@ static json_t *json_real_copy(json_t *real)
double json_number_value(const json_t *json)
{
if(json_is_integer(json))
return json_integer_value(json);
return (double)json_integer_value(json);
else if(json_is_real(json))
return json_real_value(json);
else
@@ -873,20 +928,28 @@ json_t *json_null(void)
void json_delete(json_t *json)
{
if(json_is_object(json))
json_delete_object(json_to_object(json));
if (!json)
return;
else if(json_is_array(json))
json_delete_array(json_to_array(json));
else if(json_is_string(json))
json_delete_string(json_to_string(json));
else if(json_is_integer(json))
json_delete_integer(json_to_integer(json));
else if(json_is_real(json))
json_delete_real(json_to_real(json));
switch(json_typeof(json)) {
case JSON_OBJECT:
json_delete_object(json_to_object(json));
break;
case JSON_ARRAY:
json_delete_array(json_to_array(json));
break;
case JSON_STRING:
json_delete_string(json_to_string(json));
break;
case JSON_INTEGER:
json_delete_integer(json_to_integer(json));
break;
case JSON_REAL:
json_delete_real(json_to_real(json));
break;
default:
return;
}
/* json_delete is not called for true, false or null */
}
@@ -906,22 +969,20 @@ int json_equal(json_t *json1, json_t *json2)
if(json1 == json2)
return 1;
if(json_is_object(json1))
return json_object_equal(json1, json2);
if(json_is_array(json1))
return json_array_equal(json1, json2);
if(json_is_string(json1))
return json_string_equal(json1, json2);
if(json_is_integer(json1))
return json_integer_equal(json1, json2);
if(json_is_real(json1))
return json_real_equal(json1, json2);
return 0;
switch(json_typeof(json1)) {
case JSON_OBJECT:
return json_object_equal(json1, json2);
case JSON_ARRAY:
return json_array_equal(json1, json2);
case JSON_STRING:
return json_string_equal(json1, json2);
case JSON_INTEGER:
return json_integer_equal(json1, json2);
case JSON_REAL:
return json_real_equal(json1, json2);
default:
return 0;
}
}
@@ -932,52 +993,53 @@ json_t *json_copy(json_t *json)
if(!json)
return NULL;
if(json_is_object(json))
return json_object_copy(json);
if(json_is_array(json))
return json_array_copy(json);
if(json_is_string(json))
return json_string_copy(json);
if(json_is_integer(json))
return json_integer_copy(json);
if(json_is_real(json))
return json_real_copy(json);
if(json_is_true(json) || json_is_false(json) || json_is_null(json))
return json;
switch(json_typeof(json)) {
case JSON_OBJECT:
return json_object_copy(json);
case JSON_ARRAY:
return json_array_copy(json);
case JSON_STRING:
return json_string_copy(json);
case JSON_INTEGER:
return json_integer_copy(json);
case JSON_REAL:
return json_real_copy(json);
case JSON_TRUE:
case JSON_FALSE:
case JSON_NULL:
return json;
default:
return NULL;
}
return NULL;
}
json_t *json_deep_copy(json_t *json)
json_t *json_deep_copy(const json_t *json)
{
if(!json)
return NULL;
if(json_is_object(json))
return json_object_deep_copy(json);
if(json_is_array(json))
return json_array_deep_copy(json);
/* for the rest of the types, deep copying doesn't differ from
shallow copying */
if(json_is_string(json))
return json_string_copy(json);
if(json_is_integer(json))
return json_integer_copy(json);
if(json_is_real(json))
return json_real_copy(json);
if(json_is_true(json) || json_is_false(json) || json_is_null(json))
return json;
switch(json_typeof(json)) {
case JSON_OBJECT:
return json_object_deep_copy(json);
case JSON_ARRAY:
return json_array_deep_copy(json);
/* for the rest of the types, deep copying doesn't differ from
shallow copying */
case JSON_STRING:
return json_string_copy(json);
case JSON_INTEGER:
return json_integer_copy(json);
case JSON_REAL:
return json_real_copy(json);
case JSON_TRUE:
case JSON_FALSE:
case JSON_NULL:
return (json_t *)json;
default:
return NULL;
}
return NULL;
}

2
test/.gitignore vendored
View File

@@ -4,6 +4,7 @@ suites/api/test_array
suites/api/test_copy
suites/api/test_cpp
suites/api/test_dump
suites/api/test_dump_callback
suites/api/test_equal
suites/api/test_load
suites/api/test_loadb
@@ -13,3 +14,4 @@ suites/api/test_object
suites/api/test_pack
suites/api/test_simple
suites/api/test_unpack
suites/api/test_load_callback

View File

@@ -1,6 +1,5 @@
check_PROGRAMS = json_process
AM_CPPFLAGS = -I$(top_srcdir)/src
AM_CFLAGS = -Wall -Werror
AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_srcdir)/src
LDFLAGS = -static # for speed and Valgrind
LDADD = $(top_builddir)/src/libjansson.la

View File

@@ -1,16 +1,244 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 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.
*/
#ifdef HAVE_CONFIG_H
#include <jansson_private_config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <jansson.h>
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
#if _WIN32
#include <io.h> /* for _setmode() */
#include <fcntl.h> /* for _O_BINARY */
static const char dir_sep = '\\';
#else
static const char dir_sep = '/';
#endif
struct config {
int indent;
int compact;
int preserve_order;
int ensure_ascii;
int sort_keys;
int strip;
int use_env;
int have_hashseed;
int hashseed;
int precision;
} conf;
#define l_isspace(c) ((c) == ' ' || (c) == '\n' || (c) == '\r' || (c) == '\t')
/* 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 && l_isspace(*result))
result++;
length = strlen(result);
if (length == 0)
return result;
while (l_isspace(result[length - 1]))
result[--length] = '\0';
return result;
}
static char *loadfile(FILE *file)
{
long fsize, ret;
char *buf;
fseek(file, 0, SEEK_END);
fsize = ftell(file);
fseek(file, 0, SEEK_SET);
buf = malloc(fsize+1);
ret = fread(buf, 1, fsize, file);
if (ret != fsize)
exit(1);
buf[fsize] = '\0';
return buf;
}
static void read_conf(FILE *conffile)
{
char *buffer, *line, *val;
buffer = loadfile(conffile);
for (line = strtok(buffer, "\r\n"); line; line = strtok(NULL, "\r\n")) {
if (!strncmp(line, "export ", 7))
continue;
val = strchr(line, '=');
if (!val) {
printf("invalid configuration line\n");
break;
}
*val++ = '\0';
if (!strcmp(line, "JSON_INDENT"))
conf.indent = atoi(val);
if (!strcmp(line, "JSON_COMPACT"))
conf.compact = atoi(val);
if (!strcmp(line, "JSON_ENSURE_ASCII"))
conf.ensure_ascii = atoi(val);
if (!strcmp(line, "JSON_PRESERVE_ORDER"))
conf.preserve_order = atoi(val);
if (!strcmp(line, "JSON_SORT_KEYS"))
conf.sort_keys = atoi(val);
if (!strcmp(line, "JSON_REAL_PRECISION"))
conf.precision = atoi(val);
if (!strcmp(line, "STRIP"))
conf.strip = atoi(val);
if (!strcmp(line, "HASHSEED")) {
conf.have_hashseed = 1;
conf.hashseed = atoi(val);
} else {
conf.have_hashseed = 0;
}
}
free(buffer);
}
static int cmpfile(const char *str, const char *path, const char *fname)
{
char filename[1024], *buffer;
int ret;
FILE *file;
sprintf(filename, "%s%c%s", path, dir_sep, fname);
file = fopen(filename, "rb");
if (!file) {
if (conf.strip)
strcat(filename, ".strip");
else
strcat(filename, ".normal");
file = fopen(filename, "rb");
}
if (!file) {
printf("Error: test result file could not be opened.\n");
exit(1);
}
buffer = loadfile(file);
if (strcmp(buffer, str) != 0)
ret = 1;
else
ret = 0;
free(buffer);
fclose(file);
return ret;
}
int use_conf(char *test_path)
{
int ret;
size_t flags = 0;
char filename[1024], errstr[1024];
char *buffer;
FILE *infile, *conffile;
json_t *json;
json_error_t error;
sprintf(filename, "%s%cinput", test_path, dir_sep);
if (!(infile = fopen(filename, "rb"))) {
fprintf(stderr, "Could not open \"%s\"\n", filename);
return 2;
}
sprintf(filename, "%s%cenv", test_path, dir_sep);
conffile = fopen(filename, "rb");
if (conffile) {
read_conf(conffile);
fclose(conffile);
}
if (conf.indent < 0 || conf.indent > 31) {
fprintf(stderr, "invalid value for JSON_INDENT: %d\n", conf.indent);
fclose(infile);
return 2;
}
if (conf.indent)
flags |= JSON_INDENT(conf.indent);
if (conf.compact)
flags |= JSON_COMPACT;
if (conf.ensure_ascii)
flags |= JSON_ENSURE_ASCII;
if (conf.preserve_order)
flags |= JSON_PRESERVE_ORDER;
if (conf.sort_keys)
flags |= JSON_SORT_KEYS;
if (conf.precision < 0 || conf.precision > 31) {
fprintf(stderr, "invalid value for JSON_REAL_PRECISION: %d\n",
conf.precision);
fclose(infile);
return 2;
}
if (conf.precision)
flags |= JSON_REAL_PRECISION(conf.precision);
if (conf.have_hashseed)
json_object_seed(conf.hashseed);
if (conf.strip) {
/* Load to memory, strip leading and trailing whitespace */
buffer = loadfile(infile);
json = json_loads(strip(buffer), 0, &error);
free(buffer);
}
else
json = json_loadf(infile, 0, &error);
fclose(infile);
if (!json) {
sprintf(errstr, "%d %d %d\n%s\n",
error.line, error.column, error.position,
error.text);
ret = cmpfile(errstr, test_path, "error");
return ret;
}
buffer = json_dumps(json, flags);
ret = cmpfile(buffer, test_path, "output");
free(buffer);
json_decref(json);
return ret;
}
static int getenv_int(const char *name)
{
char *value, *end;
@@ -27,45 +255,25 @@ static int getenv_int(const char *name)
return (int)result;
}
/* Return a pointer to the first non-whitespace character of str.
Modifies str so that all trailing whitespace characters are
replaced by '\0'. */
static const char *strip(char *str)
int use_env()
{
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;
int indent, precision;
size_t flags = 0;
json_t *json;
json_error_t error;
if(argc != 1) {
fprintf(stderr, "usage: %s\n", argv[0]);
return 2;
}
#ifdef _WIN32
/* On Windows, set stdout and stderr to binary mode to avoid
outputting DOS line terminators */
_setmode(_fileno(stdout), _O_BINARY);
_setmode(_fileno(stderr), _O_BINARY);
#endif
indent = getenv_int("JSON_INDENT");
if(indent < 0 || indent > 255) {
if(indent < 0 || indent > 31) {
fprintf(stderr, "invalid value for JSON_INDENT: %d\n", indent);
return 2;
}
if(indent > 0)
flags |= JSON_INDENT(indent);
@@ -81,20 +289,35 @@ int main(int argc, char *argv[])
if(getenv_int("JSON_SORT_KEYS"))
flags |= JSON_SORT_KEYS;
precision = getenv_int("JSON_REAL_PRECISION");
if(precision < 0 || precision > 31) {
fprintf(stderr, "invalid value for JSON_REAL_PRECISION: %d\n",
precision);
return 2;
}
if(getenv("HASHSEED"))
json_object_seed(getenv_int("HASHSEED"));
if(precision > 0)
flags |= JSON_REAL_PRECISION(precision);
if(getenv_int("STRIP")) {
/* Load to memory, strip leading and trailing whitespace */
size_t size = 0, used = 0;
char *buffer = NULL;
char *buffer = NULL, *buf_ck = NULL;
while(1) {
int count;
size_t count;
size = (size == 0 ? 128 : size * 2);
buffer = realloc(buffer, size);
if(!buffer) {
buf_ck = realloc(buffer, size);
if(!buf_ck) {
fprintf(stderr, "Unable to allocate %d bytes\n", (int)size);
free(buffer);
return 1;
}
buffer = buf_ck;
count = fread(buffer + used, 1, size - used, stdin);
if(count < size - used) {
@@ -112,8 +335,8 @@ int main(int argc, char *argv[])
if(!json) {
fprintf(stderr, "%d %d %d\n%s\n",
error.line, error.column, error.position,
error.text);
error.line, error.column,
error.position, error.text);
return 1;
}
@@ -122,3 +345,41 @@ int main(int argc, char *argv[])
return 0;
}
int main(int argc, char *argv[])
{
int i;
char *test_path = NULL;
#ifdef HAVE_SETLOCALE
setlocale(LC_ALL, "");
#endif
if (argc < 2) {
goto usage;
}
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--strip"))
conf.strip = 1;
else if (!strcmp(argv[i], "--env"))
conf.use_env = 1;
else
test_path = argv[i];
}
if (conf.use_env)
return use_env();
else
{
if (!test_path)
goto usage;
return use_conf(test_path);
}
usage:
fprintf(stderr, "argc =%d\n", argc);
fprintf(stderr, "usage: %s [--strip] [--env] test_dir\n", argv[0]);
return 2;
}

View File

@@ -22,26 +22,27 @@ 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
suites_srcdir=$top_srcdir/test/suites
suites_builddir=suites
scriptdir=$top_srcdir/test/scripts
logdir=logs
bindir=bin
export suites_srcdir suites_builddir scriptdir logdir bindir
passed=0
failed=0
for suite in $SUITES; do
echo "Suite: $suite"
if $suites_srcdir/$suite/run $suite; then
passed=$(($passed+1))
passed=`expr $passed + 1`
else
failed=$(($failed+1))
failed=`expr $failed + 1`
[ $STOP -eq 1 ] && break
fi
done
if [ $failed -gt 0 ]; then
echo "$failed of $((passed+failed)) test suites failed"
echo "$failed of `expr $passed + $failed` test suites failed"
exit 1
else
echo "$passed test suites passed"

View File

@@ -1,8 +1,20 @@
# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
# Copyright (c) 2009-2014 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.
die() {
echo "$1" >&2
exit 1
}
[ -n "$1" ] || die "Usage: $0 suite-name"
[ -n "$bindir" ] || die "Set bindir"
[ -n "$logdir" ] || die "Set logdir"
[ -n "$scriptdir" ] || die "Set scriptdir"
[ -n "$suites_srcdir" ] || die "Set suites_srcdir"
[ -n "$suites_builddir" ] || die "Set suites_builddir"
json_process=$bindir/json_process
suite_name=$1
@@ -10,7 +22,6 @@ 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

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
# Copyright (c) 2009-2014 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.

2
test/suites/.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
api/ text=auto
* text eol=lf

View File

@@ -1,12 +1,14 @@
EXTRA_DIST = run
EXTRA_DIST = run check-exports
check_PROGRAMS = \
test_array \
test_copy \
test_dump \
test_dump_callback \
test_equal \
test_load \
test_loadb \
test_load_callback \
test_memory_funcs \
test_number \
test_object \
@@ -17,6 +19,7 @@ check_PROGRAMS = \
test_array_SOURCES = test_array.c util.h
test_copy_SOURCES = test_copy.c util.h
test_dump_SOURCES = test_dump.c util.h
test_dump_callback_SOURCES = test_dump_callback.c util.h
test_load_SOURCES = test_load.c util.h
test_loadb_SOURCES = test_loadb.c util.h
test_memory_funcs_SOURCES = test_memory_funcs.c util.h
@@ -26,7 +29,6 @@ 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
AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_srcdir)/src
LDFLAGS = -static # for speed and Valgrind
LDADD = $(top_builddir)/src/libjansson.la

View File

@@ -1,99 +1,21 @@
#!/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
#
# This test checks that libjansson.so exports the correct symbols.
#
SOFILE="../src/.libs/libjansson.so"
# The list of symbols, which the shared object should export, is read
# from the def file, which is used in Windows builds
grep 'json_' $top_srcdir/src/jansson.def \
| sed -e 's/ //g' \
| sort \
>$test_log/exports
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
grep ' [DT] ' $test_log/symbols | cut -d' ' -f3 | grep -v '^_' | sort >$test_log/output
if ! cmp -s $test_log/exports $test_log/output; then
diff -u $test_log/exports $test_log/output >&2

View File

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

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 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.
@@ -11,7 +11,7 @@
static void test_misc(void)
{
json_t *array, *five, *seven, *value;
int i;
size_t i;
array = json_array();
five = json_integer(5);
@@ -206,6 +206,7 @@ static void test_insert(void)
static void test_remove(void)
{
json_t *array, *five, *seven;
int i;
array = json_array();
five = json_integer(5);
@@ -253,6 +254,19 @@ static void test_remove(void)
json_array_get(array, 2) != seven)
fail("remove works incorrectly");
json_decref(array);
array = json_array();
for(i = 0; i < 4; i++) {
json_array_append(array, five);
json_array_append(array, seven);
}
if(json_array_size(array) != 8)
fail("unable to append 8 items to array");
/* Remove an element from a "full" array. */
json_array_remove(array, 5);
json_decref(five);
json_decref(seven);
json_decref(array);
@@ -386,8 +400,27 @@ static void test_circular()
json_decref(array1);
}
static void test_array_foreach()
{
size_t index;
json_t *array1, *array2, *value;
int main()
array1 = json_pack("[sisisi]", "foo", 1, "bar", 2, "baz", 3);
array2 = json_array();
json_array_foreach(array1, index, value) {
json_array_append(array2, value);
}
if(!json_equal(array1, array2))
fail("json_array_foreach failed to iterate all elements");
json_decref(array1);
json_decref(array2);
}
static void run_tests()
{
test_misc();
test_insert();
@@ -395,6 +428,5 @@ int main()
test_clear();
test_extend();
test_circular();
return 0;
test_array_foreach();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -232,6 +232,9 @@ static void test_copy_object(void)
const char *json_object_text =
"{\"foo\": \"bar\", \"a\": 1, \"b\": 3.141592, \"c\": [1,2,3,4]}";
const char *keys[] = {"foo", "a", "b", "c"};
int i;
json_t *object, *copy;
void *iter;
@@ -247,6 +250,7 @@ static void test_copy_object(void)
if(!json_equal(copy, object))
fail("copying an object produces an inequal copy");
i = 0;
iter = json_object_iter(object);
while(iter)
{
@@ -258,9 +262,13 @@ static void test_copy_object(void)
value2 = json_object_get(copy, key);
if(value1 != value2)
fail("deep copying an object modifies its items");
fail("copying an object modifies its items");
if (strcmp(key, keys[i]) != 0)
fail("copying an object doesn't preserve key order");
iter = json_object_iter_next(object, iter);
i++;
}
json_decref(object);
@@ -272,6 +280,9 @@ static void test_deep_copy_object(void)
const char *json_object_text =
"{\"foo\": \"bar\", \"a\": 1, \"b\": 3.141592, \"c\": [1,2,3,4]}";
const char *keys[] = {"foo", "a", "b", "c"};
int i;
json_t *object, *copy;
void *iter;
@@ -287,6 +298,7 @@ static void test_deep_copy_object(void)
if(!json_equal(copy, object))
fail("deep copying an object produces an inequal copy");
i = 0;
iter = json_object_iter(object);
while(iter)
{
@@ -300,14 +312,18 @@ static void test_deep_copy_object(void)
if(value1 == value2)
fail("deep copying an object doesn't copy its items");
if (strcmp(key, keys[i]) != 0)
fail("deep copying an object doesn't preserve key order");
iter = json_object_iter_next(object, iter);
i++;
}
json_decref(object);
json_decref(copy);
}
int main()
static void run_tests()
{
test_copy_simple();
test_deep_copy_simple();
@@ -315,5 +331,4 @@ int main()
test_deep_copy_array();
test_copy_object();
test_deep_copy_object();
return 0;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 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,6 +9,29 @@
#include <string.h>
#include "util.h"
static int encode_null_callback(const char *buffer, size_t size, void *data)
{
(void)buffer;
(void)size;
(void)data;
return 0;
}
static void encode_null()
{
if(json_dumps(NULL, JSON_ENCODE_ANY) != NULL)
fail("json_dumps didn't fail for NULL");
if(json_dumpf(NULL, stderr, JSON_ENCODE_ANY) != -1)
fail("json_dumpf didn't fail for NULL");
/* Don't test json_dump_file to avoid creating a file */
if(json_dump_callback(NULL, encode_null_callback, NULL, JSON_ENCODE_ANY) != -1)
fail("json_dump_callback didn't fail for NULL");
}
static void encode_twice()
{
/* Encode an empty object/array, add an item, encode again */
@@ -133,10 +156,69 @@ static void encode_other_than_array_or_object()
}
int main()
static void escape_slashes()
{
/* Test dump escaping slashes */
json_t *json;
char *result;
json = json_object();
json_object_set_new(json, "url", json_string("https://github.com/akheron/jansson"));
result = json_dumps(json, 0);
if(!result || strcmp(result, "{\"url\": \"https://github.com/akheron/jansson\"}"))
fail("json_dumps failed to not escape slashes");
free(result);
result = json_dumps(json, JSON_ESCAPE_SLASH);
if(!result || strcmp(result, "{\"url\": \"https:\\/\\/github.com\\/akheron\\/jansson\"}"))
fail("json_dumps failed to escape slashes");
free(result);
json_decref(json);
}
static void encode_nul_byte()
{
json_t *json;
char *result;
json = json_stringn("nul byte \0 in string", 20);
result = json_dumps(json, JSON_ENCODE_ANY);
if(!result || memcmp(result, "\"nul byte \\u0000 in string\"", 27))
fail("json_dumps failed to dump an embedded NUL byte");
free(result);
json_decref(json);
}
static void dump_file()
{
json_t *json;
int result;
result = json_dump_file(NULL, "", 0);
if (result != -1)
fail("json_dump_file succeeded with invalid args");
json = json_object();
result = json_dump_file(json, "json_dump_file.json", 0);
if (result != 0)
fail("json_dump_file failed");
json_decref(json);
remove("json_dump_file.json");
}
static void run_tests()
{
encode_null();
encode_twice();
circular_references();
encode_other_than_array_or_object();
return 0;
escape_slashes();
encode_nul_byte();
dump_file();
}

View File

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

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 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.
@@ -180,11 +180,10 @@ static void test_equal_complex()
/* TODO: There's no negative test case here */
}
int main()
static void run_tests()
{
test_equal_simple();
test_equal_array();
test_equal_object();
test_equal_complex();
return 0;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 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.
@@ -13,16 +13,41 @@ static void file_not_found()
{
json_t *json;
json_error_t error;
char *pos;
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)
/* The error message is locale specific, only check the beginning
of the error message. */
pos = strchr(error.text, ':');
if(!pos)
fail("json_load_file returne an invalid error message");
*pos = '\0';
if(strcmp(error.text, "unable to open /path/to/nonexistent/file.json") != 0)
fail("json_load_file returned an invalid error message");
}
static void very_long_file_name() {
json_t *json;
json_error_t error;
json = json_load_file("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 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 (strncmp(error.source, "...aaa", 6) != 0)
fail("error source was set incorrectly");
}
static void reject_duplicates()
{
json_error_t error;
@@ -50,11 +75,142 @@ static void disable_eof_check()
json_decref(json);
}
int main()
static void decode_any()
{
json_t *json;
json_error_t error;
json = json_loads("\"foo\"", JSON_DECODE_ANY, &error);
if (!json || !json_is_string(json))
fail("json_load decoded any failed - string");
json_decref(json);
json = json_loads("42", JSON_DECODE_ANY, &error);
if (!json || !json_is_integer(json))
fail("json_load decoded any failed - integer");
json_decref(json);
json = json_loads("true", JSON_DECODE_ANY, &error);
if (!json || !json_is_true(json))
fail("json_load decoded any failed - boolean");
json_decref(json);
json = json_loads("null", JSON_DECODE_ANY, &error);
if (!json || !json_is_null(json))
fail("json_load decoded any failed - null");
json_decref(json);
}
static void decode_int_as_real()
{
json_t *json;
json_error_t error;
#if JSON_INTEGER_IS_LONG_LONG
const char *imprecise;
json_int_t expected;
#endif
char big[311];
json = json_loads("42", JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY, &error);
if (!json || !json_is_real(json) || json_real_value(json) != 42.0)
fail("json_load decode int as real failed - int");
json_decref(json);
#if JSON_INTEGER_IS_LONG_LONG
/* This number cannot be represented exactly by a double */
imprecise = "9007199254740993";
expected = 9007199254740992ll;
json = json_loads(imprecise, JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY,
&error);
if (!json || !json_is_real(json) || expected != (json_int_t)json_real_value(json))
fail("json_load decode int as real failed - expected imprecision");
json_decref(json);
#endif
/* 1E309 overflows. Here we create 1E309 as a decimal number, i.e.
1000...(309 zeroes)...0. */
big[0] = '1';
memset(big + 1, '0', 309);
big[310] = '\0';
json = json_loads(big, JSON_DECODE_INT_AS_REAL | JSON_DECODE_ANY, &error);
if (json || strcmp(error.text, "real number overflow") != 0)
fail("json_load decode int as real failed - expected overflow");
json_decref(json);
}
static void allow_nul()
{
const char *text = "\"nul byte \\u0000 in string\"";
const char *expected = "nul byte \0 in string";
size_t len = 20;
json_t *json;
json = json_loads(text, JSON_ALLOW_NUL | JSON_DECODE_ANY, NULL);
if(!json || !json_is_string(json))
fail("unable to decode embedded NUL byte");
if(json_string_length(json) != len)
fail("decoder returned wrong string length");
if(memcmp(json_string_value(json), expected, len + 1))
fail("decoder returned wrong string content");
json_decref(json);
}
static void load_wrong_args()
{
json_t *json;
json_error_t error;
json = json_loads(NULL, 0, &error);
if (json)
fail("json_loads should return NULL if the first argument is NULL");
json = json_loadb(NULL, 0, 0, &error);
if (json)
fail("json_loadb should return NULL if the first argument is NULL");
json = json_loadf(NULL, 0, &error);
if (json)
fail("json_loadf should return NULL if the first argument is NULL");
json = json_load_file(NULL, 0, &error);
if (json)
fail("json_loadf should return NULL if the first argument is NULL");
}
static void position()
{
json_t *json;
size_t flags = JSON_DISABLE_EOF_CHECK;
json_error_t error;
json = json_loads("{\"foo\": \"bar\"}", 0, &error);
if(error.position != 14)
fail("json_loads returned a wrong position");
json_decref(json);
json = json_loads("{\"foo\": \"bar\"} baz quux", flags, &error);
if(error.position != 14)
fail("json_loads returned a wrong position");
json_decref(json);
}
static void run_tests()
{
file_not_found();
very_long_file_name();
reject_duplicates();
disable_eof_check();
return 0;
decode_any();
decode_int_as_real();
allow_nul();
load_wrong_args();
position();
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <jansson.h>
#include <string.h>
#include <stdlib.h>
#include "util.h"
struct my_source {
const char *buf;
size_t off;
size_t cap;
};
static const char my_str[] = "[\"A\", {\"B\": \"C\", \"e\": false}, 1, null, \"foo\"]";
static size_t greedy_reader(void *buf, size_t buflen, void *arg)
{
struct my_source *s = arg;
if (buflen > s->cap - s->off)
buflen = s->cap - s->off;
if (buflen > 0) {
memcpy(buf, s->buf + s->off, buflen);
s->off += buflen;
return buflen;
} else {
return 0;
}
}
static void run_tests()
{
struct my_source s;
json_t *json;
json_error_t error;
s.off = 0;
s.cap = strlen(my_str);
s.buf = my_str;
json = json_load_callback(greedy_reader, &s, 0, &error);
if (!json)
fail("json_load_callback failed on a valid callback");
json_decref(json);
s.off = 0;
s.cap = strlen(my_str) - 1;
s.buf = my_str;
json = json_load_callback(greedy_reader, &s, 0, &error);
if (json) {
json_decref(json);
fail("json_load_callback should have failed on an incomplete stream, but it didn't");
}
if (strcmp(error.source, "<callback>") != 0) {
fail("json_load_callback returned an invalid error source");
}
if (strcmp(error.text, "']' expected near end of file") != 0) {
fail("json_load_callback returned an invalid error message for an unclosed top-level array");
}
json = json_load_callback(NULL, NULL, 0, &error);
if (json) {
json_decref(json);
fail("json_load_callback should have failed on NULL load callback, but it didn't");
}
if (strcmp(error.text, "wrong arguments") != 0) {
fail("json_load_callback returned an invalid error message for a NULL load callback");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 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,7 @@
#include <string.h>
#include "util.h"
int main()
static void run_tests()
{
json_t *json;
json_error_t error;
@@ -33,6 +33,4 @@ int main()
if(strcmp(error.text, "']' expected near end of file") != 0) {
fail("json_loadb returned an invalid error message for an unclosed top-level array");
}
return 0;
}

View File

@@ -5,13 +5,14 @@
static int malloc_called = 0;
static int free_called = 0;
static size_t malloc_used = 0;
/* helper */
/* helpers */
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]",
obj = json_pack("{s:i,s:n,s:b,s:b,s:{s:s},s:[i,i,i]}",
"foo", 42,
"bar",
"baz", 1,
@@ -22,28 +23,74 @@ static void create_and_free_complex_object()
json_decref(obj);
}
static void create_and_free_object_with_oom()
{
int i;
char key[4];
json_t *obj = json_object();
for (i = 0; i < 10; i++)
{
snprintf(key, sizeof key, "%d", i);
json_object_set_new(obj, key, json_integer(i));
}
json_decref(obj);
}
static void *my_malloc(size_t size)
{
malloc_called += 1;
malloc_called = 1;
return malloc(size);
}
static void my_free(void *ptr)
{
free_called += 1;
free_called = 1;
free(ptr);
}
static void test_simple()
{
json_malloc_t mfunc = NULL;
json_free_t ffunc = NULL;
json_set_alloc_funcs(my_malloc, my_free);
json_get_alloc_funcs(&mfunc, &ffunc);
create_and_free_complex_object();
if(malloc_called != 27 || free_called != 27)
if (malloc_called != 1 || free_called != 1
|| mfunc != my_malloc || ffunc != my_free)
fail("Custom allocation failed");
}
static void *oom_malloc(size_t size)
{
if (malloc_used + size > 800)
return NULL;
malloc_used += size;
return malloc(size);
}
static void oom_free(void *ptr)
{
free_called++;
free(ptr);
}
static void test_oom()
{
free_called = 0;
json_set_alloc_funcs(oom_malloc, oom_free);
create_and_free_object_with_oom();
if (free_called == 0)
fail("Allocation with OOM failed");
}
/*
Test the secure memory functions code given in the API reference
documentation, but by using plain memset instead of
@@ -55,17 +102,17 @@ 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;
return (char *)ptr + 8;
}
static void secure_free(void *ptr)
{
size_t size;
ptr -= 8;
ptr = (char *)ptr - 8;
size = *((size_t *)ptr);
/*guaranteed_*/memset(ptr, 0, size);
/*guaranteed_*/memset(ptr, 0, size + 8);
free(ptr);
}
@@ -75,10 +122,9 @@ static void test_secure_funcs(void)
create_and_free_complex_object();
}
int main()
static void run_tests()
{
test_simple();
test_secure_funcs();
return 0;
test_oom();
}

View File

@@ -1,17 +1,46 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 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 <math.h>
#include <jansson.h>
#include "util.h"
int main()
#ifdef INFINITY
// This test triggers "warning C4756: overflow in constant arithmetic"
// in Visual Studio. This warning is triggered here by design, so disable it.
// (This can only be done on function level so we keep these tests separate)
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning (disable: 4756)
#endif
static void test_inifity()
{
json_t *real = json_real(INFINITY);
if (real != NULL)
fail("could construct a real from Inf");
real = json_real(1.0);
if (json_real_set(real, INFINITY) != -1)
fail("could set a real to Inf");
if (json_real_value(real) != 1.0)
fail("real value changed unexpectedly");
json_decref(real);
#ifdef _MSC_VER
#pragma warning(pop)
#endif
}
#endif // INFINITY
static void run_tests()
{
json_t *integer, *real;
int i;
json_int_t i;
double d;
integer = json_integer(5);
@@ -40,5 +69,22 @@ int main()
json_decref(integer);
json_decref(real);
return 0;
#ifdef NAN
real = json_real(NAN);
if(real != NULL)
fail("could construct a real from NaN");
real = json_real(1.0);
if(json_real_set(real, NAN) != -1)
fail("could set a real to NaN");
if(json_real_value(real) != 1.0)
fail("real value changed unexpectedly");
json_decref(real);
#endif
#ifdef INFINITY
test_inifity();
#endif
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 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.
@@ -94,7 +94,7 @@ static void test_update()
/* perform the same update again */
if(json_object_update(object, other))
fail("unable to update an empty object");
fail("unable to update a non-empty object");
if(json_object_size(object) != 5)
fail("invalid size after update");
@@ -139,6 +139,74 @@ static void test_update()
json_decref(object);
}
static void test_set_many_keys()
{
json_t *object, *value;
const char *keys = "abcdefghijklmnopqrstuvwxyz";
char buf[2];
size_t i;
object = json_object();
if (!object)
fail("unable to create object");
value = json_string("a");
if (!value)
fail("unable to create string");
buf[1] = '\0';
for (i = 0; i < strlen(keys); i++) {
buf[0] = keys[i];
if (json_object_set(object, buf, value))
fail("unable to set object key");
}
json_decref(object);
json_decref(value);
}
static void test_conditional_updates()
{
json_t *object, *other;
object = json_pack("{sisi}", "foo", 1, "bar", 2);
other = json_pack("{sisi}", "foo", 3, "baz", 4);
if(json_object_update_existing(object, other))
fail("json_object_update_existing failed");
if(json_object_size(object) != 2)
fail("json_object_update_existing added new items");
if(json_integer_value(json_object_get(object, "foo")) != 3)
fail("json_object_update_existing failed to update existing key");
if(json_integer_value(json_object_get(object, "bar")) != 2)
fail("json_object_update_existing updated wrong key");
json_decref(object);
object = json_pack("{sisi}", "foo", 1, "bar", 2);
if(json_object_update_missing(object, other))
fail("json_object_update_missing failed");
if(json_object_size(object) != 3)
fail("json_object_update_missing didn't add new items");
if(json_integer_value(json_object_get(object, "foo")) != 1)
fail("json_object_update_missing updated existing key");
if(json_integer_value(json_object_get(object, "bar")) != 2)
fail("json_object_update_missing updated wrong key");
if(json_integer_value(json_object_get(object, "baz")) != 4)
fail("json_object_update_missing didn't add new items");
json_decref(object);
json_decref(other);
}
static void test_circular()
{
json_t *object1, *object2;
@@ -220,7 +288,7 @@ static void test_iterators()
foo = json_string("foo");
bar = json_string("bar");
baz = json_string("baz");
if(!object || !foo || !bar || !bar)
if(!object || !foo || !bar || !baz)
fail("unable to create values");
if(json_object_iter_next(object, NULL))
@@ -234,26 +302,26 @@ static void test_iterators()
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");
if (strcmp(json_object_iter_key(iter), "a") != 0)
fail("iterating doesn't yield keys in order");
if (json_object_iter_value(iter) != foo)
fail("iterating doesn't yield values in order");
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");
if (strcmp(json_object_iter_key(iter), "b") != 0)
fail("iterating doesn't yield keys in order");
if (json_object_iter_value(iter) != bar)
fail("iterating doesn't yield values in order");
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 (strcmp(json_object_iter_key(iter), "c") != 0)
fail("iterating doesn't yield keys in order");
if (json_object_iter_value(iter) != baz)
fail("iterating doesn't yield values in order");
if(json_object_iter_next(object, iter) != NULL)
fail("able to iterate over the end");
@@ -270,22 +338,14 @@ static void test_iterators()
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))
if(json_object_iter_set(object, iter, baz))
fail("unable to set value at iterator");
if(strcmp(json_object_iter_key(iter), "c"))
if(strcmp(json_object_iter_key(iter), "b"))
fail("json_object_iter_key() fails after json_object_iter_set()");
if(json_object_iter_value(iter) != bar)
if(json_object_iter_value(iter) != baz)
fail("json_object_iter_value() fails after json_object_iter_set()");
if(json_object_get(object, "c") != bar)
if(json_object_get(object, "b") != baz)
fail("json_object_get() fails after json_object_iter_set()");
json_decref(object);
@@ -316,6 +376,12 @@ static void test_misc()
if(!json_object_set(object, NULL, string))
fail("able to set NULL key");
if(json_object_del(object, "a"))
fail("unable to del the only key");
if(json_object_set(object, "a", string))
fail("unable to set value");
if(!json_object_set(object, "a", NULL))
fail("able to set NULL value");
@@ -437,15 +503,53 @@ static void test_preserve_order()
json_decref(object);
}
int main()
static void test_object_foreach()
{
const char *key;
json_t *object1, *object2, *value;
object1 = json_pack("{sisisi}", "foo", 1, "bar", 2, "baz", 3);
object2 = json_object();
json_object_foreach(object1, key, value)
json_object_set(object2, key, value);
if(!json_equal(object1, object2))
fail("json_object_foreach failed to iterate all key-value pairs");
json_decref(object1);
json_decref(object2);
}
static void test_object_foreach_safe()
{
const char *key;
void *tmp;
json_t *object, *value;
object = json_pack("{sisisi}", "foo", 1, "bar", 2, "baz", 3);
json_object_foreach_safe(object, tmp, key, value) {
json_object_del(object, key);
}
if(json_object_size(object) != 0)
fail("json_object_foreach_safe failed to iterate all key-value pairs");
json_decref(object);
}
static void run_tests()
{
test_misc();
test_clear();
test_update();
test_set_many_keys();
test_conditional_updates();
test_circular();
test_set_nocheck();
test_iterators();
test_preserve_order();
return 0;
test_object_foreach();
test_object_foreach_safe();
}

View File

@@ -1,31 +1,37 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2010-2011 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2010-2012 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.
*/
#ifdef HAVE_CONFIG_H
#include <jansson_private_config.h>
#endif
#include <jansson_config.h>
#include <string.h>
#include <jansson.h>
#include <stdio.h>
#include "util.h"
int main()
static void run_tests()
{
json_t *value;
int i;
char buffer[4] = {'t', 'e', 's', 't'};
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)
if(value->refcount != (size_t)-1)
fail("json_pack boolean refcount failed");
json_decref(value);
@@ -33,7 +39,7 @@ int main()
value = json_pack("b", 0);
if(!json_is_false(value))
fail("json_pack boolean failed");
if(value->refcount != (ssize_t)-1)
if(value->refcount != (size_t)-1)
fail("json_pack boolean refcount failed");
json_decref(value);
@@ -41,7 +47,7 @@ int main()
value = json_pack("n");
if(!json_is_null(value))
fail("json_pack null failed");
if(value->refcount != (ssize_t)-1)
if(value->refcount != (size_t)-1)
fail("json_pack null refcount failed");
json_decref(value);
@@ -49,7 +55,7 @@ int main()
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)
if(value->refcount != (size_t)1)
fail("json_pack integer refcount failed");
json_decref(value);
@@ -57,7 +63,7 @@ int main()
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)
if(value->refcount != (size_t)1)
fail("json_pack integer refcount failed");
json_decref(value);
@@ -65,7 +71,7 @@ int main()
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)
if(value->refcount != (size_t)1)
fail("json_pack real refcount failed");
json_decref(value);
@@ -73,15 +79,87 @@ int main()
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)
if(value->refcount != (size_t)1)
fail("json_pack string refcount failed");
json_decref(value);
/* nullable string (defined case) */
value = json_pack("s?", "test");
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack nullable string (defined case) failed");
if(value->refcount != (size_t)1)
fail("json_pack nullable string (defined case) refcount failed");
json_decref(value);
/* nullable string (NULL case) */
value = json_pack("s?", NULL);
if(!json_is_null(value))
fail("json_pack nullable string (NULL case) failed");
if(value->refcount != (size_t)-1)
fail("json_pack nullable string (NULL case) refcount failed");
json_decref(value);
/* string and length (int) */
value = json_pack("s#", "test asdf", 4);
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack string and length failed");
if(value->refcount != (size_t)1)
fail("json_pack string and length refcount failed");
json_decref(value);
/* string and length (size_t) */
value = json_pack("s%", "test asdf", (size_t)4);
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack string and length failed");
if(value->refcount != (size_t)1)
fail("json_pack string and length refcount failed");
json_decref(value);
/* string and length (int), non-NUL terminated string */
value = json_pack("s#", buffer, 4);
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack string and length (int) failed");
if(value->refcount != (size_t)1)
fail("json_pack string and length (int) refcount failed");
json_decref(value);
/* string and length (size_t), non-NUL terminated string */
value = json_pack("s%", buffer, (size_t)4);
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack string and length (size_t) failed");
if(value->refcount != (size_t)1)
fail("json_pack string and length (size_t) refcount failed");
json_decref(value);
/* string concatenation */
value = json_pack("s++", "te", "st", "ing");
if(!json_is_string(value) || strcmp("testing", json_string_value(value)))
fail("json_pack string concatenation failed");
if(value->refcount != (size_t)1)
fail("json_pack string concatenation refcount failed");
json_decref(value);
/* string concatenation and length (int) */
value = json_pack("s#+#+", "test", 1, "test", 2, "test");
if(!json_is_string(value) || strcmp("ttetest", json_string_value(value)))
fail("json_pack string concatenation and length (int) failed");
if(value->refcount != (size_t)1)
fail("json_pack string concatenation and length (int) refcount failed");
json_decref(value);
/* string concatenation and length (size_t) */
value = json_pack("s%+%+", "test", (size_t)1, "test", (size_t)2, "test");
if(!json_is_string(value) || strcmp("ttetest", json_string_value(value)))
fail("json_pack string concatenation and length (size_t) failed");
if(value->refcount != (size_t)1)
fail("json_pack string concatenation and length (size_t) 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)
if(value->refcount != (size_t)1)
fail("json_pack empty object refcount failed");
json_decref(value);
@@ -89,7 +167,7 @@ int main()
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)
if(value->refcount != (size_t)1)
fail("json_pack empty list failed");
json_decref(value);
@@ -97,26 +175,68 @@ int main()
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)
if(value->refcount != (size_t)1)
fail("json_pack integer refcount failed");
json_decref(value);
/* non-incref'd nullable object (defined case) */
value = json_pack("o?", json_integer(1));
if(!json_is_integer(value) || json_integer_value(value) != 1)
fail("json_pack nullable object (defined case) failed");
if(value->refcount != (size_t)1)
fail("json_pack nullable object (defined case) refcount failed");
json_decref(value);
/* non-incref'd nullable object (NULL case) */
value = json_pack("o?", NULL);
if(!json_is_null(value))
fail("json_pack nullable object (NULL case) failed");
if(value->refcount != (size_t)-1)
fail("json_pack nullable object (NULL case) 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)
if(value->refcount != (size_t)2)
fail("json_pack integer refcount failed");
json_decref(value);
json_decref(value);
/* incref'd nullable object (defined case) */
value = json_pack("O?", json_integer(1));
if(!json_is_integer(value) || json_integer_value(value) != 1)
fail("json_pack incref'd nullable object (defined case) failed");
if(value->refcount != (size_t)2)
fail("json_pack incref'd nullable object (defined case) refcount failed");
json_decref(value);
json_decref(value);
/* incref'd nullable object (NULL case) */
value = json_pack("O?", NULL);
if(!json_is_null(value))
fail("json_pack incref'd nullable object (NULL case) failed");
if(value->refcount != (size_t)-1)
fail("json_pack incref'd nullable object (NULL case) refcount failed");
/* 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)
if(json_object_get(value, "foo")->refcount != (size_t)1)
fail("json_pack object refcount failed");
json_decref(value);
/* object with complex key */
value = json_pack("{s+#+: []}", "foo", "barbar", 3, "baz");
if(!json_is_object(value) || json_object_size(value) != 1)
fail("json_pack array failed");
if(!json_is_array(json_object_get(value, "foobarbaz")))
fail("json_pack array failed");
if(json_object_get(value, "foobarbaz")->refcount != (size_t)1)
fail("json_pack object refcount failed");
json_decref(value);
@@ -193,6 +313,11 @@ int main()
fail("json_pack failed to catch null argument string");
check_error("NULL string argument", "<args>", 1, 1, 1);
/* + on its own */
if(json_pack_ex(&error, 0, "+", NULL))
fail("json_pack failed to a lone +");
check_error("Unexpected format character '+'", "<format>", 1, 1, 1);
/* NULL format */
if(json_pack_ex(&error, 0, NULL))
fail("json_pack failed to catch NULL format string");
@@ -201,7 +326,7 @@ int main()
/* 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);
check_error("NULL string argument", "<args>", 1, 2, 2);
/* More complicated checks for row/columns */
if(json_pack_ex(&error, 0, "{ {}: s }", "foo"))
@@ -221,12 +346,10 @@ int main()
/* 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);
check_error("Invalid UTF-8 object key", "<args>", 1, 2, 2);
/* Invalid UTF-8 in a string */
if(json_pack_ex(&error, 0, "{s:s}", "foo", "\xff\xff"))
fail("json_pack failed to catch invalid UTF-8 in a string");
check_error("Invalid UTF-8 string", "<args>", 1, 4, 4);
return 0;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -10,10 +10,28 @@
#include "util.h"
/* Call the simple functions not covered by other tests of the public API */
int main()
static void run_tests()
{
json_t *value;
value = json_boolean(1);
if(!json_is_true(value))
fail("json_boolean(1) failed");
json_decref(value);
value = json_boolean(-123);
if(!json_is_true(value))
fail("json_boolean(-123) failed");
json_decref(value);
value = json_boolean(0);
if(!json_is_false(value))
fail("json_boolean(0) failed");
if(json_boolean_value(value) != 0)
fail("json_boolean_value failed");
json_decref(value);
value = json_integer(1);
if(json_typeof(value) != JSON_INTEGER)
fail("json_typeof failed");
@@ -56,11 +74,22 @@ int main()
fail("json_string failed");
if(strcmp(json_string_value(value), "foo"))
fail("invalid string value");
if (json_string_length(value) != 3)
fail("invalid string length");
if(json_string_set(value, "bar"))
if(json_string_set(value, "barr"))
fail("json_string_set failed");
if(strcmp(json_string_value(value), "bar"))
if(strcmp(json_string_value(value), "barr"))
fail("invalid string value");
if (json_string_length(value) != 4)
fail("invalid string length");
if(json_string_setn(value, "hi\0ho", 5))
fail("json_string_set failed");
if(memcmp(json_string_value(value), "hi\0ho\0", 6))
fail("invalid string value");
if (json_string_length(value) != 5)
fail("invalid string length");
json_decref(value);
@@ -78,11 +107,22 @@ int main()
fail("json_string_nocheck failed");
if(strcmp(json_string_value(value), "foo"))
fail("invalid string value");
if (json_string_length(value) != 3)
fail("invalid string length");
if(json_string_set_nocheck(value, "bar"))
if(json_string_set_nocheck(value, "barr"))
fail("json_string_set_nocheck failed");
if(strcmp(json_string_value(value), "bar"))
if(strcmp(json_string_value(value), "barr"))
fail("invalid string value");
if (json_string_length(value) != 4)
fail("invalid string length");
if(json_string_setn_nocheck(value, "hi\0ho", 5))
fail("json_string_set failed");
if(memcmp(json_string_value(value), "hi\0ho\0", 6))
fail("invalid string value");
if (json_string_length(value) != 5)
fail("invalid string length");
json_decref(value);
@@ -92,11 +132,15 @@ int main()
fail("json_string_nocheck failed");
if(strcmp(json_string_value(value), "qu\xff"))
fail("invalid string value");
if (json_string_length(value) != 3)
fail("invalid string length");
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");
if (json_string_length(value) != 3)
fail("invalid string length");
json_decref(value);
@@ -180,6 +224,4 @@ int main()
json_incref(value);
if(value->refcount != (size_t)-1)
fail("refcounting null works incorrectly");
return 0;
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2010-2011 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
* Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2010-2012 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.
@@ -11,12 +11,13 @@
#include <stdio.h>
#include "util.h"
int main()
static void run_tests()
{
json_t *j, *j2;
int i1, i2, i3;
json_int_t I1;
int rv;
size_t z;
double f;
char *s;
@@ -81,6 +82,13 @@ int main()
fail("json_unpack string failed");
json_decref(j);
/* string with length (size_t) */
j = json_string("foo");
rv = json_unpack(j, "s%", &s, &z);
if(rv || strcmp(s, "foo") || z != 3)
fail("json_unpack string with length (size_t) failed");
json_decref(j);
/* empty object */
j = json_object();
if(json_unpack(j, "{}"))
@@ -96,14 +104,14 @@ int main()
/* non-incref'd object */
j = json_object();
rv = json_unpack(j, "o", &j2);
if(j2 != j || j->refcount != 1)
if(rv || 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)
if(rv || j2 != j || j->refcount != 2)
fail("json_unpack object failed");
json_decref(j);
json_decref(j);
@@ -290,10 +298,16 @@ int main()
json_decref(j);
/* Unpack the same item twice */
j = json_pack("{s:s, s:i}", "foo", "bar", "baz", 42);
j = json_pack("{s:s, s:i, s:b}", "foo", "bar", "baz", 42, "quux", 1);
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);
{
const char *possible_errors[] = {
"2 object item(s) left unpacked: baz, quux",
"2 object item(s) left unpacked: quux, baz"
};
check_errors(possible_errors, 2, "<validation>", 1, 10, 10);
}
json_decref(j);
j = json_pack("[i,{s:i,s:n},[i,i]]", 1, "foo", 2, "bar", 3, 4);
@@ -327,7 +341,7 @@ int main()
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);
check_error("1 object item(s) left unpacked: baz", "<validation>", 1, 7, 7);
json_decref(j);
/* Error in nested array */
@@ -337,5 +351,56 @@ int main()
check_error("1 array item(s) left unpacked", "<validation>", 1, 5, 5);
json_decref(j);
return 0;
/* Optional values */
j = json_object();
i1 = 0;
if(json_unpack(j, "{s?i}", "foo", &i1))
fail("json_unpack failed for optional key");
if(i1 != 0)
fail("json_unpack unpacked an optional key");
json_decref(j);
i1 = 0;
j = json_pack("{si}", "foo", 42);
if(json_unpack(j, "{s?i}", "foo", &i1))
fail("json_unpack failed for an optional value");
if(i1 != 42)
fail("json_unpack failed to unpack an optional value");
json_decref(j);
j = json_object();
i1 = i2 = i3 = 0;
if(json_unpack(j, "{s?[ii]s?{s{si}}}",
"foo", &i1, &i2,
"bar", "baz", "quux", &i3))
fail("json_unpack failed for complex optional values");
if(i1 != 0 || i2 != 0 || i3 != 0)
fail("json_unpack unexpectedly unpacked something");
json_decref(j);
j = json_pack("{s{si}}", "foo", "bar", 42);
if(json_unpack(j, "{s?{s?i}}", "foo", "bar", &i1))
fail("json_unpack failed for complex optional values");
if(i1 != 42)
fail("json_unpack failed to unpack");
json_decref(j);
/* Combine ? and ! */
j = json_pack("{si}", "foo", 42);
i1 = i2 = 0;
if(json_unpack(j, "{sis?i!}", "foo", &i1, "bar", &i2))
fail("json_unpack failed for optional values with strict mode");
if(i1 != 42)
fail("json_unpack failed to unpack");
if(i2 != 0)
fail("json_unpack failed to unpack");
json_decref(j);
/* But don't compensate a missing key with an optional one. */
j = json_pack("{sisi}", "foo", 42, "baz", 43);
i1 = i2 = i3 = 0;
if(!json_unpack_ex(j, &error, 0, "{sis?i!}", "foo", &i1, "bar", &i2))
fail("json_unpack failed for optional values with strict mode and compensation");
check_error("1 object item(s) left unpacked: baz", "<validation>", 1, 8, 8);
json_decref(j);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2009-2014 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,19 @@
#ifndef UTIL_H
#define UTIL_H
#ifdef HAVE_CONFIG_H
#include <jansson_private_config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#if HAVE_LOCALE_H
#include <locale.h>
#endif
#include <jansson.h>
#define failhdr fprintf(stderr, "%s:%s:%d: ", __FILE__, __FUNCTION__, __LINE__)
#define failhdr fprintf(stderr, "%s:%d: ", __FILE__, __LINE__)
#define fail(msg) \
do { \
@@ -22,11 +30,22 @@
} while(0)
/* Assumes json_error_t error */
#define check_error(text_, source_, line_, column_, position_) \
#define check_errors(texts_, num_, source_, line_, column_, position_) \
do { \
if(strcmp(error.text, text_) != 0) { \
int i_, found_ = 0; \
for(i_ = 0; i_ < num_; i_++) { \
if(strcmp(error.text, texts_[i_]) == 0) { \
found_ = 1; \
break; \
} \
} \
if (!found_) { \
failhdr; \
fprintf(stderr, "text: \"%s\" != \"%s\"\n", error.text, text_); \
if (num_ == 1) { \
fprintf(stderr, "text: \"%s\" != \"%s\"\n", error.text, texts_[0]); \
} else { \
fprintf(stderr, "text: \"%s\" does not match\n", error.text); \
} \
exit(1); \
} \
if(strcmp(error.source, source_) != 0) { \
@@ -52,4 +71,20 @@
} \
} while(0)
/* Assumes json_error_t error */
#define check_error(text_, source_, line_, column_, position_) \
check_errors(&text_, 1, source_, line_, column_, position_)
static void run_tests();
int main() {
#ifdef HAVE_SETLOCALE
setlocale(LC_ALL, "");
#endif
run_tests();
return 0;
}
#endif

View File

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

View File

@@ -1 +1,3 @@
export JSON_COMPACT=1
JSON_COMPACT=1
HASHSEED=1
export JSON_COMPACT HASHSEED

View File

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

View File

@@ -1 +1 @@
["foo", "\u00e5 \u00e4 \u00f6", "foo \u00e5\u00e4", "\u00e5\u00e4 foo", "\u00e5 foo \u00e4", "clef g: \ud834\udd1e"]
["foo", "\u00E5 \u00E4 \u00F6", "foo \u00E5\u00E4", "\u00E5\u00E4 foo", "\u00E5 foo \u00E4", "clef g: \uD834\uDD1E"]

View File

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

View File

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

View File

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

View File

@@ -1 +1,3 @@
export JSON_INDENT=4
JSON_INDENT=4
HASHSEED=1
export JSON_INDENT HASHSEED

View File

@@ -0,0 +1,2 @@
HASHSEED=1
export HASHSEED

View File

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

View File

@@ -0,0 +1,2 @@
JSON_REAL_PRECISION=4
export JSON_REAL_PRECISION

View File

@@ -0,0 +1 @@
[1.23456789, 1.0, 1.0000000000000002]

View File

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

View File

@@ -1,6 +1,6 @@
#!/bin/sh
#
# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
# Copyright (c) 2009-2014 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.
@@ -14,7 +14,7 @@ run_test() {
if [ -f $test_path/env ]; then
. $test_path/env
fi
$json_process <$test_path/input >$test_log/stdout 2>$test_log/stderr
$json_process --env <$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

View File

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

View File

@@ -1,6 +1,6 @@
#!/bin/sh
#
# Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
# Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
#
# Jansson is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
@@ -10,7 +10,7 @@ is_test() {
}
run_test() {
$json_process <$test_path/input >$test_log/stdout 2>$test_log/stderr
$json_process --env <$test_path/input >$test_log/stdout 2>$test_log/stderr
valgrind_check $test_log/stderr || return 1
cmp -s $test_path/error $test_log/stderr
}

View File

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

View File

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

View File

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

View File

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

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