├── NEWS ├── README ├── tests ├── test_charcase.expected ├── test_obj_obj_get_ex-null.expected ├── test_obj_iter-del.expected ├── ucs_copyright_char.expected ├── test_float.expected ├── parse_flags.h ├── test_null.expected ├── test4.expected ├── test_locale.expected ├── test_object_object_add_ex.expected ├── test_object_object_add_exFormatted_plain.expected ├── test_object_object_add_exFormatted_spaced.expected ├── test_object_object_add_exFormatted_pretty.expected ├── test4.test ├── test_cast.test ├── test_float.test ├── test_null.test ├── test_parse.test ├── test_locale.test ├── test_charcase.test ├── test_printbuf.test ├── test_parse_int64.test ├── testReplaceExisting.test ├── test_set_serializer.test ├── test_many_subobj.test ├── test_obj_iter-del.test ├── test_obj_obj_get_ex-null.test ├── test2Formatted_plain.expected ├── test2.expected ├── test2Formatted_spaced.expected ├── ucs_copyright_char.test ├── test1.test ├── test2.test ├── ucs_copyright_char.c ├── testReplaceExisting.expected ├── test_object_object_add_ex.test ├── test_set_serializer.expected ├── test2Formatted_pretty.expected ├── chk_version.c ├── test1Formatted_plain.expected ├── test1.expected ├── test1Formatted_spaced.expected ├── test_locale.c ├── test1Formatted_pretty.expected ├── test_float.c ├── parse_flags.c ├── test_obj_obj_get_ex-null.c ├── test_charcase.c ├── cr_obj_multi.c ├── test_printbuf.expected ├── test2.c ├── test_parse_int64.expected ├── test4.c ├── test_null.c ├── test_obj_iter-del.c ├── test_object_object_add_ex.c ├── test_many_subobj.c ├── test_cast.expected ├── test_parse_int64.c ├── Makefile.am ├── testReplaceExisting.c ├── test-defs.sh ├── test_cast.c ├── test_parse.expected ├── test1.c ├── test_printbuf.c ├── test_many_subobj.expected └── test_parse.c ├── ChangeLog ├── CI ├── README ├── check_codestyle.sh ├── clang-check-sanitizer.sh └── try_merge.sh ├── autoconf-archive └── README.txt ├── AUTHORS ├── libfastjson-uninstalled.pc.in ├── libfastjson.pc.in ├── autogen.sh ├── json_version.c ├── .travis.yml ├── CONTRIBUTING.md ├── Makefile.am ├── m4 ├── ax_require_defined.m4 ├── atomic_operations.m4 ├── atomic_operations_64bit.m4 ├── ax_append_flag.m4 ├── ax_append_compile_flags.m4 └── ax_check_compile_flag.m4 ├── .gitignore ├── arraylist.h ├── json_util.h ├── README.html ├── debug.c ├── debug.h ├── README.md ├── DIFFERENCES ├── json_object_private.h ├── printbuf.h ├── json.h ├── COPYING ├── RELEASE_CHECKLIST.txt ├── arraylist.c ├── configure.ac ├── json_object_iterator.c ├── printbuf.c ├── json_tokener.h ├── json_util.c ├── atomic.h └── json_object_iterator.h /NEWS: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_charcase.expected: -------------------------------------------------------------------------------- 1 | OK 2 | -------------------------------------------------------------------------------- /tests/test_obj_obj_get_ex-null.expected: -------------------------------------------------------------------------------- 1 | found=1 2 | -------------------------------------------------------------------------------- /tests/test_obj_iter-del.expected: -------------------------------------------------------------------------------- 1 | b: "b" 2 | d: "d" 3 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsyslog/libfastjson/HEAD/ChangeLog -------------------------------------------------------------------------------- /tests/ucs_copyright_char.expected: -------------------------------------------------------------------------------- 1 | string = { "foo" : "©" } 2 | json = { 3 | "foo":"©" 4 | } 5 | -------------------------------------------------------------------------------- /tests/test_float.expected: -------------------------------------------------------------------------------- 1 | json = 1.0 2 | json = 1.23 3 | json = 123456789.0 4 | json = 123456789.123 5 | -------------------------------------------------------------------------------- /CI/README: -------------------------------------------------------------------------------- 1 | This directory contains scripts and other files 2 | related to the various Continous Integration 3 | environments. 4 | -------------------------------------------------------------------------------- /tests/parse_flags.h: -------------------------------------------------------------------------------- 1 | #ifndef __parse_flags_h 2 | #define __parse_flags_h 3 | int parse_flags(int argc, char **argv); 4 | #endif 5 | -------------------------------------------------------------------------------- /tests/test_null.expected: -------------------------------------------------------------------------------- 1 | JSON write result is correct: " \u0000 " 2 | PASS 3 | Re-parsed object string len=3, chars=[32, 0, 32] 4 | -------------------------------------------------------------------------------- /tests/test4.expected: -------------------------------------------------------------------------------- 1 | input: "\ud840\udd26,\ud840\udd27,\ud800\udd26,\ud800\udd27" 2 | JSON parse result is correct: 𠄦,𠄧,𐄦,𐄧 3 | PASS 4 | -------------------------------------------------------------------------------- /tests/test_locale.expected: -------------------------------------------------------------------------------- 1 | new_obj.to_string()=[ 1.2, 3.4, 123456.78, 5.0, 2.3e10 ] 2 | new_obj.to_string()=[1.2,3.4,123456.78,5.0,2.3e10] 3 | -------------------------------------------------------------------------------- /tests/test_object_object_add_ex.expected: -------------------------------------------------------------------------------- 1 | my_object= 2 | abc: 12 3 | foo: "bar" 4 | bool0: false 5 | bool1: true 6 | my_object.to_string()={ "abc": 12, "foo": "bar", "bool0": false, "bool1": true } 7 | -------------------------------------------------------------------------------- /tests/test_object_object_add_exFormatted_plain.expected: -------------------------------------------------------------------------------- 1 | my_object= 2 | abc: 12 3 | foo: "bar" 4 | bool0: false 5 | bool1: true 6 | my_object.to_string()={"abc":12,"foo":"bar","bool0":false,"bool1":true} 7 | -------------------------------------------------------------------------------- /tests/test_object_object_add_exFormatted_spaced.expected: -------------------------------------------------------------------------------- 1 | my_object= 2 | abc: 12 3 | foo: "bar" 4 | bool0: false 5 | bool1: true 6 | my_object.to_string()={ "abc": 12, "foo": "bar", "bool0": false, "bool1": true } 7 | -------------------------------------------------------------------------------- /autoconf-archive/README.txt: -------------------------------------------------------------------------------- 1 | 2 | Autoconf Archive fetched from: 3 | 4 | http://gnu.mirror.iweb.com/autoconf-archive/autoconf-archive-2015.09.25.tar.xz 5 | 6 | Grabbed the minimum files needed for the AX_APPEND_COMPILE_FLAGS macro. 7 | 8 | -------------------------------------------------------------------------------- /tests/test_object_object_add_exFormatted_pretty.expected: -------------------------------------------------------------------------------- 1 | my_object= 2 | abc: 12 3 | foo: "bar" 4 | bool0: false 5 | bool1: true 6 | my_object.to_string()={ 7 | "abc":12, 8 | "foo":"bar", 9 | "bool0":false, 10 | "bool1":true 11 | } 12 | -------------------------------------------------------------------------------- /CI/check_codestyle.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | mkdir _tmp_stylecheck 3 | cd _tmp_stylecheck 4 | git clone https://github.com/rsyslog/codestyle 5 | cd codestyle 6 | gcc --std=c99 stylecheck.c -o stylecheck 7 | cd ../.. 8 | find *.[ch] | xargs _tmp_stylecheck/codestyle/stylecheck -l 130 9 | -------------------------------------------------------------------------------- /CI/clang-check-sanitizer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -v 3 | 4 | export CC=gcc # clang --> package currently broken in fedora 23 5 | export CFLAGS="-g -fsanitize=address" 6 | ./autogen.sh 7 | ./configure 8 | cat config.log 9 | make clean 10 | export VERBOSE=1 11 | make check 12 | -------------------------------------------------------------------------------- /tests/test4.test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Common definitions 4 | if test -z "$srcdir"; then 5 | srcdir="${0%/*}" 6 | test "$srcdir" = "$0" && srcdir=. 7 | test -z "$srcdir" && srcdir=. 8 | fi 9 | . "$srcdir/test-defs.sh" 10 | 11 | run_output_test test4 12 | exit $? 13 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Rainer Gerhards 2 | 3 | With special thanks for the original json-c library: 4 | Michael Clark 5 | Jehiah Czebotar 6 | Eric Haszlakiewicz 7 | C. Watford (christopher.watford@gmail.com) 8 | 9 | -------------------------------------------------------------------------------- /tests/test_cast.test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Common definitions 4 | if test -z "$srcdir"; then 5 | srcdir="${0%/*}" 6 | test "$srcdir" = "$0" && srcdir=. 7 | test -z "$srcdir" && srcdir=. 8 | fi 9 | . "$srcdir/test-defs.sh" 10 | 11 | run_output_test test_cast 12 | exit $? 13 | -------------------------------------------------------------------------------- /tests/test_float.test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Common definitions 4 | if test -z "$srcdir"; then 5 | srcdir="${0%/*}" 6 | test "$srcdir" = "$0" && srcdir=. 7 | test -z "$srcdir" && srcdir=. 8 | fi 9 | . "$srcdir/test-defs.sh" 10 | 11 | run_output_test test_float 12 | exit $? 13 | -------------------------------------------------------------------------------- /tests/test_null.test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Common definitions 4 | if test -z "$srcdir"; then 5 | srcdir="${0%/*}" 6 | test "$srcdir" = "$0" && srcdir=. 7 | test -z "$srcdir" && srcdir=. 8 | fi 9 | . "$srcdir/test-defs.sh" 10 | 11 | run_output_test test_null 12 | exit $? 13 | -------------------------------------------------------------------------------- /tests/test_parse.test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Common definitions 4 | if test -z "$srcdir"; then 5 | srcdir="${0%/*}" 6 | test "$srcdir" = "$0" && srcdir=. 7 | test -z "$srcdir" && srcdir=. 8 | fi 9 | . "$srcdir/test-defs.sh" 10 | 11 | run_output_test test_parse 12 | exit $? 13 | -------------------------------------------------------------------------------- /libfastjson-uninstalled.pc.in: -------------------------------------------------------------------------------- 1 | prefix= 2 | exec_prefix= 3 | libdir=@abs_top_builddir@ 4 | includedir=@abs_top_srcdir@ 5 | 6 | Name: json 7 | Description: JSON implementation in C 8 | Version: @VERSION@ 9 | Requires: 10 | Libs: -L@abs_top_builddir@ -ljson-c 11 | Cflags: -I@abs_top_srcdir@ 12 | -------------------------------------------------------------------------------- /tests/test_locale.test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Common definitions 4 | if test -z "$srcdir"; then 5 | srcdir="${0%/*}" 6 | test "$srcdir" = "$0" && srcdir=. 7 | test -z "$srcdir" && srcdir=. 8 | fi 9 | . "$srcdir/test-defs.sh" 10 | 11 | run_output_test test_locale 12 | exit $? 13 | -------------------------------------------------------------------------------- /tests/test_charcase.test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Common definitions 4 | if test -z "$srcdir"; then 5 | srcdir="${0%/*}" 6 | test "$srcdir" = "$0" && srcdir=. 7 | test -z "$srcdir" && srcdir=. 8 | fi 9 | . "$srcdir/test-defs.sh" 10 | 11 | run_output_test test_charcase 12 | exit $? 13 | -------------------------------------------------------------------------------- /tests/test_printbuf.test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Common definitions 4 | if test -z "$srcdir"; then 5 | srcdir="${0%/*}" 6 | test "$srcdir" = "$0" && srcdir=. 7 | test -z "$srcdir" && srcdir=. 8 | fi 9 | . "$srcdir/test-defs.sh" 10 | 11 | run_output_test test_printbuf 12 | exit $? 13 | -------------------------------------------------------------------------------- /tests/test_parse_int64.test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Common definitions 4 | if test -z "$srcdir"; then 5 | srcdir="${0%/*}" 6 | test "$srcdir" = "$0" && srcdir=. 7 | test -z "$srcdir" && srcdir=. 8 | fi 9 | . "$srcdir/test-defs.sh" 10 | 11 | run_output_test test_parse_int64 12 | exit $? 13 | -------------------------------------------------------------------------------- /tests/testReplaceExisting.test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Common definitions 4 | if test -z "$srcdir"; then 5 | srcdir="${0%/*}" 6 | test "$srcdir" = "$0" && srcdir=. 7 | test -z "$srcdir" && srcdir=. 8 | fi 9 | . "$srcdir/test-defs.sh" 10 | 11 | run_output_test testReplaceExisting 12 | exit $? 13 | -------------------------------------------------------------------------------- /tests/test_set_serializer.test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Common definitions 4 | if test -z "$srcdir"; then 5 | srcdir="${0%/*}" 6 | test "$srcdir" = "$0" && srcdir=. 7 | test -z "$srcdir" && srcdir=. 8 | fi 9 | . "$srcdir/test-defs.sh" 10 | 11 | run_output_test test_set_serializer 12 | exit $? 13 | -------------------------------------------------------------------------------- /tests/test_many_subobj.test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Common definitions 4 | if test -z "$srcdir"; then 5 | srcdir="${0%/*}" 6 | test "$srcdir" = "$0" && srcdir=. 7 | test -z "$srcdir" && srcdir=. 8 | fi 9 | . "$srcdir/test-defs.sh" 10 | 11 | run_output_test test_many_subobj 12 | _err=$? 13 | 14 | exit $_err 15 | -------------------------------------------------------------------------------- /tests/test_obj_iter-del.test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Common definitions 4 | if test -z "$srcdir"; then 5 | srcdir="${0%/*}" 6 | test "$srcdir" = "$0" && srcdir=. 7 | test -z "$srcdir" && srcdir=. 8 | fi 9 | . "$srcdir/test-defs.sh" 10 | 11 | run_output_test test_obj_iter-del 12 | _err=$? 13 | 14 | exit $_err 15 | -------------------------------------------------------------------------------- /libfastjson.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: libfastjson 7 | Description: a fast JSON implementation in C 8 | Version: @VERSION@ 9 | Requires: 10 | Libs.private: @LIBS@ 11 | Libs: -L${libdir} -lfastjson -lm 12 | Cflags: -I${includedir}/libfastjson 13 | -------------------------------------------------------------------------------- /tests/test_obj_obj_get_ex-null.test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Common definitions 4 | if test -z "$srcdir"; then 5 | srcdir="${0%/*}" 6 | test "$srcdir" = "$0" && srcdir=. 7 | test -z "$srcdir" && srcdir=. 8 | fi 9 | . "$srcdir/test-defs.sh" 10 | 11 | run_output_test test_obj_obj_get_ex-null 12 | _err=$? 13 | 14 | exit $_err 15 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | autoreconf -v --install || exit 1 3 | 4 | # If there are any options, assume the user wants to run configure. 5 | # To run configure w/o any options, use ./autogen.sh --configure 6 | if [ $# -gt 0 ] ; then 7 | case "$1" in 8 | --conf*) 9 | shift 1 10 | ;; 11 | esac 12 | exec ./configure "$@" 13 | fi 14 | -------------------------------------------------------------------------------- /tests/test2Formatted_plain.expected: -------------------------------------------------------------------------------- 1 | new_obj.to_string()={"glossary":{"title":"example glossary","GlossDiv":{"title":"S","GlossList":[{"ID":"SGML","SortAs":"SGML","GlossTerm":"Standard Generalized Markup Language","Acronym":"SGML","Abbrev":"ISO 8879:1986","GlossDef":"A meta-markup language, used to create markup languages such as DocBook.","GlossSeeAlso":["GML","XML","markup"]}]}}} 2 | -------------------------------------------------------------------------------- /tests/test2.expected: -------------------------------------------------------------------------------- 1 | new_obj.to_string()={ "glossary": { "title": "example glossary", "GlossDiv": { "title": "S", "GlossList": [ { "ID": "SGML", "SortAs": "SGML", "GlossTerm": "Standard Generalized Markup Language", "Acronym": "SGML", "Abbrev": "ISO 8879:1986", "GlossDef": "A meta-markup language, used to create markup languages such as DocBook.", "GlossSeeAlso": [ "GML", "XML", "markup" ] } ] } } } 2 | -------------------------------------------------------------------------------- /tests/test2Formatted_spaced.expected: -------------------------------------------------------------------------------- 1 | new_obj.to_string()={ "glossary": { "title": "example glossary", "GlossDiv": { "title": "S", "GlossList": [ { "ID": "SGML", "SortAs": "SGML", "GlossTerm": "Standard Generalized Markup Language", "Acronym": "SGML", "Abbrev": "ISO 8879:1986", "GlossDef": "A meta-markup language, used to create markup languages such as DocBook.", "GlossSeeAlso": [ "GML", "XML", "markup" ] } ] } } } 2 | -------------------------------------------------------------------------------- /json_version.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Eric Haszlakiewicz 3 | * Copyright (c) 2016 Adiscon GmbH 4 | * Rainer Gerhards 5 | * 6 | * This library is free software; you can redistribute it and/or modify 7 | * it under the terms of the MIT license. See COPYING for details. 8 | */ 9 | #include "config.h" 10 | #include "json.h" 11 | 12 | const char *fjson_version(void) 13 | { 14 | return VERSION; 15 | } 16 | -------------------------------------------------------------------------------- /tests/ucs_copyright_char.test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Common definitions 3 | if test -z "$srcdir"; then 4 | srcdir="${0%/*}" 5 | test "$srcdir" = "$0" && srcdir=. 6 | test -z "$srcdir" && srcdir=. 7 | fi 8 | . "$srcdir/test-defs.sh" 9 | 10 | #if [ "x$CI_SOLARIS" = "xyes" ] ; then 11 | uname 12 | if [ `uname` = "SunOS" ] ; then 13 | echo "This test currently does not work on all flavors of Solaris." 14 | exit 77 15 | fi 16 | 17 | 18 | run_output_test ucs_copyright_char 19 | exit $? 20 | -------------------------------------------------------------------------------- /tests/test1.test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Common definitions 4 | if test -z "$srcdir"; then 5 | srcdir="${0%/*}" 6 | test "$srcdir" = "$0" && srcdir=. 7 | test -z "$srcdir" && srcdir=. 8 | fi 9 | . "$srcdir/test-defs.sh" 10 | 11 | run_output_test test1 12 | _err=$? 13 | 14 | for flag in plain spaced pretty ; do 15 | run_output_test -o test1Formatted_${flag} test1Formatted ${flag} 16 | _err2=$? 17 | if [ $_err -eq 0 ] ; then 18 | _err=$_err2 19 | fi 20 | done 21 | 22 | exit $_err 23 | -------------------------------------------------------------------------------- /tests/test2.test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Common definitions 4 | if test -z "$srcdir"; then 5 | srcdir="${0%/*}" 6 | test "$srcdir" = "$0" && srcdir=. 7 | test -z "$srcdir" && srcdir=. 8 | fi 9 | . "$srcdir/test-defs.sh" 10 | 11 | run_output_test test2 12 | _err=$? 13 | 14 | for flag in plain spaced pretty ; do 15 | run_output_test -o test2Formatted_${flag} test2Formatted ${flag} 16 | _err2=$? 17 | if [ $_err -eq 0 ] ; then 18 | _err=$_err2 19 | fi 20 | done 21 | 22 | exit $_err 23 | -------------------------------------------------------------------------------- /tests/ucs_copyright_char.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2016 by Rainer Gerhards 2 | * Released under ASL 2.0 */ 3 | #include "config.h" 4 | #include 5 | #include "../json_object.h" 6 | #include "../json_tokener.h" 7 | int main(void) 8 | { 9 | const char *s; 10 | json_object *json; 11 | 12 | s = "{ \"foo\" : \"\u00a9\" }"; 13 | 14 | printf("string = %s\n", s); 15 | json = json_tokener_parse(s); 16 | printf("json = %s\n", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY)); 17 | 18 | json_object_put(json); 19 | } 20 | -------------------------------------------------------------------------------- /tests/testReplaceExisting.expected: -------------------------------------------------------------------------------- 1 | ==== delete-in-loop test starting ==== 2 | Key at index 0 is [foo1] (kept) 3 | Key at index 1 is [foo2] (kept) 4 | Key at index 2 is [deleteme] (deleted) 5 | Key at index 3 is [foo3] (kept) 6 | ==== replace-value first loop starting ==== 7 | Key at index 0 is [foo1] 8 | Key at index 1 is [foo2] 9 | replacing value for key [foo2] 10 | Key at index 2 is [foo3] 11 | ==== second loop starting ==== 12 | Key at index 0 is [foo1] 13 | Key at index 1 is [foo2] 14 | pointer for key [foo2] does match 15 | Key at index 2 is [foo3] 16 | -------------------------------------------------------------------------------- /tests/test_object_object_add_ex.test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Common definitions 4 | if test -z "$srcdir"; then 5 | srcdir="${0%/*}" 6 | test "$srcdir" = "$0" && srcdir=. 7 | test -z "$srcdir" && srcdir=. 8 | fi 9 | . "$srcdir/test-defs.sh" 10 | 11 | run_output_test test_object_object_add_ex 12 | _err=$? 13 | 14 | for flag in plain spaced pretty ; do 15 | run_output_test -o test_object_object_add_exFormatted_${flag} test_object_object_add_exFormatted ${flag} 16 | _err2=$? 17 | if [ $_err -eq 0 ] ; then 18 | _err=$_err2 19 | fi 20 | done 21 | 22 | exit $_err 23 | -------------------------------------------------------------------------------- /tests/test_set_serializer.expected: -------------------------------------------------------------------------------- 1 | Test setting, then resetting a custom serializer: 2 | my_object.to_string(standard)={ "abc": 12, "foo": "bar" } 3 | my_object.to_string(custom serializer)=Custom Output 4 | Next line of output should be from the custom freeit function: 5 | freeit, value=123 6 | my_object.to_string(standard)={ "abc": 12, "foo": "bar" } 7 | Check that the custom serializer isn't free'd until the last fjson_object_put: 8 | my_object.to_string(custom serializer)=Custom Output 9 | Next line of output should be from the custom freeit function: 10 | freeit, value=123 11 | -------------------------------------------------------------------------------- /tests/test2Formatted_pretty.expected: -------------------------------------------------------------------------------- 1 | new_obj.to_string()={ 2 | "glossary":{ 3 | "title":"example glossary", 4 | "GlossDiv":{ 5 | "title":"S", 6 | "GlossList":[ 7 | { 8 | "ID":"SGML", 9 | "SortAs":"SGML", 10 | "GlossTerm":"Standard Generalized Markup Language", 11 | "Acronym":"SGML", 12 | "Abbrev":"ISO 8879:1986", 13 | "GlossDef":"A meta-markup language, used to create markup languages such as DocBook.", 14 | "GlossSeeAlso":[ 15 | "GML", 16 | "XML", 17 | "markup" 18 | ] 19 | } 20 | ] 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/chk_version.c: -------------------------------------------------------------------------------- 1 | /* libfastjson testbench tool 2 | * 3 | * Copyright (c) 2016 Adiscon GmbH 4 | * Rainer Gerhards 5 | * 6 | * This library is free software; you can redistribute it and/or modify 7 | * it under the terms of the MIT license. See COPYING for details. 8 | * 9 | */ 10 | #include "config.h" 11 | 12 | #include "../json.h" 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | int main(int __attribute__((unused)) argc, char __attribute__((unused)) **argv) 19 | { 20 | if(strcmp(fjson_version(), VERSION)) { 21 | fprintf(stderr, "ERROR: fjson_version reports '%s', VERSION is '%s'.\n", 22 | fjson_version(), VERSION); 23 | exit(1); 24 | } 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /tests/test1Formatted_plain.expected: -------------------------------------------------------------------------------- 1 | my_string= 2 | my_string.to_string()="\t" 3 | my_string=\ 4 | my_string.to_string()="\\" 5 | my_string=foo 6 | my_string.to_string()="foo" 7 | my_int=9 8 | my_int.to_string()=9 9 | my_array= 10 | [0]=1 11 | [1]=2 12 | [2]=3 13 | [3]=null 14 | [4]=5 15 | my_array.to_string()=[1,2,3,null,5] 16 | my_array= 17 | [0]=3 18 | [1]=1 19 | [2]=2 20 | [3]=null 21 | [4]=0 22 | my_array.to_string()=[3,1,2,null,0] 23 | my_array= 24 | [0]=null 25 | [1]=0 26 | [2]=1 27 | [3]=2 28 | [4]=3 29 | my_array.to_string()=[null,0,1,2,3] 30 | baz_obj.to_string()="fark" 31 | my_object= 32 | abc: 12 33 | foo: "bar" 34 | bool0: false 35 | bool1: true 36 | my_object.to_string()={"abc":12,"foo":"bar","bool0":false,"bool1":true} 37 | -------------------------------------------------------------------------------- /tests/test1.expected: -------------------------------------------------------------------------------- 1 | my_string= 2 | my_string.to_string()="\t" 3 | my_string=\ 4 | my_string.to_string()="\\" 5 | my_string=foo 6 | my_string.to_string()="foo" 7 | my_int=9 8 | my_int.to_string()=9 9 | my_array= 10 | [0]=1 11 | [1]=2 12 | [2]=3 13 | [3]=null 14 | [4]=5 15 | my_array.to_string()=[ 1, 2, 3, null, 5 ] 16 | my_array= 17 | [0]=3 18 | [1]=1 19 | [2]=2 20 | [3]=null 21 | [4]=0 22 | my_array.to_string()=[ 3, 1, 2, null, 0 ] 23 | my_array= 24 | [0]=null 25 | [1]=0 26 | [2]=1 27 | [3]=2 28 | [4]=3 29 | my_array.to_string()=[ null, 0, 1, 2, 3 ] 30 | baz_obj.to_string()="fark" 31 | my_object= 32 | abc: 12 33 | foo: "bar" 34 | bool0: false 35 | bool1: true 36 | my_object.to_string()={ "abc": 12, "foo": "bar", "bool0": false, "bool1": true } 37 | -------------------------------------------------------------------------------- /tests/test1Formatted_spaced.expected: -------------------------------------------------------------------------------- 1 | my_string= 2 | my_string.to_string()="\t" 3 | my_string=\ 4 | my_string.to_string()="\\" 5 | my_string=foo 6 | my_string.to_string()="foo" 7 | my_int=9 8 | my_int.to_string()=9 9 | my_array= 10 | [0]=1 11 | [1]=2 12 | [2]=3 13 | [3]=null 14 | [4]=5 15 | my_array.to_string()=[ 1, 2, 3, null, 5 ] 16 | my_array= 17 | [0]=3 18 | [1]=1 19 | [2]=2 20 | [3]=null 21 | [4]=0 22 | my_array.to_string()=[ 3, 1, 2, null, 0 ] 23 | my_array= 24 | [0]=null 25 | [1]=0 26 | [2]=1 27 | [3]=2 28 | [4]=3 29 | my_array.to_string()=[ null, 0, 1, 2, 3 ] 30 | baz_obj.to_string()="fark" 31 | my_object= 32 | abc: 12 33 | foo: "bar" 34 | bool0: false 35 | bool1: true 36 | my_object.to_string()={ "abc": 12, "foo": "bar", "bool0": false, "bool1": true } 37 | -------------------------------------------------------------------------------- /tests/test_locale.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../json.h" 8 | #include "../json_tokener.h" 9 | #include "../debug.h" 10 | 11 | #ifdef HAVE_LOCALE_H 12 | #include 13 | #endif /* HAVE_LOCALE_H */ 14 | 15 | int main(int __attribute__((unused)) argc, char __attribute__((unused)) **argv) 16 | { 17 | fjson_object *new_obj; 18 | #ifdef HAVE_SETLOCALE 19 | setlocale(LC_NUMERIC, "de_DE"); 20 | #else 21 | printf("No locale\n"); 22 | #endif 23 | 24 | MC_SET_DEBUG(1); 25 | 26 | new_obj = fjson_tokener_parse("[1.2,3.4,123456.78,5.0,2.3e10]"); 27 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 28 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string_ext(new_obj,FJSON_TO_STRING_NOZERO)); 29 | fjson_object_put(new_obj); 30 | return 0; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /tests/test1Formatted_pretty.expected: -------------------------------------------------------------------------------- 1 | my_string= 2 | my_string.to_string()="\t" 3 | my_string=\ 4 | my_string.to_string()="\\" 5 | my_string=foo 6 | my_string.to_string()="foo" 7 | my_int=9 8 | my_int.to_string()=9 9 | my_array= 10 | [0]=1 11 | [1]=2 12 | [2]=3 13 | [3]=null 14 | [4]=5 15 | my_array.to_string()=[ 16 | 1, 17 | 2, 18 | 3, 19 | null, 20 | 5 21 | ] 22 | my_array= 23 | [0]=3 24 | [1]=1 25 | [2]=2 26 | [3]=null 27 | [4]=0 28 | my_array.to_string()=[ 29 | 3, 30 | 1, 31 | 2, 32 | null, 33 | 0 34 | ] 35 | my_array= 36 | [0]=null 37 | [1]=0 38 | [2]=1 39 | [3]=2 40 | [4]=3 41 | my_array.to_string()=[ 42 | null, 43 | 0, 44 | 1, 45 | 2, 46 | 3 47 | ] 48 | baz_obj.to_string()="fark" 49 | my_object= 50 | abc: 12 51 | foo: "bar" 52 | bool0: false 53 | bool1: true 54 | my_object.to_string()={ 55 | "abc":12, 56 | "foo":"bar", 57 | "bool0":false, 58 | "bool1":true 59 | } 60 | -------------------------------------------------------------------------------- /tests/test_float.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2016 by Rainer Gerhards 2 | * Released under ASL 2.0 */ 3 | #include "config.h" 4 | #include 5 | #include "../json_object.h" 6 | #include "../json_tokener.h" 7 | int main(void) 8 | { 9 | fjson_object *json; 10 | 11 | json = fjson_object_new_double(1.0); 12 | printf("json = %s\n", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY)); 13 | json_object_put(json); 14 | json = fjson_object_new_double(1.23); 15 | printf("json = %s\n", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY)); 16 | json_object_put(json); 17 | json = fjson_object_new_double(123456789.0); 18 | printf("json = %s\n", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY)); 19 | json_object_put(json); 20 | json = fjson_object_new_double(123456789.123); 21 | printf("json = %s\n", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY)); 22 | json_object_put(json); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: required 3 | dist: trusty 4 | 5 | compiler: 6 | - gcc 7 | - clang 8 | 9 | os: 10 | - linux 11 | - osx 12 | 13 | addons: 14 | apt: 15 | packages: 16 | - clang-3.6 17 | - autoconf-archive 18 | - valgrind 19 | 20 | env: 21 | - CFLAGS="-g" 22 | 23 | before_install: 24 | - echo $LANG 25 | - echo $LC_ALL 26 | 27 | install: 28 | - sh autogen.sh 29 | 30 | before_script: 31 | - # note: valgrind is only available on Linux 32 | - if [ "$CC" == "gcc" ] && [ "$TRAVIS_OS_NAME" == "linux" ]; then export ENA_VG="--enable-valgrind"; fi 33 | - if [ "$CC" == "clang" ] && [ "$TRAVIS_OS_NAME" == "linux" ]; then export SANITIZER="-fsanitize=address" ; fi 34 | - if [ "$CC" == "clang" ] && [ "$TRAVIS_OS_NAME" == "linux" ]; then export CFLAGS="$CFLAGS -fsanitize=address" ; fi 35 | - ./CI/check_codestyle.sh 36 | - ./configure $ENA_VG 37 | 38 | script: 39 | - if [ "$CC" == "clang" ] && [ "$TRAVIS_OS_NAME" == "linux" ]; then scan-build-3.6 --status-bugs make check TESTS="" && make clean ; fi 40 | - make 41 | - export VERBOSE=1 42 | - make check 43 | - make distcheck 44 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | CI Tests 2 | ======== 3 | We use various continous integration platforms and a testbench to 4 | ensure good code quality. 5 | 6 | All pull request MUST pass all CI tests in order to be considered 7 | for merging. 8 | 9 | If bugs are fixed or new functionality is provided, it is highly 10 | suggested to add a related test to the testbench. If not done, 11 | the merge may be rejected. This is not a hard rule as for some 12 | situations it may be very hard and even impossible to craft an 13 | automatic tests. This should not be used as an excuse for 14 | lazyness. 15 | 16 | Code Style 17 | ========== 18 | Unfortunaly, code style has not been officially described in the 19 | past. As such, some parts of the code do not yet fully conform to 20 | what we really want. 21 | 22 | Also, the rsyslog team has not yet fully agreed on a formal description 23 | of the coding style. This is currently under discussion. 24 | 25 | Here are the minimal style guidelines to ensure code will pass 26 | automatted code style checks during CI runs: 27 | 28 | * indentions are done via TAB, not spaces 29 | * no trailing whitespace is permitted at the end of line 30 | -------------------------------------------------------------------------------- /tests/parse_flags.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../json.h" 8 | #include "parse_flags.h" 9 | 10 | #if !defined(HAVE_STRCASECMP) 11 | # error You do not have strcasecmp on your system. 12 | #endif /* HAVE_STRNCASECMP */ 13 | 14 | static struct { 15 | const char *arg; 16 | int flag; 17 | } format_args[] = { 18 | { "plain", FJSON_TO_STRING_PLAIN }, 19 | { "spaced", FJSON_TO_STRING_SPACED }, 20 | { "pretty", FJSON_TO_STRING_PRETTY }, 21 | }; 22 | 23 | #ifndef NELEM 24 | #define NELEM(x) (sizeof(x) / sizeof(&x[0])) 25 | #endif 26 | 27 | int parse_flags(int argc, char **argv) 28 | { 29 | int arg_idx; 30 | int sflags = 0; 31 | for (arg_idx = 1; arg_idx < argc ; arg_idx++) 32 | { 33 | int jj; 34 | for (jj = 0; jj < (int)NELEM(format_args); jj++) 35 | { 36 | if (strcasecmp(argv[arg_idx], format_args[jj].arg) == 0) 37 | { 38 | sflags |= format_args[jj].flag; 39 | break; 40 | } 41 | } 42 | if (jj == NELEM(format_args)) 43 | { 44 | printf("Unknown arg: %s\n", argv[arg_idx]); 45 | exit(1); 46 | } 47 | } 48 | return sflags; 49 | } 50 | -------------------------------------------------------------------------------- /tests/test_obj_obj_get_ex-null.c: -------------------------------------------------------------------------------- 1 | /* libfastjson testbench tool 2 | * 3 | * Copyright (c) 2016 Adiscon GmbH 4 | * Rainer Gerhards 5 | * 6 | * This library is free software; you can redistribute it and/or modify 7 | * it under the terms of the MIT license. See COPYING for details. 8 | * 9 | */ 10 | #include "config.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "../json.h" 17 | #include "../debug.h" 18 | #include "parse_flags.h" 19 | 20 | /* this is a work-around until we manage to fix configure.ac */ 21 | #pragma GCC diagnostic ignored "-Wdeclaration-after-statement" 22 | 23 | #define DEBUG_SEED(s) 24 | 25 | int main(int __attribute__((unused)) argc, char __attribute__((unused)) **argv) 26 | { 27 | fjson_object *my_object; 28 | 29 | MC_SET_DEBUG(1); 30 | 31 | my_object = fjson_object_new_object(); 32 | fjson_object_object_add_ex(my_object, "a", fjson_object_new_int(1), 0); 33 | 34 | int found = fjson_object_object_get_ex(my_object, "a", NULL); 35 | printf("found=%d\n", found); 36 | 37 | fjson_object_put(my_object); 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /tests/test_charcase.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../json.h" 8 | #include "../json_tokener.h" 9 | #include "../debug.h" 10 | 11 | #define CHK(x) if (!(x)) { \ 12 | printf("%s:%d: unexpected result with '%s'\n", \ 13 | __FILE__, __LINE__, #x); \ 14 | exit(1); \ 15 | } 16 | 17 | static void test_case_parse(void); 18 | 19 | int main(int __attribute__((unused)) argc, char __attribute__((unused)) **argv) 20 | { 21 | MC_SET_DEBUG(1); 22 | 23 | test_case_parse(); 24 | return 0; 25 | } 26 | 27 | /* make sure only lowercase forms are parsed in strict mode */ 28 | static void test_case_parse(void) 29 | { 30 | struct fjson_tokener *tok; 31 | fjson_object *new_obj; 32 | 33 | tok = fjson_tokener_new(); 34 | fjson_tokener_set_flags(tok, FJSON_TOKENER_STRICT); 35 | 36 | new_obj = fjson_tokener_parse_ex(tok, "True", 4); 37 | CHK(new_obj == NULL); 38 | 39 | new_obj = fjson_tokener_parse_ex(tok, "False", 5); 40 | CHK(new_obj == NULL); 41 | 42 | new_obj = fjson_tokener_parse_ex(tok, "Null", 4); 43 | CHK(new_obj == NULL); 44 | 45 | printf("OK\n"); 46 | 47 | fjson_tokener_free(tok); 48 | } 49 | -------------------------------------------------------------------------------- /tests/cr_obj_multi.c: -------------------------------------------------------------------------------- 1 | /* libfastjson testbench tool 2 | * 3 | * Copyright (c) 2016 Adiscon GmbH 4 | * Rainer Gerhards 5 | * 6 | * This library is free software; you can redistribute it and/or modify 7 | * it under the terms of the MIT license. See COPYING for details. 8 | * 9 | */ 10 | #include "config.h" 11 | 12 | #include "../json.h" 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #define NUM_CREATIONS 1000000 19 | 20 | int main(int __attribute__((unused)) argc, char __attribute__((unused)) **argv) 21 | { 22 | int i; 23 | char pb[64]; 24 | struct fjson_object **json = calloc(NUM_CREATIONS, sizeof(struct fjson_object *)); 25 | 26 | if(json == NULL) { 27 | perror("malloc ptr table failed:"); 28 | exit(1); 29 | } 30 | 31 | for(i = 0 ; i < NUM_CREATIONS ; ++i) { 32 | json[i] = fjson_object_new_object(); 33 | //fprintf(stderr, "main: json[%d] %p\n", i, json[i]); 34 | snprintf(pb, sizeof(pb), "%d", i); 35 | fjson_object_object_add(json[i], pb, fjson_object_new_string(pb)); 36 | 37 | } 38 | 39 | /* free all objects again */ 40 | for(i = 0 ; i < NUM_CREATIONS ; ++i) { 41 | fjson_object_put(json[i]); 42 | } 43 | 44 | free(json); 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | EXTRA_DIST = README.html 3 | 4 | SUBDIRS = . tests 5 | 6 | lib_LTLIBRARIES = libfastjson.la 7 | noinst_LTLIBRARIES = libfastjson-internal.la 8 | 9 | pkgconfigdir = $(libdir)/pkgconfig 10 | pkgconfig_DATA = libfastjson.pc 11 | 12 | libfastjsonincludedir = $(includedir)/libfastjson 13 | libfastjsoninclude_HEADERS = \ 14 | atomic.h \ 15 | json.h \ 16 | json_object.h \ 17 | json_object_iterator.h \ 18 | json_object_private.h \ 19 | json_tokener.h \ 20 | json_util.h 21 | 22 | libfastjson_la_CFLAGS = $(WARN_CFLAGS) 23 | # info on version-info: 24 | # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html 25 | libfastjson_la_LDFLAGS = \ 26 | -version-info 7:0:3 \ 27 | -export-symbols-regex '^fjson_.*' \ 28 | -no-undefined \ 29 | @JSON_BSYMBOLIC_LDFLAGS@ 30 | libfastjson_la_LIBADD = libfastjson-internal.la 31 | 32 | libfastjson_la_SOURCES = \ 33 | json_version.c \ 34 | json_object.c \ 35 | json_print.c \ 36 | json_object_iterator.c \ 37 | json_tokener.c \ 38 | json_util.c 39 | 40 | libfastjson_internal_la_CFLAGS = $(WARN_CFLAGS) 41 | libfastjson_internal_la_SOURCES = \ 42 | arraylist.h \ 43 | arraylist.c \ 44 | debug.h \ 45 | debug.c \ 46 | printbuf.h \ 47 | printbuf.c 48 | 49 | ACLOCAL_AMFLAGS = -I m4 50 | -------------------------------------------------------------------------------- /tests/test_printbuf.expected: -------------------------------------------------------------------------------- 1 | test_basic_printbuf_memset: starting test 2 | Buffer contents:blue:1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 3 | test_basic_printbuf_memset: end test 4 | ======================================== 5 | test_printbuf_memset_length: starting test 6 | Buffer length: 0 7 | Buffer length: 12 8 | Buffer length: 18 9 | Buffer length: 76 10 | Buffer length: 76 11 | Buffer length: 77 12 | test_printbuf_memset_length: end test 13 | ======================================== 14 | test_printbuf_memappend: starting test 15 | Buffer length: 0 16 | Appended 32 bytes for resize: [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] 17 | Partial append: 3, [blu] 18 | With embedded \0 character: 4, [ab] 19 | Append to just before resize: 31, [XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX] 20 | Append to just after resize: 32, [XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX] 21 | test_printbuf_memappend: end test 22 | ======================================== 23 | test_sprintbuf: starting test 24 | Buffer length: 0 25 | sprintbuf to just after resize(31+1): 32, [XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX], strlen(buf)=32 26 | 5, [plain] 27 | 6, [plain1] 28 | 16, [plain12147483647] 29 | 27, [plain12147483647-2147483648] 30 | 29, [plain12147483647-2147483648%s] 31 | test_sprintbuf: end test 32 | ======================================== 33 | -------------------------------------------------------------------------------- /tests/test2.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../json.h" 8 | #include "../debug.h" 9 | #include "parse_flags.h" 10 | 11 | #ifdef TEST_FORMATTED 12 | #define fjson_object_to_json_string(obj) fjson_object_to_json_string_ext(obj,sflags) 13 | #else 14 | /* no special define */ 15 | #endif 16 | 17 | 18 | int main(int __attribute__((unused)) argc, char __attribute__((unused)) **argv) 19 | { 20 | fjson_object *new_obj; 21 | #ifdef TEST_FORMATTED 22 | int sflags = 0; 23 | #endif 24 | 25 | MC_SET_DEBUG(1); 26 | 27 | #ifdef TEST_FORMATTED 28 | sflags = parse_flags(argc, argv); 29 | #endif 30 | 31 | new_obj = fjson_tokener_parse("/* more difficult test case */ { \"glossary\": { \"title\": \"example glossary\", \"GlossDiv\": { \"title\": \"S\", \"GlossList\": [ { \"ID\": \"SGML\", \"SortAs\": \"SGML\", \"GlossTerm\": \"Standard Generalized Markup Language\", \"Acronym\": \"SGML\", \"Abbrev\": \"ISO 8879:1986\", \"GlossDef\": \"A meta-markup language, used to create markup languages such as DocBook.\", \"GlossSeeAlso\": [\"GML\", \"XML\", \"markup\"] } ] } } }"); 32 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 33 | fjson_object_put(new_obj); 34 | 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /tests/test_parse_int64.expected: -------------------------------------------------------------------------------- 1 | buf=x parseit=1, value=-666 2 | buf=0 parseit=0, value=0 3 | buf=-0 parseit=0, value=0 4 | buf=00000000 parseit=0, value=0 5 | buf=-00000000 parseit=0, value=0 6 | buf=1 parseit=0, value=1 7 | buf=2147483647 parseit=0, value=2147483647 8 | buf=-1 parseit=0, value=-1 9 | buf= -1 parseit=0, value=-1 10 | buf=00001234 parseit=0, value=1234 11 | buf=0001234x parseit=0, value=1234 12 | buf=-00001234 parseit=0, value=-1234 13 | buf=-00001234x parseit=0, value=-1234 14 | buf=21474836470 parseit=0, value=21474836470 15 | buf=31474836470 parseit=0, value=31474836470 16 | buf=-2147483647 parseit=0, value=-2147483647 17 | buf=-2147483648 parseit=0, value=-2147483648 18 | buf=-2147483649 parseit=0, value=-2147483649 19 | buf=-21474836480 parseit=0, value=-21474836480 20 | buf=9223372036854775806 parseit=0, value=9223372036854775806 21 | buf=9223372036854775807 parseit=0, value=9223372036854775807 22 | buf=9223372036854775808 parseit=0, value=9223372036854775807 23 | buf=-9223372036854775808 parseit=0, value=-9223372036854775808 24 | buf=-9223372036854775809 parseit=0, value=-9223372036854775808 25 | buf=18446744073709551614 parseit=0, value=9223372036854775807 26 | buf=18446744073709551615 parseit=0, value=9223372036854775807 27 | buf=123 parseit=0, value=123 28 | -------------------------------------------------------------------------------- /m4/ax_require_defined.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_require_defined.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_REQUIRE_DEFINED(MACRO) 8 | # 9 | # DESCRIPTION 10 | # 11 | # AX_REQUIRE_DEFINED is a simple helper for making sure other macros have 12 | # been defined and thus are available for use. This avoids random issues 13 | # where a macro isn't expanded. Instead the configure script emits a 14 | # non-fatal: 15 | # 16 | # ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found 17 | # 18 | # It's like AC_REQUIRE except it doesn't expand the required macro. 19 | # 20 | # Here's an example: 21 | # 22 | # AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG]) 23 | # 24 | # LICENSE 25 | # 26 | # Copyright (c) 2014 Mike Frysinger 27 | # 28 | # Copying and distribution of this file, with or without modification, are 29 | # permitted in any medium without royalty provided the copyright notice 30 | # and this notice are preserved. This file is offered as-is, without any 31 | # warranty. 32 | 33 | #serial 1 34 | 35 | AC_DEFUN([AX_REQUIRE_DEFINED], [dnl 36 | m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])]) 37 | ])dnl AX_REQUIRE_DEFINED 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | *.swo 4 | *.tar.gz 5 | callgrind.out.* 6 | tmp 7 | libfastjson-internal.la 8 | /INSTALL 9 | .dirstamp 10 | .deps/ 11 | .libs/ 12 | /aclocal.m4 13 | /autom4te.cache 14 | m4/libtool.m4 15 | m4/ltoptions.m4 16 | m4/ltsugar.m4 17 | m4/ltversion.m4 18 | m4/lt~obsolete.m4 19 | /config.guess 20 | /json_config.h 21 | /compile 22 | /config.h 23 | /config.h.in 24 | /config.log 25 | /config.status 26 | /config.sub 27 | /configure 28 | /depcomp 29 | /doc 30 | /install-sh 31 | /libfastjson.pc 32 | /libfastjson-uninstalled.pc 33 | /libtool 34 | /ltmain.sh 35 | /Makefile 36 | /Makefile.in 37 | /missing 38 | /stamp-h1 39 | /stamp-h2 40 | /test-driver 41 | /tests/Makefile 42 | /tests/Makefile.in 43 | /tests/test1 44 | /tests/test1Formatted 45 | /tests/test2 46 | /tests/test2Formatted 47 | /tests/test4 48 | /tests/testReplaceExisting 49 | /tests/testSubDir 50 | /tests/test_parse_int64 51 | /tests/test_parse 52 | /tests/test_cast 53 | /tests/test_charcase 54 | /tests/test_locale 55 | /tests/test_null 56 | /tests/test_printbuf 57 | /tests/test_set_serializer 58 | /tests/test_object_object_add_ex 59 | /tests/test_object_object_add_exFormatted 60 | /tests/test_many_subobj 61 | /tests/test_obj_iter-del 62 | /tests/chk_version 63 | /tests/*.vg.out 64 | /tests/*.log 65 | /tests/*.trs 66 | /Debug 67 | /Release 68 | /*/Debug 69 | /*/Release 70 | *.lo 71 | *.o 72 | /libfastjson.la 73 | /libjson.la 74 | -------------------------------------------------------------------------------- /CI/try_merge.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | oldbranch=`git rev-parse --abbrev-ref HEAD` 3 | git config --global user.email "buildbot@rsyslog.com" 4 | git config --global user.name "buildbot" 5 | git branch -D tmp-CI 6 | git branch tmp-CI 7 | git checkout tmp-CI 8 | git fetch origin 9 | git merge --no-edit origin/master 10 | if [ $? -ne 0 ]; then 11 | echo "======================================================================" 12 | echo "= FAIL: git merge, not doing any tests on the merge result =" 13 | echo "======================================================================" 14 | echo "Note: this is not an error per se, can happen. In this case the user" 15 | echo "must be somewhat more careful when merging." 16 | git merge --abort 17 | git checkout ${oldbranch} 18 | exit 0 19 | fi 20 | echo "======================================================================" 21 | echo "= SUCCESS: git merge; doing some more tests on master+PR =" 22 | echo "======================================================================" 23 | echo "Note: failing tests may not be reproducible if master branch advances." 24 | echo " However, they should not be taken lightly as they point into to" 25 | echo " two conflicting changes to the codebase." 26 | 27 | # from here on, we want to see what's going on and we also want to abort 28 | # on any error. 29 | set -v 30 | set -e 31 | 32 | CI/clang-check-sanitizer.sh 33 | 34 | git checkout ${oldbranch} 35 | -------------------------------------------------------------------------------- /m4/atomic_operations.m4: -------------------------------------------------------------------------------- 1 | # rsyslog 2 | # 3 | # atomic_operations.m4 - autoconf macro to check if compiler supports atomic 4 | # operations 5 | # 6 | # rgerhards, 2008-09-18, added based on 7 | # http://svn.apache.org/repos/asf/apr/apr/trunk/configure.in 8 | # 9 | # 10 | AC_DEFUN([RS_ATOMIC_OPERATIONS], 11 | [AC_CACHE_CHECK([whether the compiler provides atomic builtins], [ap_cv_atomic_builtins], 12 | [AC_LINK_IFELSE([AC_LANG_PROGRAM([], [[ 13 | unsigned long val = 1010, tmp, *mem = &val; 14 | 15 | if (__sync_fetch_and_add(&val, 1010) != 1010 || val != 2020) 16 | return 1; 17 | 18 | tmp = val; 19 | 20 | if (__sync_fetch_and_sub(mem, 1010) != tmp || val != 1010) 21 | return 1; 22 | 23 | if (__sync_sub_and_fetch(&val, 1010) != 0 || val != 0) 24 | return 1; 25 | 26 | tmp = 3030; 27 | 28 | if (__sync_val_compare_and_swap(mem, 0, tmp) != 0 || val != tmp) 29 | return 1; 30 | 31 | if (__sync_lock_test_and_set(&val, 4040) != 3030) 32 | return 1; 33 | 34 | mem = &tmp; 35 | 36 | if (__sync_val_compare_and_swap(&mem, &tmp, &val) != &tmp) 37 | return 1; 38 | 39 | __sync_synchronize(); 40 | 41 | if (mem != &val) 42 | return 1; 43 | 44 | return 0; 45 | ]])], [ap_cv_atomic_builtins=yes], [ap_cv_atomic_builtins=no])]) 46 | 47 | if test "$ap_cv_atomic_builtins" = "yes"; then 48 | AC_DEFINE(HAVE_ATOMIC_BUILTINS, 1, [Define if compiler provides atomic builtins]) 49 | fi 50 | 51 | ]) 52 | -------------------------------------------------------------------------------- /arraylist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. 3 | * Michael Clark 4 | * 5 | * This library is free software; you can redistribute it and/or modify 6 | * it under the terms of the MIT license. See COPYING for details. 7 | * 8 | */ 9 | 10 | #ifndef _fj_arraylist_h_ 11 | #define _fj_arraylist_h_ 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | #define ARRAY_LIST_DEFAULT_SIZE 32 18 | 19 | typedef void (array_list_free_fn) (void *data); 20 | 21 | struct array_list 22 | { 23 | void **array; 24 | int length; 25 | int size; 26 | array_list_free_fn *free_fn; 27 | }; 28 | 29 | extern struct array_list* 30 | array_list_new(array_list_free_fn *free_fn); 31 | 32 | extern void 33 | array_list_free(struct array_list *al); 34 | 35 | extern void* 36 | array_list_get_idx(struct array_list *al, int i); 37 | 38 | extern int 39 | array_list_put_idx(struct array_list *al, int i, void *data); 40 | 41 | extern int 42 | array_list_add(struct array_list *al, void *data); 43 | 44 | extern void 45 | array_list_del_idx(struct array_list *const arr, const int idx); 46 | 47 | extern int 48 | array_list_length(struct array_list *al); 49 | 50 | extern void 51 | array_list_sort(struct array_list *arr, int(*compar)(const void *, const void *)); 52 | 53 | extern void* array_list_bsearch(const void **key, 54 | struct array_list *arr, 55 | int (*sort_fn)(const void *, const void *)); 56 | 57 | 58 | #ifdef __cplusplus 59 | } 60 | #endif 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /json_util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. 3 | * Michael Clark 4 | * 5 | * This library is free software; you can redistribute it and/or modify 6 | * it under the terms of the MIT license. See COPYING for details. 7 | * 8 | */ 9 | 10 | #ifndef _fj_json_util_h_ 11 | #define _fj_json_util_h_ 12 | 13 | #include "json_object.h" 14 | 15 | #ifndef fjson_min 16 | #define fjson_min(a,b) ((a) < (b) ? (a) : (b)) 17 | #endif 18 | 19 | #ifndef fjson_max 20 | #define fjson_max(a,b) ((a) > (b) ? (a) : (b)) 21 | #endif 22 | 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | #define FJSON_FILE_BUF_SIZE 4096 29 | 30 | /* utility functions */ 31 | extern struct fjson_object* fjson_object_from_file(const char *filename); 32 | extern struct fjson_object* fjson_object_from_fd(int fd); 33 | extern int fjson_object_to_file(const char *filename, struct fjson_object *obj); 34 | extern int fjson_object_to_file_ext(const char *filename, struct fjson_object *obj, int flags); 35 | extern int fjson_parse_int64(const char *buf, int64_t *retval); 36 | extern int fjson_parse_double(const char *buf, double *retval); 37 | 38 | /** 39 | * Return a string describing the type of the object. 40 | * e.g. "int", or "object", etc... 41 | */ 42 | extern const char *fjson_type_to_name(enum fjson_type o_type); 43 | 44 | #ifndef FJSON_NATIVE_API_ONLY 45 | #define json_type_to_name fjson_type_to_name 46 | #endif 47 | 48 | #ifdef __cplusplus 49 | } 50 | #endif 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /m4/atomic_operations_64bit.m4: -------------------------------------------------------------------------------- 1 | # rsyslog 2 | # 3 | # atomic_operations.m4 - autoconf macro to check if compiler supports atomic 4 | # operations 5 | # 6 | # rgerhards, 2008-09-18, added based on 7 | # http://svn.apache.org/repos/asf/apr/apr/trunk/configure.in 8 | # 9 | # 10 | AC_DEFUN([RS_ATOMIC_OPERATIONS_64BIT], 11 | [AC_CACHE_CHECK([whether the compiler provides atomic builtins for 64 bit data types], [ap_cv_atomic_builtins_64], 12 | [AC_LINK_IFELSE([AC_LANG_PROGRAM([], [[ 13 | unsigned long long val = 1010, tmp, *mem = &val; 14 | 15 | if (__sync_fetch_and_add(&val, 1010) != 1010 || val != 2020) 16 | return 1; 17 | 18 | tmp = val; 19 | 20 | if (__sync_fetch_and_sub(mem, 1010) != tmp || val != 1010) 21 | return 1; 22 | 23 | if (__sync_sub_and_fetch(&val, 1010) != 0 || val != 0) 24 | return 1; 25 | 26 | tmp = 3030; 27 | 28 | if (__sync_val_compare_and_swap(mem, 0, tmp) != 0 || val != tmp) 29 | return 1; 30 | 31 | if (__sync_lock_test_and_set(&val, 4040) != 3030) 32 | return 1; 33 | 34 | mem = &tmp; 35 | 36 | if (__sync_val_compare_and_swap(&mem, &tmp, &val) != &tmp) 37 | return 1; 38 | 39 | __sync_synchronize(); 40 | 41 | if (mem != &val) 42 | return 1; 43 | 44 | return 0; 45 | ]])], [ap_cv_atomic_builtins_64=yes], [ap_cv_atomic_builtins_64=no])]) 46 | 47 | if test "$ap_cv_atomic_builtins_64" = "yes"; then 48 | AC_DEFINE(HAVE_ATOMIC_BUILTINS64, 1, [Define if compiler provides 64 bit atomic builtins]) 49 | fi 50 | 51 | ]) 52 | -------------------------------------------------------------------------------- /README.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JSON-C - A JSON implementation in C 5 | 6 | 7 | 8 |

JSON-C - A JSON implementation in C

9 | 10 |

Overview

11 |

JSON-C implements a reference counting object model that allows you to easily 12 | construct JSON objects in C, output them as JSON formatted strings and parse 13 | JSON formatted strings back into the C representation of JSON objects.

14 | 15 |

Building

16 |

To setup JSON-C to build on your system please run configure and make.

17 | 18 |

Documentation

19 |

Doxygen generated documentation exists here.

20 | 21 |

GIT Reposository

22 |

git clone https://github.com/json-c/json-c.git

23 | 24 |

Mailing List

25 | Send email to json-c <at> googlegroups <dot> com

26 | 27 |

License

28 |

This program is free software; you can redistribute it and/or modify it under the terms of the MIT License..

29 |
30 | 31 | 32 | -------------------------------------------------------------------------------- /tests/test4.c: -------------------------------------------------------------------------------- 1 | /* 2 | * gcc -o utf8 utf8.c -I/home/y/include -L./.libs -ljson 3 | */ 4 | 5 | #include "config.h" 6 | #include 7 | #include 8 | 9 | /* this is a work-around until we manage to fix configure.ac */ 10 | #pragma GCC diagnostic ignored "-Wdeclaration-after-statement" 11 | 12 | #define DEBUG_SEED(s) 13 | 14 | #include "../json_object.h" 15 | #include "../json_tokener.h" 16 | 17 | static void print_hex( const char* s) 18 | { 19 | const char *iter = s; 20 | unsigned char ch; 21 | while ((ch = *iter++) != 0) 22 | { 23 | if( ',' != ch) 24 | printf("%x ", ch); 25 | else 26 | printf( ","); 27 | } 28 | printf("\n"); 29 | } 30 | 31 | int main(int __attribute__((unused)) argc, char __attribute__((unused)) **argv) 32 | { 33 | const char *input = "\"\\ud840\\udd26,\\ud840\\udd27,\\ud800\\udd26,\\ud800\\udd27\""; 34 | const char *expected = "\xF0\xA0\x84\xA6,\xF0\xA0\x84\xA7,\xF0\x90\x84\xA6,\xF0\x90\x84\xA7"; 35 | struct fjson_object *parse_result = fjson_tokener_parse((char*)input); 36 | const char *unjson = fjson_object_get_string(parse_result); 37 | 38 | printf("input: %s\n", input); 39 | 40 | int strings_match = !strcmp( expected, unjson); 41 | int retval = 0; 42 | if (strings_match) 43 | { 44 | printf("JSON parse result is correct: %s\n", unjson); 45 | printf("PASS\n"); 46 | } else { 47 | printf("JSON parse result doesn't match expected string\n"); 48 | printf("expected string bytes: "); 49 | print_hex( expected); 50 | printf("parsed string bytes: "); 51 | print_hex( unjson); 52 | printf("FAIL\n"); 53 | retval = 1; 54 | } 55 | fjson_object_put(parse_result); 56 | return retval; 57 | } 58 | -------------------------------------------------------------------------------- /tests/test_null.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Tests if binary strings are supported. 3 | */ 4 | 5 | #include "config.h" 6 | #include 7 | #include 8 | 9 | #include "json_inttypes.h" 10 | #include "json_object.h" 11 | #include "json_tokener.h" 12 | 13 | int main() 14 | { 15 | // this test has a space after the null character. check that it's still included 16 | const char *input = " \0 "; 17 | const char *expected = "\" \\u0000 \""; 18 | struct fjson_object *string = fjson_object_new_string_len(input, 3); 19 | const char *json = fjson_object_to_json_string(string); 20 | 21 | int strings_match = !strcmp( expected, json); 22 | int retval = 0; 23 | if (strings_match) 24 | { 25 | printf("JSON write result is correct: %s\n", json); 26 | printf("PASS\n"); 27 | } else { 28 | printf("JSON write result doesn't match expected string\n"); 29 | printf("expected string: "); 30 | printf("%s\n", expected); 31 | printf("parsed string: "); 32 | printf("%s\n", json); 33 | printf("FAIL\n"); 34 | retval=1; 35 | } 36 | fjson_object_put(string); 37 | 38 | struct fjson_object *parsed_str = fjson_tokener_parse(expected); 39 | if (parsed_str) 40 | { 41 | int parsed_len = fjson_object_get_string_len(parsed_str); 42 | const char *parsed_cstr = fjson_object_get_string(parsed_str); 43 | int ii; 44 | printf("Re-parsed object string len=%d, chars=[", parsed_len); 45 | for (ii = 0; ii < parsed_len ; ii++) 46 | { 47 | printf("%s%d", (ii ? ", " : ""), (int)parsed_cstr[ii]); 48 | } 49 | printf("]\n"); 50 | fjson_object_put(parsed_str); 51 | } 52 | else 53 | { 54 | printf("ERROR: failed to parse\n"); 55 | } 56 | return retval; 57 | } 58 | -------------------------------------------------------------------------------- /debug.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. 3 | * Michael Clark 4 | * Copyright (c) 2016 Adiscon GmbH 5 | * Rainer Gerhards 6 | * 7 | * This library is free software; you can redistribute it and/or modify 8 | * it under the terms of the MIT license. See COPYING for details. 9 | * 10 | */ 11 | 12 | #include "config.h" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #if HAVE_SYSLOG_H 20 | # include 21 | #endif /* HAVE_SYSLOG_H */ 22 | 23 | #if HAVE_UNISTD_H 24 | # include 25 | #endif /* HAVE_UNISTD_H */ 26 | 27 | #if HAVE_SYS_PARAM_H 28 | #include 29 | #endif /* HAVE_SYS_PARAM_H */ 30 | 31 | #include "debug.h" 32 | 33 | static int _syslog = 0; 34 | static int _debug = 0; 35 | 36 | void mc_set_debug(int debug) { _debug = debug; } 37 | int mc_get_debug(void) { return _debug; } 38 | 39 | extern void mc_set_syslog(int use_syslog) 40 | { 41 | _syslog = use_syslog; 42 | } 43 | 44 | void mc_debug(const char *msg, ...) 45 | { 46 | va_list ap; 47 | if(_debug) { 48 | va_start(ap, msg); 49 | #if HAVE_VSYSLOG 50 | if(_syslog) { 51 | vsyslog(LOG_DEBUG, msg, ap); 52 | } else 53 | #endif 54 | vprintf(msg, ap); 55 | va_end(ap); 56 | } 57 | } 58 | 59 | void mc_error(const char *msg, ...) 60 | { 61 | va_list ap; 62 | va_start(ap, msg); 63 | #if HAVE_VSYSLOG 64 | if(_syslog) { 65 | vsyslog(LOG_ERR, msg, ap); 66 | } else 67 | #endif 68 | vfprintf(stderr, msg, ap); 69 | va_end(ap); 70 | } 71 | 72 | void mc_info(const char *msg, ...) 73 | { 74 | va_list ap; 75 | va_start(ap, msg); 76 | #if HAVE_VSYSLOG 77 | if(_syslog) { 78 | vsyslog(LOG_INFO, msg, ap); 79 | } else 80 | #endif 81 | vfprintf(stderr, msg, ap); 82 | va_end(ap); 83 | } 84 | -------------------------------------------------------------------------------- /tests/test_obj_iter-del.c: -------------------------------------------------------------------------------- 1 | /* libfastjson testbench tool 2 | * 3 | * Copyright (c) 2016 Adiscon GmbH 4 | * Rainer Gerhards 5 | * 6 | * This library is free software; you can redistribute it and/or modify 7 | * it under the terms of the MIT license. See COPYING for details. 8 | * 9 | */ 10 | #include "config.h" 11 | 12 | #include "../json.h" 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | /* this is a work-around until we manage to fix configure.ac */ 19 | #pragma GCC diagnostic ignored "-Wdeclaration-after-statement" 20 | 21 | int main(int __attribute__((unused)) argc, char __attribute__((unused)) **argv) 22 | { 23 | struct fjson_object *my_object = fjson_object_new_object(); 24 | if (my_object == NULL) { 25 | perror("malloc ptr table failed:"); 26 | exit(1); 27 | } 28 | 29 | /* add some keys */ 30 | fjson_object_object_add_ex (my_object, "a", fjson_object_new_string("a"), 0); 31 | fjson_object_object_add_ex (my_object, "b", fjson_object_new_string("b"), 0); 32 | fjson_object_object_add_ex (my_object, "c", fjson_object_new_string("c"), 0); 33 | fjson_object_object_add_ex (my_object, "d", fjson_object_new_string("d"), 0); 34 | 35 | /* delete some keys */ 36 | fjson_object_object_del (my_object, "a"); 37 | fjson_object_object_del (my_object, "c"); 38 | 39 | /* check that iteration properly skips the deleted keys */ 40 | struct fjson_object_iterator it = fjson_object_iter_begin(my_object); 41 | struct fjson_object_iterator itEnd = fjson_object_iter_end(my_object); 42 | while (!fjson_object_iter_equal (&it, &itEnd)) { 43 | printf("%s: %s\n", 44 | fjson_object_iter_peek_name (&it), 45 | fjson_object_to_json_string (fjson_object_iter_peek_value(&it))); 46 | fjson_object_iter_next (&it); 47 | } 48 | 49 | fjson_object_put (my_object); 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /debug.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. 3 | * Michael Clark 4 | * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. 5 | * Copyright (c) 2016 Adiscon GmbH 6 | * Rainer Gerhards 7 | * 8 | * This library is free software; you can redistribute it and/or modify 9 | * it under the terms of the MIT license. See COPYING for details. 10 | * 11 | */ 12 | 13 | #ifndef _FJ_DEBUG_H_ 14 | #define _FJ_DEBUG_H_ 15 | 16 | #include 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | extern void mc_set_debug(int debug); 23 | extern int mc_get_debug(void); 24 | 25 | extern void mc_set_syslog(int syslog); 26 | 27 | extern void mc_debug(const char *msg, ...) __attribute__((format(printf, 1, 2))); 28 | extern void mc_error(const char *msg, ...) __attribute__((format(printf, 1, 2))); 29 | extern void mc_info(const char *msg, ...) __attribute__((format(printf, 1, 2))); 30 | 31 | #ifndef __STRING 32 | #define __STRING(x) #x 33 | #endif 34 | 35 | #ifndef PARSER_BROKEN_FIXED 36 | 37 | #define JASSERT(cond) do {} while(0) 38 | 39 | #else 40 | 41 | #define JASSERT(cond) do { \ 42 | if (!(cond)) { \ 43 | mc_error("cjson assert failure %s:%d : cond \"" __STRING(cond) "failed\n", __FILE__, __LINE__); \ 44 | *(int *)0 = 1;\ 45 | abort(); \ 46 | }\ 47 | } while(0) 48 | 49 | #endif 50 | 51 | #define MC_ERROR(x, ...) mc_error(x, ##__VA_ARGS__) 52 | 53 | #ifdef MC_MAINTAINER_MODE 54 | #define MC_SET_DEBUG(x) mc_set_debug(x) 55 | #define MC_GET_DEBUG() mc_get_debug() 56 | #define MC_SET_SYSLOG(x) mc_set_syslog(x) 57 | #define MC_DEBUG(x, ...) mc_debug(x, ##__VA_ARGS__) 58 | #define MC_INFO(x, ...) mc_info(x, ##__VA_ARGS__) 59 | #else 60 | #define MC_SET_DEBUG(x) if (0) mc_set_debug(x) 61 | #define MC_GET_DEBUG() (0) 62 | #define MC_SET_SYSLOG(x) if (0) mc_set_syslog(x) 63 | #define MC_DEBUG(x, ...) if (0) mc_debug(x, ##__VA_ARGS__) 64 | #define MC_INFO(x, ...) if (0) mc_info(x, ##__VA_ARGS__) 65 | #endif 66 | 67 | #ifdef __cplusplus 68 | } 69 | #endif 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | libfastjson 2 | =========== 3 | **NOTE: libfastjson is a fork from json-c, and is currently under development.** 4 | 5 | The aim of this project is **not** to provide a slightly modified clone 6 | of json-c. It's aim is to provide 7 | 8 | * a **small** library with essential json handling functions 9 | * sufficiently good json support (not 100% standards compliant) 10 | * be very fast in processing 11 | 12 | In order to reach these goals, we reduce the features of json-c. For 13 | similarities and differences, see the file DIFFERENCES. 14 | 15 | **IMPORTANT** 16 | The current API is **not** stable and will change until version 1.0.0 is 17 | reached. We plan to reach it by summer 2016 at latest. With 1.0.0, the API 18 | will be stable. Until then, everything may change. Of course, we will not 19 | deliberatly break things but we need freedom to restructure. 20 | 21 | 22 | Building on Unix with `git`, `gcc` and `autotools` 23 | -------------------------------------------------- 24 | 25 | Prerequisites: 26 | 27 | - `gcc`, `clang`, or another C compiler 28 | - `libtool` 29 | 30 | If you're not using a release tarball, you'll also need: 31 | 32 | - `autoconf` (`autoreconf`) 33 | - `automake` 34 | 35 | Make sure you have a complete `libtool` install, including `libtoolize`. 36 | 37 | `libfastjson` GitHub repo: https://github.com/rsyslog/libfastjson 38 | 39 | ```bash 40 | $ git clone https://github.com/rsyslog/libfastjson.git 41 | $ cd libfastjson 42 | $ sh autogen.sh 43 | ``` 44 | 45 | followed by 46 | 47 | ```bash 48 | $ ./configure 49 | $ make 50 | $ make install 51 | ``` 52 | 53 | To build and run the test programs: 54 | 55 | ```bash 56 | $ make check 57 | ``` 58 | 59 | Linking to `libfastjson` 60 | --------------------------- 61 | 62 | If your system has `pkgconfig`, 63 | then you can just add this to your `makefile`: 64 | 65 | ```make 66 | CFLAGS += $(shell pkg-config --cflags libfastjson) 67 | LDFLAGS += $(shell pkg-config --libs libfastjson) 68 | ``` 69 | 70 | Without `pkgconfig`, you would do something like this: 71 | 72 | ```make 73 | LIBFASTJSON_DIR=/path/to/json_c/install 74 | CFLAGS += -I$(LIBFASTJSON_DIR)/include/libfastjson 75 | LDFLAGS+= -L$(LIBFASTJSON_DIR)/lib -lfastjson 76 | ``` 77 | -------------------------------------------------------------------------------- /tests/test_object_object_add_ex.c: -------------------------------------------------------------------------------- 1 | /* libfastjson testbench tool 2 | * 3 | * Copyright (c) 2016 Adiscon GmbH 4 | * Rainer Gerhards 5 | * 6 | * This library is free software; you can redistribute it and/or modify 7 | * it under the terms of the MIT license. See COPYING for details. 8 | * 9 | */ 10 | #include "config.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "../json.h" 18 | #include "../debug.h" 19 | #include "parse_flags.h" 20 | 21 | /* this is a work-around until we manage to fix configure.ac */ 22 | #pragma GCC diagnostic ignored "-Wdeclaration-after-statement" 23 | 24 | #define DEBUG_SEED(s) 25 | 26 | #ifdef TEST_FORMATTED 27 | #define fjson_object_to_json_string(obj) fjson_object_to_json_string_ext(obj,sflags) 28 | #else 29 | /* no special define */ 30 | #endif 31 | 32 | int main(int __attribute__((unused)) argc, char __attribute__((unused)) **argv) 33 | { 34 | fjson_object *my_object; 35 | #ifdef TEST_FORMATTED 36 | int sflags = 0; 37 | #endif 38 | 39 | MC_SET_DEBUG(1); 40 | 41 | #ifdef TEST_FORMATTED 42 | sflags = parse_flags(argc, argv); 43 | #endif 44 | 45 | my_object = fjson_object_new_object(); 46 | fjson_object_object_add_ex(my_object, "abc", fjson_object_new_int(12), 0); 47 | fjson_object_object_add_ex(my_object, "foo", fjson_object_new_string("bar"), 48 | FJSON_OBJECT_ADD_KEY_IS_NEW); 49 | fjson_object_object_add_ex(my_object, "bool0", fjson_object_new_boolean(0), 50 | FJSON_OBJECT_KEY_IS_CONSTANT); 51 | fjson_object_object_add_ex(my_object, "bool1", fjson_object_new_boolean(1), 52 | FJSON_OBJECT_ADD_KEY_IS_NEW | FJSON_OBJECT_KEY_IS_CONSTANT); 53 | 54 | printf("my_object=\n"); 55 | struct fjson_object_iterator it = fjson_object_iter_begin(my_object); 56 | struct fjson_object_iterator itEnd = fjson_object_iter_end(my_object); 57 | while (!fjson_object_iter_equal(&it, &itEnd)) { 58 | printf("\t%s: %s\n", 59 | fjson_object_iter_peek_name(&it), 60 | fjson_object_to_json_string(fjson_object_iter_peek_value(&it))); 61 | fjson_object_iter_next(&it); 62 | } 63 | 64 | printf("my_object.to_string()=%s\n", fjson_object_to_json_string(my_object)); 65 | 66 | fjson_object_put(my_object); 67 | 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /DIFFERENCES: -------------------------------------------------------------------------------- 1 | Compatibility Layer 2 | libfastjson offers some limited source code compatiblity to 3 | json-c via preprocessor macros. They are currently primarily 4 | targeted at what the rsyslog family of projects need. 5 | Also, they do not work work properly with ./configure checks. 6 | This is due to the way autotools checks for functions. 7 | 8 | Differences to json-c 9 | --------------------- 10 | 11 | * hash tables are no longer part of the API; we may use them 12 | internally, but we may also use any other data structure. This 13 | is not of concern for the caller. 14 | 15 | * we removed the json_object_object_foreach[C] macros, which 16 | required the caller to known library implementation details. 17 | The same functionality is provided in a clean way via 18 | fjson_object_iter_*() 19 | 20 | * we do NOT handle NUL characters inside strings or names 21 | At the time of fork, json-c did not properly handle this, but 22 | could at least, with tricks, generate outbound json with NUL 23 | in string values. We do not support this. 24 | This also means libfastjson is not 100% JSON compliant. Sorry 25 | for that, but we try to keep things working great in the C 26 | spirit, and that JSON feature simply doesn't play well here. 27 | If you need 100% JSON compliance, look for another library. 28 | 29 | * Windows and Android are not supported. If you want to actively 30 | maintain libfastjson on those platforms, please let us know 31 | and we can work together (a long term commitment) to get this 32 | going again. 33 | 34 | * removed API calls: 35 | If you need any of this API calls urgently, please let us know 36 | why you consider it important enough to become re-added; 37 | especially let us know why you think this is important to the 38 | community at large. Thanks. 39 | * [f]json_c_version_num() 40 | * [f]json_object_get_object() 41 | It returns a hash table, which exposes an implementation detail 42 | that a caller should never know about. Most often, this can cleanly 43 | be replaced by the regular json_object_iter_* API. 44 | 45 | * renamed API calls: 46 | * all calls have been added an "f" in the front 47 | json_* --> fjson_* 48 | * the same holds true for preprocessor defines, 49 | JSON_C_* --> FJSON_* (note removal of "_C") 50 | * json_c_version() -> fjson_version() 51 | -------------------------------------------------------------------------------- /json_object_private.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. 3 | * Michael Clark 4 | * Copyright (c) 2015 Rainer Gerhards 5 | * 6 | * This library is free software; you can redistribute it and/or modify 7 | * it under the terms of the MIT license. See COPYING for details. 8 | * 9 | */ 10 | 11 | #ifndef _fj_json_object_private_h_ 12 | #define _fj_json_object_private_h_ 13 | 14 | #include "atomic.h" 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | /* define a couple of attributes to improve cross-platform builds */ 21 | #if __GNUC__ > 6 22 | #define ATTR_FALLTHROUGH __attribute__((fallthrough)); 23 | #else 24 | #define ATTR_FALLTHROUGH 25 | #endif 26 | 27 | #define LEN_DIRECT_STRING_DATA 32 /**< how many bytes are directly stored in fjson_object for strings? */ 28 | 29 | /** 30 | * Type of the delete and serialization functions. 31 | */ 32 | typedef void (fjson_object_private_delete_fn)(struct fjson_object *o); 33 | typedef int (fjson_object_to_json_string_fn)(struct fjson_object *jso, 34 | struct printbuf *pb, 35 | int level, 36 | int flags); 37 | 38 | struct _fjson_child { 39 | /** 40 | * The key. 41 | */ 42 | const char *k; 43 | int k_is_constant; 44 | struct { 45 | unsigned k_is_constant : 1; 46 | } flags; 47 | /** 48 | * The value. 49 | */ 50 | struct fjson_object *v; 51 | }; 52 | 53 | struct _fjson_child_pg { 54 | struct _fjson_child children[FJSON_OBJECT_CHLD_PG_SIZE]; 55 | struct _fjson_child_pg *next; 56 | }; 57 | 58 | struct fjson_object 59 | { 60 | enum fjson_type o_type; 61 | fjson_object_private_delete_fn *_delete; 62 | fjson_object_to_json_string_fn *_to_json_string; 63 | int _ref_count; 64 | struct printbuf *_pb; 65 | union data { 66 | fjson_bool c_boolean; 67 | struct { 68 | double value; 69 | char *source; 70 | } c_double; 71 | int64_t c_int64; 72 | struct { 73 | int nelem; 74 | int ndeleted; 75 | struct _fjson_child_pg pg; 76 | struct _fjson_child_pg *lastpg; 77 | } c_obj; 78 | struct array_list *c_array; 79 | struct { 80 | union { 81 | /* optimize: if we have small strings, we can store them 82 | * directly. This saves considerable CPU cycles AND memory. 83 | */ 84 | char *ptr; 85 | char data[LEN_DIRECT_STRING_DATA]; 86 | } str; 87 | int len; 88 | } c_string; 89 | } o; 90 | DEF_ATOMIC_HELPER_MUT(_mut_ref_count) 91 | }; 92 | 93 | #ifdef __cplusplus 94 | } 95 | #endif 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /tests/test_many_subobj.c: -------------------------------------------------------------------------------- 1 | /* libfastjson testbench tool 2 | * 3 | * Copyright (c) 2016 Adiscon GmbH 4 | * Rainer Gerhards 5 | * 6 | * This library is free software; you can redistribute it and/or modify 7 | * it under the terms of the MIT license. See COPYING for details. 8 | * 9 | */ 10 | #include "config.h" 11 | 12 | #include "../json.h" 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #define NUM_SUBOBJ 200 19 | #define NUM_SUBOBJ_HALF (NUM_SUBOBJ/2) 20 | #define NUM_SUBOBJ_QUARTER (NUM_SUBOBJ/4) 21 | 22 | int main(int __attribute__((unused)) argc, char __attribute__((unused)) **argv) 23 | { 24 | int i; 25 | char pb[64]; 26 | struct fjson_object *json = fjson_object_new_object(); 27 | if (json == NULL) { 28 | perror("malloc ptr table failed:"); 29 | exit(1); 30 | } 31 | 32 | /* add some keys */ 33 | for (i = 0 ; i < NUM_SUBOBJ ; ++i) { 34 | snprintf(pb, sizeof(pb), "key-%d", i); 35 | fjson_object_object_add_ex(json, pb, fjson_object_new_int(i), 0); 36 | 37 | } 38 | printf("STEP1: %s\n", fjson_object_to_json_string(json)); 39 | 40 | /* delete some keys */ 41 | for (i = NUM_SUBOBJ_HALF - NUM_SUBOBJ_QUARTER ; 42 | i < NUM_SUBOBJ_HALF + NUM_SUBOBJ_QUARTER ; 43 | ++i) { 44 | snprintf(pb, sizeof(pb), "key-%d", i); 45 | fjson_object_object_del(json, pb); 46 | } 47 | printf("STEP2: %s\n", fjson_object_to_json_string(json)); 48 | 49 | /* add new keys */ 50 | for (i = NUM_SUBOBJ_HALF + NUM_SUBOBJ_QUARTER - 1; 51 | i >= NUM_SUBOBJ_HALF - NUM_SUBOBJ_QUARTER ; 52 | --i) { 53 | snprintf(pb, sizeof(pb), "KEY-%d", i); 54 | fjson_object_object_add_ex(json, pb, fjson_object_new_int(i), 0); 55 | } 56 | printf("STEP3: %s\n", fjson_object_to_json_string(json)); 57 | 58 | /* delete the new keys again, and also update key values */ 59 | for (i = NUM_SUBOBJ_HALF - NUM_SUBOBJ_QUARTER ; 60 | i < NUM_SUBOBJ_HALF + NUM_SUBOBJ_QUARTER ; 61 | ++i) { 62 | snprintf(pb, sizeof(pb), "KEY-%d", i); 63 | fjson_object_object_del(json, pb); 64 | } 65 | for (i = 0 ; i < NUM_SUBOBJ ; ++i) { 66 | snprintf(pb, sizeof(pb), "key-%d", i); 67 | fjson_object_object_add_ex(json, pb, fjson_object_new_int(i*10), 0); 68 | 69 | } 70 | printf("STEP4: %s\n", fjson_object_to_json_string(json)); 71 | 72 | /* add one more key to see that adding works when extending the array */ 73 | snprintf(pb, sizeof(pb), "key-%d", NUM_SUBOBJ); 74 | fjson_object_object_add(json, pb, fjson_object_new_int(NUM_SUBOBJ)); 75 | printf("STEP5:%s\n", fjson_object_to_json_string(json)); 76 | 77 | fjson_object_put(json); 78 | return 0; 79 | } 80 | -------------------------------------------------------------------------------- /tests/test_cast.expected: -------------------------------------------------------------------------------- 1 | Parsed input: { 2 | "string_of_digits": "123", 3 | "regular_number": 222, 4 | "decimal_number": 99.55, 5 | "boolean_true": true, 6 | "boolean_false": false, 7 | "big_number": 2147483649, 8 | "a_null": null, 9 | } 10 | Result is not NULL 11 | new_obj.string_of_digits fjson_object_get_type()=string 12 | new_obj.string_of_digits fjson_object_get_int()=123 13 | new_obj.string_of_digits fjson_object_get_int64()=123 14 | new_obj.string_of_digits fjson_object_get_boolean()=1 15 | new_obj.string_of_digits fjson_object_get_double()=123.000000 16 | new_obj.regular_number fjson_object_get_type()=int 17 | new_obj.regular_number fjson_object_get_int()=222 18 | new_obj.regular_number fjson_object_get_int64()=222 19 | new_obj.regular_number fjson_object_get_boolean()=1 20 | new_obj.regular_number fjson_object_get_double()=222.000000 21 | new_obj.decimal_number fjson_object_get_type()=double 22 | new_obj.decimal_number fjson_object_get_int()=99 23 | new_obj.decimal_number fjson_object_get_int64()=99 24 | new_obj.decimal_number fjson_object_get_boolean()=1 25 | new_obj.decimal_number fjson_object_get_double()=99.550000 26 | new_obj.boolean_true fjson_object_get_type()=boolean 27 | new_obj.boolean_true fjson_object_get_int()=1 28 | new_obj.boolean_true fjson_object_get_int64()=1 29 | new_obj.boolean_true fjson_object_get_boolean()=1 30 | new_obj.boolean_true fjson_object_get_double()=1.000000 31 | new_obj.boolean_false fjson_object_get_type()=boolean 32 | new_obj.boolean_false fjson_object_get_int()=0 33 | new_obj.boolean_false fjson_object_get_int64()=0 34 | new_obj.boolean_false fjson_object_get_boolean()=0 35 | new_obj.boolean_false fjson_object_get_double()=0.000000 36 | new_obj.big_number fjson_object_get_type()=int 37 | new_obj.big_number fjson_object_get_int()=2147483647 38 | new_obj.big_number fjson_object_get_int64()=2147483649 39 | new_obj.big_number fjson_object_get_boolean()=1 40 | new_obj.big_number fjson_object_get_double()=2147483649.000000 41 | new_obj.a_null fjson_object_get_type()=null 42 | new_obj.a_null fjson_object_get_int()=0 43 | new_obj.a_null fjson_object_get_int64()=0 44 | new_obj.a_null fjson_object_get_boolean()=0 45 | new_obj.a_null fjson_object_get_double()=0.000000 46 | 47 | ================================ 48 | fjson_object_is_type: null,boolean,double,int,object,array,string 49 | new_obj : 0,0,0,0,1,0,0 50 | new_obj.string_of_digits : 0,0,0,0,0,0,1 51 | new_obj.regular_number : 0,0,0,1,0,0,0 52 | new_obj.decimal_number : 0,0,1,0,0,0,0 53 | new_obj.boolean_true : 0,1,0,0,0,0,0 54 | new_obj.boolean_false : 0,1,0,0,0,0,0 55 | new_obj.big_number : 0,0,0,1,0,0,0 56 | new_obj.a_null : 1,0,0,0,0,0,0 57 | -------------------------------------------------------------------------------- /tests/test_parse_int64.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #include "../json_util.h" 7 | 8 | static void checkit(const char *buf) 9 | { 10 | int64_t cint64 = -666; 11 | 12 | int retval = fjson_parse_int64(buf, &cint64); 13 | printf("buf=%s parseit=%d, value=%" PRId64 " \n", buf, retval, cint64); 14 | } 15 | 16 | /** 17 | * This test calls fjson_parse_int64 with a variety of different strings. 18 | * It's purpose is to ensure that the results are consistent across all 19 | * different environments that it might be executed in. 20 | * 21 | * This always exits with a 0 exit value. The output should be compared 22 | * against previously saved expected output. 23 | */ 24 | int main(int __attribute__((unused)) argc, char __attribute__((unused)) **argv) 25 | { 26 | char buf[100]; 27 | 28 | checkit("x"); 29 | 30 | checkit("0"); 31 | checkit("-0"); 32 | 33 | checkit("00000000"); 34 | checkit("-00000000"); 35 | 36 | checkit("1"); 37 | 38 | strcpy(buf, "2147483647"); // aka INT32_MAX 39 | checkit(buf); 40 | 41 | strcpy(buf, "-1"); 42 | checkit(buf); 43 | 44 | strcpy(buf, " -1"); 45 | checkit(buf); 46 | 47 | strcpy(buf, "00001234"); 48 | checkit(buf); 49 | 50 | strcpy(buf, "0001234x"); 51 | checkit(buf); 52 | 53 | strcpy(buf, "-00001234"); 54 | checkit(buf); 55 | 56 | strcpy(buf, "-00001234x"); 57 | checkit(buf); 58 | 59 | strcpy(buf, "4294967295"); // aka UINT32_MAX 60 | 61 | sprintf(buf, "4294967296"); // aka UINT32_MAX + 1 62 | 63 | strcpy(buf, "21474836470"); // INT32_MAX * 10 64 | checkit(buf); 65 | 66 | strcpy(buf, "31474836470"); // INT32_MAX * 10 + a bunch 67 | checkit(buf); 68 | 69 | strcpy(buf, "-2147483647"); // INT32_MIN + 1 70 | checkit(buf); 71 | 72 | strcpy(buf, "-2147483648"); // INT32_MIN 73 | checkit(buf); 74 | 75 | strcpy(buf, "-2147483649"); // INT32_MIN - 1 76 | checkit(buf); 77 | 78 | strcpy(buf, "-21474836480"); // INT32_MIN * 10 79 | checkit(buf); 80 | 81 | strcpy(buf, "9223372036854775806"); // INT64_MAX - 1 82 | checkit(buf); 83 | 84 | strcpy(buf, "9223372036854775807"); // INT64_MAX 85 | checkit(buf); 86 | 87 | strcpy(buf, "9223372036854775808"); // INT64_MAX + 1 88 | checkit(buf); 89 | 90 | strcpy(buf, "-9223372036854775808"); // INT64_MIN 91 | checkit(buf); 92 | 93 | strcpy(buf, "-9223372036854775809"); // INT64_MIN - 1 94 | checkit(buf); 95 | 96 | strcpy(buf, "18446744073709551614"); // UINT64_MAX - 1 97 | checkit(buf); 98 | 99 | strcpy(buf, "18446744073709551615"); // UINT64_MAX 100 | checkit(buf); 101 | 102 | // Ensure we can still parse valid numbers after parsing out of range ones. 103 | strcpy(buf, "123"); 104 | checkit(buf); 105 | 106 | return 0; 107 | } 108 | -------------------------------------------------------------------------------- /printbuf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. 3 | * Michael Clark 4 | * 5 | * This library is free software; you can redistribute it and/or modify 6 | * it under the terms of the MIT license. See COPYING for details. 7 | * 8 | * 9 | * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. 10 | * The copyrights to the contents of this file are licensed under the MIT License 11 | * (http://www.opensource.org/licenses/mit-license.php) 12 | */ 13 | 14 | #ifndef _fj_printbuf_h_ 15 | #define _fj_printbuf_h_ 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | struct printbuf { 22 | char *buf; 23 | int bpos; 24 | int size; 25 | }; 26 | 27 | extern struct printbuf* 28 | printbuf_new(void); 29 | 30 | /* As an optimization, printbuf_memappend_fast is defined as a macro 31 | * that handles copying data if the buffer is large enough; otherwise 32 | * it invokes printbuf_memappend_real() which performs the heavy 33 | * lifting of realloc()ing the buffer and copying data. 34 | * Your code should not use printbuf_memappend directly--use 35 | * printbuf_memappend_fast instead. 36 | */ 37 | extern int 38 | printbuf_memappend(struct printbuf *p, const char *buf, int size); 39 | 40 | #define printbuf_memappend_fast(p, bufptr, bufsize) \ 41 | do { \ 42 | if ((p->size - p->bpos) > bufsize) { \ 43 | memcpy(p->buf + p->bpos, (bufptr), bufsize); \ 44 | p->bpos += bufsize; \ 45 | p->buf[p->bpos]= '\0'; \ 46 | } else { printbuf_memappend(p, (bufptr), bufsize); }\ 47 | } while (0) 48 | 49 | /* The following functions provide a printbuf interface where the 50 | * string terminator '\0' is not always written. This is faster, but 51 | * the string cannot be used with standard functions while being 52 | * constructed. To do so, printbuf_terminate_string() must be 53 | * called first. 54 | */ 55 | void printbuf_memappend_no_nul(struct printbuf *p, const char *buf, int size); 56 | void printbuf_memappend_char(struct printbuf *p, const char c); 57 | void printbuf_terminate_string(struct printbuf *const p); 58 | 59 | #define printbuf_length(p) ((p)->bpos) 60 | 61 | /** 62 | * Set len bytes of the buffer to charvalue, starting at offset offset. 63 | * Similar to calling memset(x, charvalue, len); 64 | * 65 | * The memory allocated for the buffer is extended as necessary. 66 | * 67 | * If offset is -1, this starts at the end of the current data in the buffer. 68 | */ 69 | extern int 70 | printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len); 71 | 72 | extern int 73 | sprintbuf(struct printbuf *p, const char *msg, ...) __attribute__((__format__(__printf__, 2, 3))); 74 | 75 | extern void 76 | printbuf_reset(struct printbuf *p); 77 | 78 | extern void 79 | printbuf_free(struct printbuf *p); 80 | #ifdef __cplusplus 81 | } 82 | #endif 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /json.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. 3 | * Michael Clark 4 | * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. 5 | * Copyright (c) 2016 Adiscon GmbH 6 | * Rainer Gerhards 7 | * 8 | * This library is free software; you can redistribute it and/or modify 9 | * it under the terms of the MIT license. See COPYING for details. 10 | * 11 | */ 12 | 13 | #ifndef _fj_json_h_ 14 | #define _fj_json_h_ 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | #include "json_util.h" 21 | #include "json_object.h" 22 | #include "json_tokener.h" 23 | #include "json_object_iterator.h" 24 | 25 | /** 26 | * Set initial size allocation for memory when creating strings, 27 | * as is done for example in fjson_object_to_json_string(). The 28 | * default size is 32, which is very conservative. If an app 29 | * knows it typically deals with larger strings, performance 30 | * can be improved by setting the initial size to a different 31 | * number, e.g. 1k. Note that this also means that memory 32 | * consumption can increase. How far entriely depens on the 33 | * application and its use of json-c. 34 | * 35 | * Note: each time this function is called, the initial size is 36 | * changed to the given value. Already existing elements are not 37 | * affected. This function is usually meant to be called just once 38 | * at start of an application, but there is no harm calling it more 39 | * than once. Note that the function is NOT thread-safe and must not 40 | * be called on different threads concurrently. 41 | * 42 | * @param size new initial size for printbuf (formatting buffer) 43 | */ 44 | extern void fjson_global_set_printbuf_initial_size(int size); 45 | 46 | /** 47 | * Set case sensitive/insensitive comparison mode. If set to 0, 48 | * comparisons for JSON keys will be case-insensitive. Otherwise, 49 | * they will be case-sensitive. 50 | * NOTE: the JSON standard demands case sensitivity. By turning 51 | * this off, the JSON standard is not obeyed. Most importantly, 52 | * if keys exists which only differ in case, only partial data 53 | * access is possible. So use with care and only if you know 54 | * exactly what you are doing! 55 | */ 56 | extern void fjson_global_do_case_sensitive_comparison(const int newval); 57 | 58 | /** 59 | * report the current libfastjson version 60 | */ 61 | extern const char *fjson_version(void); 62 | 63 | /** 64 | * default string hash function 65 | */ 66 | #define FJSON_STR_HASH_DFLT 0 67 | 68 | /** 69 | * perl-like string hash function 70 | */ 71 | #define FJSON_STR_HASH_PERLLIKE 1 72 | 73 | #ifndef FJSON_NATIVE_API_ONLY 74 | #define JSON_C_STR_HASH_PERLLIKE FJSON_STR_HASH_PERLLIKE 75 | #define json_global_set_string_hash(x) /**<< no longer exists nor is needed */ 76 | #define fjson_global_set_string_hash(x) /**<< no longer exists nor is needed */ 77 | #endif 78 | 79 | #ifdef __cplusplus 80 | } 81 | #endif 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /m4/ax_append_flag.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_append_flag.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # FLAG is appended to the FLAGS-VARIABLE shell variable, with a space 12 | # added in between. 13 | # 14 | # If FLAGS-VARIABLE is not specified, the current language's flags (e.g. 15 | # CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains 16 | # FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly 17 | # FLAG. 18 | # 19 | # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. 20 | # 21 | # LICENSE 22 | # 23 | # Copyright (c) 2008 Guido U. Draheim 24 | # Copyright (c) 2011 Maarten Bosmans 25 | # 26 | # This program is free software: you can redistribute it and/or modify it 27 | # under the terms of the GNU General Public License as published by the 28 | # Free Software Foundation, either version 3 of the License, or (at your 29 | # option) any later version. 30 | # 31 | # This program is distributed in the hope that it will be useful, but 32 | # WITHOUT ANY WARRANTY; without even the implied warranty of 33 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 34 | # Public License for more details. 35 | # 36 | # You should have received a copy of the GNU General Public License along 37 | # with this program. If not, see . 38 | # 39 | # As a special exception, the respective Autoconf Macro's copyright owner 40 | # gives unlimited permission to copy, distribute and modify the configure 41 | # scripts that are the output of Autoconf when processing the Macro. You 42 | # need not follow the terms of the GNU General Public License when using 43 | # or distributing such scripts, even though portions of the text of the 44 | # Macro appear in them. The GNU General Public License (GPL) does govern 45 | # all other use of the material that constitutes the Autoconf Macro. 46 | # 47 | # This special exception to the GPL applies to versions of the Autoconf 48 | # Macro released by the Autoconf Archive. When you make and distribute a 49 | # modified version of the Autoconf Macro, you may extend this special 50 | # exception to the GPL to apply to your modified version as well. 51 | 52 | #serial 6 53 | 54 | AC_DEFUN([AX_APPEND_FLAG], 55 | [dnl 56 | AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF 57 | AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])]) 58 | AS_VAR_SET_IF(FLAGS,[ 59 | AS_CASE([" AS_VAR_GET(FLAGS) "], 60 | [*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])], 61 | [ 62 | AS_VAR_APPEND(FLAGS,[" $1"]) 63 | AC_RUN_LOG([: FLAGS="$FLAGS"]) 64 | ]) 65 | ], 66 | [ 67 | AS_VAR_SET(FLAGS,[$1]) 68 | AC_RUN_LOG([: FLAGS="$FLAGS"]) 69 | ]) 70 | AS_VAR_POPDEF([FLAGS])dnl 71 | ])dnl AX_APPEND_FLAG 72 | -------------------------------------------------------------------------------- /m4/ax_append_compile_flags.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_append_compile_flags.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_APPEND_COMPILE_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # For every FLAG1, FLAG2 it is checked whether the compiler works with the 12 | # flag. If it does, the flag is added FLAGS-VARIABLE 13 | # 14 | # If FLAGS-VARIABLE is not specified, the current language's flags (e.g. 15 | # CFLAGS) is used. During the check the flag is always added to the 16 | # current language's flags. 17 | # 18 | # If EXTRA-FLAGS is defined, it is added to the current language's default 19 | # flags (e.g. CFLAGS) when the check is done. The check is thus made with 20 | # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to 21 | # force the compiler to issue an error when a bad flag is given. 22 | # 23 | # NOTE: This macro depends on the AX_APPEND_FLAG and 24 | # AX_CHECK_COMPILE_FLAG. Please keep this macro in sync with 25 | # AX_APPEND_LINK_FLAGS. 26 | # 27 | # LICENSE 28 | # 29 | # Copyright (c) 2011 Maarten Bosmans 30 | # 31 | # This program is free software: you can redistribute it and/or modify it 32 | # under the terms of the GNU General Public License as published by the 33 | # Free Software Foundation, either version 3 of the License, or (at your 34 | # option) any later version. 35 | # 36 | # This program is distributed in the hope that it will be useful, but 37 | # WITHOUT ANY WARRANTY; without even the implied warranty of 38 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 39 | # Public License for more details. 40 | # 41 | # You should have received a copy of the GNU General Public License along 42 | # with this program. If not, see . 43 | # 44 | # As a special exception, the respective Autoconf Macro's copyright owner 45 | # gives unlimited permission to copy, distribute and modify the configure 46 | # scripts that are the output of Autoconf when processing the Macro. You 47 | # need not follow the terms of the GNU General Public License when using 48 | # or distributing such scripts, even though portions of the text of the 49 | # Macro appear in them. The GNU General Public License (GPL) does govern 50 | # all other use of the material that constitutes the Autoconf Macro. 51 | # 52 | # This special exception to the GPL applies to versions of the Autoconf 53 | # Macro released by the Autoconf Archive. When you make and distribute a 54 | # modified version of the Autoconf Macro, you may extend this special 55 | # exception to the GPL to apply to your modified version as well. 56 | 57 | #serial 4 58 | 59 | AC_DEFUN([AX_APPEND_COMPILE_FLAGS], 60 | [AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG]) 61 | AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) 62 | for flag in $1; do 63 | AX_CHECK_COMPILE_FLAG([$flag], [AX_APPEND_FLAG([$flag], [$2])], [], [$3]) 64 | done 65 | ])dnl AX_APPEND_COMPILE_FLAGS 66 | -------------------------------------------------------------------------------- /tests/Makefile.am: -------------------------------------------------------------------------------- 1 | export VALGRIND=@VALGRIND@ 2 | LDADD = $(top_builddir)/libfastjson.la \ 3 | $(top_builddir)/libfastjson-internal.la 4 | 5 | AM_CFLAGS = $(WARN_CFLAGS) 6 | 7 | TESTS= 8 | TESTS+= ucs_copyright_char.test 9 | TESTS+= test_float.test 10 | TESTS+= test1.test 11 | TESTS+= test2.test 12 | TESTS+= test4.test 13 | TESTS+= testReplaceExisting.test 14 | TESTS+= test_parse_int64.test 15 | TESTS+= test_cast.test 16 | TESTS+= test_parse.test 17 | TESTS+= test_locale.test 18 | TESTS+= test_charcase.test 19 | TESTS+= test_printbuf.test 20 | TESTS+= test_obj_iter-del.test 21 | TESTS+= test_object_object_add_ex.test 22 | TESTS+= test_many_subobj.test 23 | TESTS+= test_obj_obj_get_ex-null.test 24 | # we officially do NOT support NUL bytes (however, we may 25 | # later add a workaround to at least transparently pass them 26 | # through, thus I keep this as reference). 27 | #TESTS+= test_null.test 28 | 29 | check_PROGRAMS= 30 | check_PROGRAMS += $(TESTS:.test=) 31 | 32 | # some programs that do internal checking 33 | check_PROGRAMS += chk_version \ 34 | cr_obj_multi 35 | 36 | TESTS += chk_version 37 | 38 | check_PROGRAMS += chk_version \ 39 | cr_obj_multi 40 | 41 | cr_obj_multi_SOURCES = cr_obj_multi.c 42 | chk_version_SOURCES = chk_version.c 43 | 44 | test_printbuf_SOURCES = test_printbuf.c 45 | 46 | # Note: handled by test1.test 47 | check_PROGRAMS += test1Formatted 48 | test1Formatted_SOURCES = test1.c parse_flags.c parse_flags.h 49 | test1Formatted_CPPFLAGS = -DTEST_FORMATTED 50 | 51 | # Note: handled by test2.test 52 | check_PROGRAMS += test2Formatted 53 | test2Formatted_SOURCES = test2.c parse_flags.c parse_flags.h 54 | test2Formatted_CPPFLAGS = -DTEST_FORMATTED 55 | 56 | # Note: handled by object_object_add_ex.test 57 | check_PROGRAMS += test_object_object_add_exFormatted 58 | test_object_object_add_exFormatted_SOURCES = test_object_object_add_ex.c parse_flags.c parse_flags.h 59 | test_object_object_add_exFormatted_CPPFLAGS = -DTEST_FORMATTED 60 | 61 | EXTRA_DIST= 62 | EXTRA_DIST += $(TESTS) 63 | EXTRA_DIST += test-defs.sh 64 | EXTRA_DIST += test1.expected 65 | EXTRA_DIST += test1Formatted_plain.expected 66 | EXTRA_DIST += test1Formatted_pretty.expected 67 | EXTRA_DIST += test1Formatted_spaced.expected 68 | EXTRA_DIST += test2.expected 69 | EXTRA_DIST += test2Formatted_plain.expected 70 | EXTRA_DIST += test2Formatted_pretty.expected 71 | EXTRA_DIST += test2Formatted_spaced.expected 72 | EXTRA_DIST += test4.expected 73 | EXTRA_DIST += ucs_copyright_char.expected 74 | EXTRA_DIST += test_float.expected 75 | EXTRA_DIST += test_cast.expected 76 | EXTRA_DIST += test_charcase.expected 77 | EXTRA_DIST += test_locale.expected 78 | EXTRA_DIST += test_null.expected 79 | EXTRA_DIST += test_parse.expected 80 | EXTRA_DIST += test_parse_int64.expected 81 | EXTRA_DIST += test_printbuf.expected 82 | EXTRA_DIST += testReplaceExisting.expected 83 | EXTRA_DIST += test_obj_iter-del.expected 84 | EXTRA_DIST += test_object_object_add_ex.expected 85 | EXTRA_DIST += test_object_object_add_exFormatted_plain.expected 86 | EXTRA_DIST += test_object_object_add_exFormatted_pretty.expected 87 | EXTRA_DIST += test_object_object_add_exFormatted_spaced.expected 88 | EXTRA_DIST += test_many_subobj.expected 89 | EXTRA_DIST += test_obj_obj_get_ex-null.expected 90 | 91 | testsubdir=testSubDir 92 | TESTS_ENVIRONMENT = top_builddir=$(top_builddir) 93 | -------------------------------------------------------------------------------- /tests/testReplaceExisting.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../json.h" 8 | #include "../debug.h" 9 | 10 | /* this is a work-around until we manage to fix configure.ac */ 11 | #pragma GCC diagnostic ignored "-Wdeclaration-after-statement" 12 | 13 | int main(int __attribute__((unused)) argc, char __attribute__((unused)) **argv) 14 | { 15 | struct fjson_object_iterator it; 16 | struct fjson_object_iterator itEnd; 17 | const char *key; 18 | MC_SET_DEBUG(1); 19 | 20 | /* 21 | * Check that replacing an existing object keeps the key valid, 22 | * and that it keeps the order the same. 23 | */ 24 | fjson_object *my_object = fjson_object_new_object(); 25 | fjson_object_object_add(my_object, "foo1", fjson_object_new_string("bar1")); 26 | fjson_object_object_add(my_object, "foo2", fjson_object_new_string("bar2")); 27 | fjson_object_object_add(my_object, "deleteme", fjson_object_new_string("bar2")); 28 | fjson_object_object_add(my_object, "foo3", fjson_object_new_string("bar3")); 29 | 30 | printf("==== delete-in-loop test starting ====\n"); 31 | 32 | int orig_count = 0; 33 | itEnd = fjson_object_iter_end(my_object); 34 | it = fjson_object_iter_begin(my_object); 35 | while (!fjson_object_iter_equal(&it, &itEnd)) { 36 | key = fjson_object_iter_peek_name(&it); 37 | printf("Key at index %d is [%s]", orig_count, key); 38 | /* need to advance now, as del invalidates "it" */ 39 | fjson_object_iter_next(&it); 40 | if (strcmp(key, "deleteme") == 0) { 41 | fjson_object_object_del(my_object, key); 42 | printf(" (deleted)\n"); 43 | } else { 44 | printf(" (kept)\n"); 45 | } 46 | orig_count++; 47 | } 48 | 49 | printf("==== replace-value first loop starting ====\n"); 50 | 51 | const char *original_key = NULL; 52 | orig_count = 0; 53 | itEnd = fjson_object_iter_end(my_object); 54 | it = fjson_object_iter_begin(my_object); 55 | while (!fjson_object_iter_equal(&it, &itEnd)) { 56 | key = fjson_object_iter_peek_name(&it); 57 | /* need to advance now, as modify invalidates "it" */ 58 | fjson_object_iter_next(&it); 59 | printf("Key at index %d is [%s]\n", orig_count, key); 60 | orig_count++; 61 | if (strcmp(key, "foo2") != 0) 62 | continue; 63 | printf("replacing value for key [%s]\n", key); 64 | original_key = key; 65 | fjson_object_object_add(my_object, key, fjson_object_new_string("zzz")); 66 | } 67 | 68 | printf("==== second loop starting ====\n"); 69 | 70 | int new_count = 0; 71 | int retval = 0; 72 | itEnd = fjson_object_iter_end(my_object); 73 | it = fjson_object_iter_begin(my_object); 74 | while (!fjson_object_iter_equal(&it, &itEnd)) { 75 | key = fjson_object_iter_peek_name(&it); 76 | /* need to advance now, as modify invalidates "it" */ 77 | fjson_object_iter_next(&it); 78 | printf("Key at index %d is [%s]\n", new_count, key); 79 | new_count++; 80 | if (strcmp(key, "foo2") != 0) 81 | continue; 82 | printf("pointer for key [%s] does %smatch\n", key, 83 | (key == original_key) ? "" : "NOT "); 84 | if (key != original_key) 85 | retval = 1; 86 | } 87 | if (new_count != orig_count) 88 | { 89 | printf("mismatch between original count (%d) and new count (%d)\n", 90 | orig_count, new_count); 91 | retval = 1; 92 | } 93 | 94 | fjson_object_put( my_object ); 95 | 96 | return retval; 97 | } 98 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2015 Rainer Gerhards 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a 5 | copy of this software and associated documentation files (the "Software"), 6 | to deal in the Software without restriction, including without limitation 7 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | and/or sell copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included 12 | in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | 22 | ---------------------------------------------------------------- 23 | 24 | Copyright (c) 2009-2012 Eric Haszlakiewicz 25 | 26 | Permission is hereby granted, free of charge, to any person obtaining a 27 | copy of this software and associated documentation files (the "Software"), 28 | to deal in the Software without restriction, including without limitation 29 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 30 | and/or sell copies of the Software, and to permit persons to whom the 31 | Software is furnished to do so, subject to the following conditions: 32 | 33 | The above copyright notice and this permission notice shall be included 34 | in all copies or substantial portions of the Software. 35 | 36 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 37 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 38 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 39 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 40 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 41 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 42 | SOFTWARE. 43 | 44 | ---------------------------------------------------------------- 45 | 46 | Copyright (c) 2004, 2005 Metaparadigm Pte Ltd 47 | 48 | Permission is hereby granted, free of charge, to any person obtaining a 49 | copy of this software and associated documentation files (the "Software"), 50 | to deal in the Software without restriction, including without limitation 51 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 52 | and/or sell copies of the Software, and to permit persons to whom the 53 | Software is furnished to do so, subject to the following conditions: 54 | 55 | The above copyright notice and this permission notice shall be included 56 | in all copies or substantial portions of the Software. 57 | 58 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 59 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 60 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 61 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 62 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 63 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 64 | SOFTWARE. 65 | -------------------------------------------------------------------------------- /m4/ax_check_compile_flag.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Check whether the given FLAG works with the current language's compiler 12 | # or gives an error. (Warnings, however, are ignored) 13 | # 14 | # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on 15 | # success/failure. 16 | # 17 | # If EXTRA-FLAGS is defined, it is added to the current language's default 18 | # flags (e.g. CFLAGS) when the check is done. The check is thus made with 19 | # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to 20 | # force the compiler to issue an error when a bad flag is given. 21 | # 22 | # INPUT gives an alternative input source to AC_COMPILE_IFELSE. 23 | # 24 | # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this 25 | # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. 26 | # 27 | # LICENSE 28 | # 29 | # Copyright (c) 2008 Guido U. Draheim 30 | # Copyright (c) 2011 Maarten Bosmans 31 | # 32 | # This program is free software: you can redistribute it and/or modify it 33 | # under the terms of the GNU General Public License as published by the 34 | # Free Software Foundation, either version 3 of the License, or (at your 35 | # option) any later version. 36 | # 37 | # This program is distributed in the hope that it will be useful, but 38 | # WITHOUT ANY WARRANTY; without even the implied warranty of 39 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 40 | # Public License for more details. 41 | # 42 | # You should have received a copy of the GNU General Public License along 43 | # with this program. If not, see . 44 | # 45 | # As a special exception, the respective Autoconf Macro's copyright owner 46 | # gives unlimited permission to copy, distribute and modify the configure 47 | # scripts that are the output of Autoconf when processing the Macro. You 48 | # need not follow the terms of the GNU General Public License when using 49 | # or distributing such scripts, even though portions of the text of the 50 | # Macro appear in them. The GNU General Public License (GPL) does govern 51 | # all other use of the material that constitutes the Autoconf Macro. 52 | # 53 | # This special exception to the GPL applies to versions of the Autoconf 54 | # Macro released by the Autoconf Archive. When you make and distribute a 55 | # modified version of the Autoconf Macro, you may extend this special 56 | # exception to the GPL to apply to your modified version as well. 57 | 58 | #serial 4 59 | 60 | AC_DEFUN([AX_CHECK_COMPILE_FLAG], 61 | [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF 62 | AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl 63 | AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ 64 | ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS 65 | _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" 66 | AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], 67 | [AS_VAR_SET(CACHEVAR,[yes])], 68 | [AS_VAR_SET(CACHEVAR,[no])]) 69 | _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) 70 | AS_VAR_IF(CACHEVAR,yes, 71 | [m4_default([$2], :)], 72 | [m4_default([$3], :)]) 73 | AS_VAR_POPDEF([CACHEVAR])dnl 74 | ])dnl AX_CHECK_COMPILE_FLAGS 75 | -------------------------------------------------------------------------------- /RELEASE_CHECKLIST.txt: -------------------------------------------------------------------------------- 1 | 2 | Release checklist: 3 | 4 | release=0.12 5 | git clone https://github.com/json-c/json-c json-c-${release} 6 | cd json-c-${release} 7 | 8 | Check that the compile works on Linux 9 | Check that the compile works on NetBSD 10 | Check ChangeLog to see if anything should be added. 11 | Make any fixes/changes *before* branching. 12 | 13 | git branch json-c-${release} 14 | git checkout json-c-${release} 15 | 16 | ------------ 17 | 18 | Update the version in json_c_version.h 19 | Update the version in Doxyfile 20 | Update the version in configure.ac 21 | Use ${release}. 22 | 23 | Update the libjson_la_LDFLAGS line in Makefile.am to the new version. 24 | Generally, unless we're doing a major release, change: 25 | -version-info x:y:z 26 | to 27 | -version-info x:y+1:z 28 | 29 | ------------ 30 | 31 | Generate the configure script and other files: 32 | sh autogen.sh 33 | git add -f Makefile.in aclocal.m4 config.guess \ 34 | config.sub configure depcomp install-sh \ 35 | ltmain.sh missing tests/Makefile.in \ 36 | INSTALL 37 | 38 | # check for anything else to be added: 39 | git status --ignored 40 | git commit 41 | 42 | ------------ 43 | 44 | Generate the doxygen documentation: 45 | doxygen 46 | git add -f doc 47 | git commit doc 48 | 49 | ------------ 50 | 51 | cd .. 52 | echo .git > excludes 53 | echo autom4te.cache >> excludes 54 | tar -czf json-c-${release}.tar.gz -X excludes json-c-${release} 55 | 56 | echo doc >> excludes 57 | tar -czf json-c-${release}-nodoc.tar.gz -X excludes json-c-${release} 58 | 59 | ------------ 60 | 61 | Tag the branch: 62 | cd json-c-${release} 63 | git tag -a json-c-${release}-$(date +%Y%m%d) -m "Release json-c-${release}" 64 | 65 | git push origin json-c-${release} 66 | git push --tags 67 | 68 | ------------ 69 | 70 | Go to Amazon S3 service at: 71 | https://console.aws.amazon.com/s3/ 72 | 73 | Upload the two tarballs in the json-c_releases folder. 74 | When uploading, use "Reduced Redundancy", and make the uploaded files publicly accessible. 75 | 76 | Logout of Amazon S3, and verify that the files are visible. 77 | https://s3.amazonaws.com/json-c_releases/releases/index.html 78 | 79 | =================================== 80 | 81 | Post-release checklist: 82 | 83 | git checkout master 84 | Add new section to ChangeLog 85 | Update the version in json_c_version.h 86 | Update the version in Doxyfile 87 | Update the version in configure.ac 88 | Use ${release}.99 to indicate a version "newer" than anything on the branch. 89 | 90 | Leave the libjson_la_LDFLAGS line in Makefile.am alone. 91 | For more details see: 92 | http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html 93 | 94 | ------------ 95 | 96 | Update the gh-pages branch with new docs: 97 | 98 | cd json-c-${release} 99 | git checkout json-c-${release} 100 | cd .. 101 | 102 | git clone -b gh-pages https://github.com/json-c/json-c json-c-pages 103 | cd json-c-pages 104 | mkdir json-c-${release} 105 | cp -R ../json-c-${release}/doc json-c-${release}/. 106 | cp ../json-c-${release}/README-WIN32.html json-c-${release}/. 107 | git add json-c-${release} 108 | git commit 109 | 110 | vi index.html 111 | Add/change links to current release. 112 | 113 | git commit index.html 114 | 115 | git push 116 | 117 | ------------ 118 | 119 | Update checksums on wiki page. 120 | 121 | cd .. 122 | openssl sha -sha256 json-c*gz 123 | openssl md5 json-c*gz 124 | 125 | Copy and paste this output into the wiki page at: 126 | https://github.com/json-c/json-c/wiki 127 | 128 | ------------ 129 | 130 | Send an email to the mailing list. 131 | 132 | -------------------------------------------------------------------------------- /tests/test-defs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Make sure srcdir is an absolute path. Supply the variable 4 | # if it does not exist. We want to be able to run the tests 5 | # stand-alone!! 6 | # 7 | srcdir=${srcdir-.} 8 | if test ! -d $srcdir ; then 9 | echo "test-defs.sh: installation error" 1>&2 10 | exit 1 11 | fi 12 | 13 | # Use absolute paths 14 | case "$srcdir" in 15 | /* | [A-Za-z]:\\*) ;; 16 | *) srcdir=`\cd $srcdir && pwd` ;; 17 | esac 18 | 19 | case "$top_builddir" in 20 | /* | [A-Za-z]:\\*) ;; 21 | *) top_builddir=`\cd ${top_builddir-..} && pwd` ;; 22 | esac 23 | 24 | top_builddir=${top_builddir}/tests 25 | 26 | progname=`echo "$0" | sed 's,^.*/,,'` 27 | testname=`echo "$progname" | sed 's,-.*$,,'` 28 | testsubdir=${testsubdir-testSubDir} 29 | testsubdir=${testsubdir}/${progname} 30 | 31 | # User can set VERBOSE to cause output redirection 32 | case "$VERBOSE" in 33 | [Nn]|[Nn][Oo]|0|"") 34 | VERBOSE=0 35 | exec > /dev/null 36 | ;; 37 | [Yy]|[Yy][Ee][Ss]) 38 | VERBOSE=1 39 | ;; 40 | esac 41 | 42 | rm -rf "$testsubdir" > /dev/null 2>&1 43 | mkdir -p "$testsubdir" 44 | CURDIR=`pwd` 45 | cd "$testsubdir" \ 46 | || { echo "Cannot make or change into $testsubdir"; exit 1; } 47 | 48 | echo "=== Running test $progname" 49 | 50 | CMP="${CMP-cmp}" 51 | 52 | # 53 | # This is a common function to check the results of a test program 54 | # that is intended to generate consistent output across runs. 55 | # 56 | # ${top_builddir} must be set to the top level build directory. 57 | # 58 | # Output will be written to the current directory. 59 | # 60 | # It must be passed the name of the test command to run, which must be present 61 | # in the ${top_builddir} directory. 62 | # 63 | # It will compare the output of running that against .expected 64 | # 65 | run_output_test() 66 | { 67 | if [ "$1" = "-o" ] ; then 68 | TEST_OUTPUT="$2" 69 | shift 70 | shift 71 | fi 72 | TEST_COMMAND="$1" 73 | shift 74 | if [ -z "${TEST_OUTPUT}" ] ; then 75 | TEST_OUTPUT=${TEST_COMMAND} 76 | fi 77 | 78 | REDIR_OUTPUT="> \"${TEST_OUTPUT}.out\"" 79 | if [ $VERBOSE -gt 1 ] ; then 80 | REDIR_OUTPUT="| tee \"${TEST_OUTPUT}.out\"" 81 | fi 82 | 83 | if [ "$VALGRIND" = "valgrind" ] ; then 84 | eval valgrind --tool=memcheck \ 85 | --trace-children=yes \ 86 | --demangle=yes \ 87 | --log-file="${TEST_OUTPUT}.vg.out" \ 88 | --leak-check=full \ 89 | --show-reachable=yes \ 90 | --run-libc-freeres=yes \ 91 | "\"${top_builddir}/${TEST_COMMAND}\"" \"\$@\" ${REDIR_OUTPUT} 92 | err=$? 93 | 94 | else 95 | eval "\"${top_builddir}/${TEST_COMMAND}"\" \"\$@\" ${REDIR_OUTPUT} 96 | err=$? 97 | fi 98 | 99 | if [ $err -ne 0 ] ; then 100 | echo "ERROR: \"${TEST_COMMAND} $@\" exited with non-zero exit status: $err" 1>&2 101 | fi 102 | 103 | if [ "$VALGRIND" = "valgrind" ] ; then 104 | if ! tail -1 "${TEST_OUTPUT}.vg.out" | grep -q "ERROR SUMMARY: 0 errors" ; then 105 | echo "ERROR: valgrind found errors during execution:" 1>&2 106 | cat "${TEST_OUTPUT}.vg.out" 107 | err=1 108 | fi 109 | fi 110 | 111 | if ! "$CMP" -s "${srcdir}/${TEST_OUTPUT}.expected" "${TEST_OUTPUT}.out" ; then 112 | echo "ERROR: \"${TEST_COMMAND} $@\" (${TEST_OUTPUT}) failed (set VERBOSE=1 to see full output):" 1>&2 113 | (cd "${CURDIR}" ; set -x ; diff "${srcdir}/${TEST_OUTPUT}.expected" "$testsubdir/${TEST_OUTPUT}.out") 114 | echo "cp \"$testsubdir/${TEST_OUTPUT}.out\" \"${srcdir}/${TEST_OUTPUT}.expected\"" 1>&2 115 | 116 | err=1 117 | fi 118 | 119 | # remove temp files for successful builds 120 | # this clanup is required for "make distcheck" 121 | if [ $err -eq 0 ] ; then 122 | rm -rf ${CURDIR}/${testsubdir}/* 123 | fi 124 | 125 | return $err 126 | } 127 | 128 | 129 | -------------------------------------------------------------------------------- /arraylist.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. 3 | * Michael Clark 4 | * Copyright (c) 2016 Rainer Gerhards 5 | * 6 | * This library is free software; you can redistribute it and/or modify 7 | * it under the terms of the MIT license. See COPYING for details. 8 | * 9 | */ 10 | 11 | #include "config.h" 12 | 13 | #ifdef STDC_HEADERS 14 | # include 15 | # include 16 | #endif /* STDC_HEADERS */ 17 | 18 | #if defined(HAVE_STRINGS_H) && !defined(_STRING_H) && !defined(__USE_BSD) 19 | # include 20 | #endif /* HAVE_STRINGS_H */ 21 | 22 | #include "arraylist.h" 23 | 24 | struct array_list* 25 | array_list_new(array_list_free_fn *free_fn) 26 | { 27 | struct array_list *arr; 28 | 29 | arr = (struct array_list*)calloc(1, sizeof(struct array_list)); 30 | if(!arr) return NULL; 31 | arr->size = ARRAY_LIST_DEFAULT_SIZE; 32 | arr->length = 0; 33 | arr->free_fn = free_fn; 34 | if(!(arr->array = (void**)calloc(sizeof(void*), arr->size))) { 35 | free(arr); 36 | return NULL; 37 | } 38 | return arr; 39 | } 40 | 41 | extern void 42 | array_list_free(struct array_list *arr) 43 | { 44 | int i; 45 | for(i = 0; i < arr->length; i++) 46 | if(arr->array[i]) arr->free_fn(arr->array[i]); 47 | free(arr->array); 48 | free(arr); 49 | } 50 | 51 | void* 52 | array_list_get_idx(struct array_list *arr, int i) 53 | { 54 | if(i >= arr->length) return NULL; 55 | return arr->array[i]; 56 | } 57 | 58 | static int array_list_expand_internal(struct array_list *arr, int max) 59 | { 60 | void *t; 61 | int new_size; 62 | 63 | if(max < arr->size) return 0; 64 | new_size = arr->size << 1; 65 | if (new_size < max) 66 | new_size = max; 67 | if(!(t = realloc(arr->array, new_size*sizeof(void*)))) return -1; 68 | arr->array = (void**)t; 69 | (void)memset(arr->array + arr->size, 0, (new_size-arr->size)*sizeof(void*)); 70 | arr->size = new_size; 71 | return 0; 72 | } 73 | 74 | int 75 | array_list_put_idx(struct array_list *arr, int idx, void *data) 76 | { 77 | if(array_list_expand_internal(arr, idx+1)) return -1; 78 | if(arr->array[idx]) arr->free_fn(arr->array[idx]); 79 | arr->array[idx] = data; 80 | if(arr->length <= idx) arr->length = idx + 1; 81 | return 0; 82 | } 83 | 84 | int 85 | array_list_add(struct array_list *arr, void *data) 86 | { 87 | return array_list_put_idx(arr, arr->length, data); 88 | } 89 | 90 | /* 91 | * Deleting the idx-th element in the array_list. 92 | */ 93 | void 94 | array_list_del_idx(struct array_list *const arr, const int idx) 95 | { 96 | if (idx < 0 || idx >= arr->length) { 97 | return; 98 | } 99 | if(arr->array[idx]) arr->free_fn(arr->array[idx]); 100 | if (--arr->length > idx) { 101 | memmove(arr->array + idx, arr->array + idx + 1, (arr->length - idx) * sizeof(void *)); 102 | } 103 | arr->array[arr->length] = NULL; 104 | return; 105 | } 106 | 107 | /* work around wrong compiler message: GCC and clang do 108 | * not handle sort_fn correctly if -Werror is given. 109 | */ 110 | #ifndef _AIX 111 | #pragma GCC diagnostic push 112 | #ifdef __clang__ 113 | #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" 114 | #else 115 | #if __GNUC__ < 6 116 | #pragma GCC diagnostic ignored "-Werror" 117 | #endif 118 | #endif 119 | #endif 120 | void 121 | array_list_sort(struct array_list *arr, int(*sort_fn)(const void *, const void *)) 122 | { 123 | qsort(arr->array, arr->length, sizeof(arr->array[0]), sort_fn); 124 | } 125 | #ifndef _AIX 126 | #pragma GCC diagnostic pop 127 | 128 | #pragma GCC diagnostic push 129 | #ifdef __clang__ 130 | #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" 131 | #else 132 | #if __GNUC__ < 6 133 | #pragma GCC diagnostic ignored "-Werror" 134 | #endif 135 | #endif 136 | #endif 137 | void* array_list_bsearch(const void **key, struct array_list *arr, 138 | int (*sort_fn)(const void *, const void *)) 139 | { 140 | return bsearch(key, arr->array, arr->length, sizeof(arr->array[0]), 141 | sort_fn); 142 | } 143 | #ifndef _AIX 144 | #pragma GCC diagnostic pop 145 | #endif 146 | 147 | int 148 | array_list_length(struct array_list *arr) 149 | { 150 | return arr->length; 151 | } 152 | -------------------------------------------------------------------------------- /tests/test_cast.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Tests if casting within the fjson_object_get_* functions work correctly. 3 | * Also checks the fjson_object_get_type and fjson_object_is_type functions. 4 | */ 5 | 6 | #include "config.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "../json_object.h" 13 | #include "../json_tokener.h" 14 | #include "../json_util.h" 15 | 16 | /* this is a work-around until we manage to fix configure.ac */ 17 | #pragma GCC diagnostic ignored "-Wdeclaration-after-statement" 18 | 19 | static void getit(struct fjson_object *new_obj, const char *field); 20 | static void checktype_header(void); 21 | static void checktype(struct fjson_object *new_obj, const char *field); 22 | 23 | int main(int __attribute__((unused)) argc, char __attribute__((unused)) **argv) 24 | { 25 | const char *input = "{\n\ 26 | \"string_of_digits\": \"123\",\n\ 27 | \"regular_number\": 222,\n\ 28 | \"decimal_number\": 99.55,\n\ 29 | \"boolean_true\": true,\n\ 30 | \"boolean_false\": false,\n\ 31 | \"big_number\": 2147483649,\n\ 32 | \"a_null\": null,\n\ 33 | }"; 34 | /* Note: 2147483649 = INT_MAX + 2 */ 35 | 36 | struct fjson_object *new_obj; 37 | 38 | new_obj = fjson_tokener_parse(input); 39 | printf("Parsed input: %s\n", input); 40 | printf("Result is %s\n", (new_obj == NULL) ? "NULL (error!)" : "not NULL"); 41 | if (!new_obj) 42 | return 1; // oops, we failed. 43 | 44 | getit(new_obj, "string_of_digits"); 45 | getit(new_obj, "regular_number"); 46 | getit(new_obj, "decimal_number"); 47 | getit(new_obj, "boolean_true"); 48 | getit(new_obj, "boolean_false"); 49 | getit(new_obj, "big_number"); 50 | getit(new_obj, "a_null"); 51 | 52 | // Now check the behaviour of the fjson_object_is_type() function. 53 | printf("\n================================\n"); 54 | checktype_header(); 55 | checktype(new_obj, NULL); 56 | checktype(new_obj, "string_of_digits"); 57 | checktype(new_obj, "regular_number"); 58 | checktype(new_obj, "decimal_number"); 59 | checktype(new_obj, "boolean_true"); 60 | checktype(new_obj, "boolean_false"); 61 | checktype(new_obj, "big_number"); 62 | checktype(new_obj, "a_null"); 63 | 64 | fjson_object_put(new_obj); 65 | 66 | return 0; 67 | } 68 | 69 | static void getit(struct fjson_object *new_obj, const char *field) 70 | { 71 | struct fjson_object *o = NULL; 72 | if (!fjson_object_object_get_ex(new_obj, field, &o)) 73 | printf("Field %s does not exist\n", field); 74 | 75 | enum fjson_type o_type = fjson_object_get_type(o); 76 | printf("new_obj.%s fjson_object_get_type()=%s\n", field, 77 | fjson_type_to_name(o_type)); 78 | printf("new_obj.%s fjson_object_get_int()=%d\n", field, 79 | fjson_object_get_int(o)); 80 | printf("new_obj.%s fjson_object_get_int64()=%" PRId64 "\n", field, 81 | fjson_object_get_int64(o)); 82 | printf("new_obj.%s fjson_object_get_boolean()=%d\n", field, 83 | fjson_object_get_boolean(o)); 84 | printf("new_obj.%s fjson_object_get_double()=%f\n", field, 85 | fjson_object_get_double(o)); 86 | } 87 | 88 | static void checktype_header(void) 89 | { 90 | printf("fjson_object_is_type: %s,%s,%s,%s,%s,%s,%s\n", 91 | fjson_type_to_name(fjson_type_null), 92 | fjson_type_to_name(fjson_type_boolean), 93 | fjson_type_to_name(fjson_type_double), 94 | fjson_type_to_name(fjson_type_int), 95 | fjson_type_to_name(fjson_type_object), 96 | fjson_type_to_name(fjson_type_array), 97 | fjson_type_to_name(fjson_type_string)); 98 | } 99 | static void checktype(struct fjson_object *new_obj, const char *field) 100 | { 101 | struct fjson_object *o = new_obj; 102 | if (field && !fjson_object_object_get_ex(new_obj, field, &o)) 103 | printf("Field %s does not exist\n", field); 104 | 105 | printf("new_obj%s%-18s: %d,%d,%d,%d,%d,%d,%d\n", 106 | field ? "." : " ", field ? field : "", 107 | fjson_object_is_type(o, fjson_type_null), 108 | fjson_object_is_type(o, fjson_type_boolean), 109 | fjson_object_is_type(o, fjson_type_double), 110 | fjson_object_is_type(o, fjson_type_int), 111 | fjson_object_is_type(o, fjson_type_object), 112 | fjson_object_is_type(o, fjson_type_array), 113 | fjson_object_is_type(o, fjson_type_string)); 114 | } 115 | -------------------------------------------------------------------------------- /tests/test_parse.expected: -------------------------------------------------------------------------------- 1 | new_obj.to_string()="\u0003" 2 | new_obj.to_string()="foo" 3 | new_obj.to_string()="foo" 4 | new_obj.to_string()="ABC" 5 | new_obj.to_string()=null 6 | new_obj.to_string()=NaN 7 | new_obj.to_string()=null 8 | new_obj.to_string()=null 9 | new_obj.to_string()=null 10 | new_obj.to_string()=Infinity 11 | new_obj.to_string()=Infinity 12 | new_obj.to_string()=-Infinity 13 | new_obj.to_string()=-Infinity 14 | new_obj.to_string()=true 15 | new_obj.to_string()=12 16 | new_obj.to_string()=12.3 17 | new_obj.to_string()=null 18 | new_obj.to_string()=null 19 | new_obj.to_string()={ "FoO": -12.3E512 } 20 | new_obj.to_string()=null 21 | new_obj.to_string()=[ "\n" ] 22 | new_obj.to_string()=[ "\nabc\n" ] 23 | new_obj.to_string()=[ null ] 24 | new_obj.to_string()=[ ] 25 | new_obj.to_string()=[ false ] 26 | new_obj.to_string()=[ "abc", null, "def", 12 ] 27 | new_obj.to_string()={ } 28 | new_obj.to_string()={ "foo": "bar" } 29 | new_obj.to_string()={ "foo": "bar", "baz": null, "bool0": true } 30 | new_obj.to_string()={ "foo": [ null, "foo" ] } 31 | new_obj.to_string()={ "abc": 12, "foo": "bar", "bool0": false, "bool1": true, "arr": [ 1, 2, 3, null, 5 ] } 32 | ================================== 33 | fjson_tokener_parse_versbose() OK 34 | ================================== 35 | Starting incremental tests. 36 | Note: quotes and backslashes seen in the output here are literal values passed 37 | to the parse functions. e.g. this is 4 characters: "\f" 38 | fjson_tokener_parse({ "foo) ... got error as expected 39 | fjson_tokener_parse_ex(tok, { "foo": 123 }, 14) ... OK: got object of type [object]: { "foo": 123 } 40 | fjson_tokener_parse_ex(tok, { "foo": 456 }, 14) ... OK: got object of type [object]: { "foo": 456 } 41 | fjson_tokener_parse_ex(tok, { "foo": 789 }, 14) ... OK: got object of type [object]: { "foo": 789 } 42 | fjson_tokener_parse_ex(tok, { "foo , 6) ... OK: got correct error: continue 43 | fjson_tokener_parse_ex(tok, ": {"bar , 8) ... OK: got correct error: continue 44 | fjson_tokener_parse_ex(tok, ":13}} , 6) ... OK: got object of type [object]: { "foo": { "bar": 13 } } 45 | fjson_tokener_parse_ex(tok, { "foo , 6) ... OK: got correct error: continue 46 | fjson_tokener_parse_ex(tok, : "bar"} , 8) ... OK: got correct error: unexpected character 47 | fjson_tokener_parse_ex(tok, { "foo , 6) ... OK: got correct error: continue 48 | fjson_tokener_parse_ex(tok, ": {"bar , 8) ... OK: got correct error: continue 49 | fjson_tokener_parse_ex(tok, ":13}}XXXX , 10) ... OK: got object of type [object]: { "foo": { "bar": 13 } } 50 | fjson_tokener_parse_ex(tok, XXXX , 4) ... OK: got correct error: unexpected character 51 | fjson_tokener_parse_ex(tok, {"x": 123 }"X", 14) ... OK: got object of type [object]: { "x": 123 } 52 | fjson_tokener_parse_ex(tok, "Y" , 3) ... OK: got object of type [string]: "Y" 53 | fjson_tokener_parse_ex(tok, 1 , 1) ... OK: got correct error: continue 54 | fjson_tokener_parse_ex(tok, 2 , 2) ... OK: got object of type [int]: 12 55 | fjson_tokener_parse_ex(tok, 2015-01-15 , 10) ... OK: got correct error: number expected 56 | fjson_tokener_parse_ex(tok, "blue" , 6) ... OK: got object of type [string]: "blue" 57 | fjson_tokener_parse_ex(tok, "\"" , 4) ... OK: got object of type [string]: "\"" 58 | fjson_tokener_parse_ex(tok, "\\" , 4) ... OK: got object of type [string]: "\\" 59 | fjson_tokener_parse_ex(tok, "\b" , 4) ... OK: got object of type [string]: "\b" 60 | fjson_tokener_parse_ex(tok, "\f" , 4) ... OK: got object of type [string]: "\f" 61 | fjson_tokener_parse_ex(tok, "\n" , 4) ... OK: got object of type [string]: "\n" 62 | fjson_tokener_parse_ex(tok, "\r" , 4) ... OK: got object of type [string]: "\r" 63 | fjson_tokener_parse_ex(tok, "\t" , 4) ... OK: got object of type [string]: "\t" 64 | fjson_tokener_parse_ex(tok, [1,2,3] , 7) ... OK: got object of type [array]: [ 1, 2, 3 ] 65 | fjson_tokener_parse_ex(tok, [1,2,3,] , 8) ... OK: got object of type [array]: [ 1, 2, 3 ] 66 | fjson_tokener_parse_ex(tok, [1,2,,3,] , 9) ... OK: got correct error: unexpected character 67 | fjson_tokener_parse_ex(tok, [1,2,3,] , 8) ... OK: got correct error: unexpected character 68 | fjson_tokener_parse_ex(tok, {"a":1,} , 8) ... OK: got correct error: unexpected character 69 | End Incremental Tests OK=30 ERROR=0 70 | ================================== 71 | -------------------------------------------------------------------------------- /tests/test1.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../json.h" 8 | #include "../debug.h" 9 | #include "parse_flags.h" 10 | 11 | /* this is a work-around until we manage to fix configure.ac */ 12 | #pragma GCC diagnostic ignored "-Wdeclaration-after-statement" 13 | 14 | #define DEBUG_SEED(s) 15 | 16 | static int sort_fn (const void *j1, const void *j2) 17 | { 18 | fjson_object * const *jso1, * const *jso2; 19 | int i1, i2; 20 | 21 | jso1 = (fjson_object* const*)j1; 22 | jso2 = (fjson_object* const*)j2; 23 | if (!*jso1 && !*jso2) 24 | return 0; 25 | if (!*jso1) 26 | return -1; 27 | if (!*jso2) 28 | return 1; 29 | 30 | i1 = fjson_object_get_int(*jso1); 31 | i2 = fjson_object_get_int(*jso2); 32 | 33 | return i1 - i2; 34 | } 35 | 36 | #ifdef TEST_FORMATTED 37 | #define fjson_object_to_json_string(obj) fjson_object_to_json_string_ext(obj,sflags) 38 | #else 39 | /* no special define */ 40 | #endif 41 | 42 | int main(int __attribute__((unused)) argc, char __attribute__((unused)) **argv) 43 | { 44 | fjson_object *my_string, *my_int, *my_object, *my_array; 45 | int i; 46 | #ifdef TEST_FORMATTED 47 | int sflags = 0; 48 | #endif 49 | 50 | MC_SET_DEBUG(1); 51 | 52 | #ifdef TEST_FORMATTED 53 | sflags = parse_flags(argc, argv); 54 | #endif 55 | 56 | my_string = fjson_object_new_string("\t"); 57 | printf("my_string=%s\n", fjson_object_get_string(my_string)); 58 | printf("my_string.to_string()=%s\n", fjson_object_to_json_string(my_string)); 59 | fjson_object_put(my_string); 60 | 61 | my_string = fjson_object_new_string("\\"); 62 | printf("my_string=%s\n", fjson_object_get_string(my_string)); 63 | printf("my_string.to_string()=%s\n", fjson_object_to_json_string(my_string)); 64 | fjson_object_put(my_string); 65 | 66 | my_string = fjson_object_new_string("foo"); 67 | printf("my_string=%s\n", fjson_object_get_string(my_string)); 68 | printf("my_string.to_string()=%s\n", fjson_object_to_json_string(my_string)); 69 | 70 | my_int = fjson_object_new_int(9); 71 | printf("my_int=%d\n", fjson_object_get_int(my_int)); 72 | printf("my_int.to_string()=%s\n", fjson_object_to_json_string(my_int)); 73 | 74 | my_array = fjson_object_new_array(); 75 | fjson_object_array_add(my_array, fjson_object_new_int(1)); 76 | fjson_object_array_add(my_array, fjson_object_new_int(2)); 77 | fjson_object_array_add(my_array, fjson_object_new_int(3)); 78 | fjson_object_array_put_idx(my_array, 4, fjson_object_new_int(5)); 79 | printf("my_array=\n"); 80 | for(i=0; i < fjson_object_array_length(my_array); i++) 81 | { 82 | fjson_object *obj = fjson_object_array_get_idx(my_array, i); 83 | printf("\t[%d]=%s\n", i, fjson_object_to_json_string(obj)); 84 | } 85 | printf("my_array.to_string()=%s\n", fjson_object_to_json_string(my_array)); 86 | 87 | fjson_object_put(my_array); 88 | 89 | my_array = fjson_object_new_array(); 90 | fjson_object_array_add(my_array, fjson_object_new_int(3)); 91 | fjson_object_array_add(my_array, fjson_object_new_int(1)); 92 | fjson_object_array_add(my_array, fjson_object_new_int(2)); 93 | fjson_object_array_put_idx(my_array, 4, fjson_object_new_int(0)); 94 | printf("my_array=\n"); 95 | for(i=0; i < fjson_object_array_length(my_array); i++) 96 | { 97 | fjson_object *obj = fjson_object_array_get_idx(my_array, i); 98 | printf("\t[%d]=%s\n", i, fjson_object_to_json_string(obj)); 99 | } 100 | printf("my_array.to_string()=%s\n", fjson_object_to_json_string(my_array)); 101 | fjson_object_array_sort(my_array, sort_fn); 102 | printf("my_array=\n"); 103 | for(i=0; i < fjson_object_array_length(my_array); i++) 104 | { 105 | fjson_object *obj = fjson_object_array_get_idx(my_array, i); 106 | printf("\t[%d]=%s\n", i, fjson_object_to_json_string(obj)); 107 | } 108 | printf("my_array.to_string()=%s\n", fjson_object_to_json_string(my_array)); 109 | 110 | my_object = fjson_object_new_object(); 111 | fjson_object_object_add(my_object, "abc", fjson_object_new_int(12)); 112 | fjson_object_object_add(my_object, "foo", fjson_object_new_string("bar")); 113 | fjson_object_object_add(my_object, "bool0", fjson_object_new_boolean(0)); 114 | fjson_object_object_add(my_object, "bool1", fjson_object_new_boolean(1)); 115 | fjson_object_object_add(my_object, "baz", fjson_object_new_string("bang")); 116 | 117 | fjson_object *baz_obj = fjson_object_new_string("fark"); 118 | fjson_object_get(baz_obj); 119 | fjson_object_object_add(my_object, "baz", baz_obj); 120 | fjson_object_object_del(my_object, "baz"); 121 | 122 | /* baz_obj should still be valid */ 123 | printf("baz_obj.to_string()=%s\n", fjson_object_to_json_string(baz_obj)); 124 | fjson_object_put(baz_obj); 125 | 126 | /*fjson_object_object_add(my_object, "arr", my_array);*/ 127 | printf("my_object=\n"); 128 | struct fjson_object_iterator it = fjson_object_iter_begin(my_object); 129 | struct fjson_object_iterator itEnd = fjson_object_iter_end(my_object); 130 | while (!fjson_object_iter_equal(&it, &itEnd)) { 131 | printf("\t%s: %s\n", 132 | fjson_object_iter_peek_name(&it), 133 | fjson_object_to_json_string(fjson_object_iter_peek_value(&it))); 134 | fjson_object_iter_next(&it); 135 | } 136 | 137 | printf("my_object.to_string()=%s\n", fjson_object_to_json_string(my_object)); 138 | 139 | fjson_object_put(my_string); 140 | fjson_object_put(my_int); 141 | fjson_object_put(my_object); 142 | fjson_object_put(my_array); 143 | 144 | return 0; 145 | } 146 | -------------------------------------------------------------------------------- /tests/test_printbuf.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../debug.h" 9 | #include "../printbuf.h" 10 | 11 | /* this is a work-around until we manage to fix configure.ac */ 12 | #pragma GCC diagnostic ignored "-Wdeclaration-after-statement" 13 | 14 | static void test_basic_printbuf_memset(void) 15 | { 16 | struct printbuf *pb; 17 | 18 | printf("%s: starting test\n", __func__); 19 | pb = printbuf_new(); 20 | sprintbuf(pb, "blue:%d", 1); 21 | printbuf_memset(pb, -1, 'x', 52); 22 | printf("Buffer contents:%.*s\n", printbuf_length(pb), pb->buf); 23 | printbuf_free(pb); 24 | printf("%s: end test\n", __func__); 25 | } 26 | 27 | static void test_printbuf_memset_length(void) 28 | { 29 | struct printbuf *pb; 30 | 31 | printf("%s: starting test\n", __func__); 32 | pb = printbuf_new(); 33 | printbuf_memset(pb, -1, ' ', 0); 34 | printbuf_memset(pb, -1, ' ', 0); 35 | printbuf_memset(pb, -1, ' ', 0); 36 | printbuf_memset(pb, -1, ' ', 0); 37 | printbuf_memset(pb, -1, ' ', 0); 38 | printf("Buffer length: %d\n", printbuf_length(pb)); 39 | printbuf_memset(pb, -1, ' ', 2); 40 | printbuf_memset(pb, -1, ' ', 4); 41 | printbuf_memset(pb, -1, ' ', 6); 42 | printf("Buffer length: %d\n", printbuf_length(pb)); 43 | printbuf_memset(pb, -1, ' ', 6); 44 | printf("Buffer length: %d\n", printbuf_length(pb)); 45 | printbuf_memset(pb, -1, ' ', 8); 46 | printbuf_memset(pb, -1, ' ', 10); 47 | printbuf_memset(pb, -1, ' ', 10); 48 | printbuf_memset(pb, -1, ' ', 10); 49 | printbuf_memset(pb, -1, ' ', 20); 50 | printf("Buffer length: %d\n", printbuf_length(pb)); 51 | 52 | // No length change should occur 53 | printbuf_memset(pb, 0, 'x', 30); 54 | printf("Buffer length: %d\n", printbuf_length(pb)); 55 | 56 | // This should extend it by one. 57 | printbuf_memset(pb, 0, 'x', printbuf_length(pb) + 1); 58 | printf("Buffer length: %d\n", printbuf_length(pb)); 59 | 60 | printbuf_free(pb); 61 | printf("%s: end test\n", __func__); 62 | } 63 | 64 | static void test_printbuf_memappend(int *before_resize); 65 | static void test_printbuf_memappend(int *before_resize) 66 | { 67 | struct printbuf *pb; 68 | int initial_size; 69 | 70 | printf("%s: starting test\n", __func__); 71 | pb = printbuf_new(); 72 | printf("Buffer length: %d\n", printbuf_length(pb)); 73 | 74 | initial_size = pb->size; 75 | 76 | while(pb->size == initial_size) 77 | { 78 | printbuf_memappend_fast(pb, "x", 1); 79 | } 80 | *before_resize = printbuf_length(pb) - 1; 81 | printf("Appended %d bytes for resize: [%s]\n", *before_resize + 1, pb->buf); 82 | 83 | printbuf_reset(pb); 84 | printbuf_memappend_fast(pb, "bluexyz123", 3); 85 | printf("Partial append: %d, [%s]\n", printbuf_length(pb), pb->buf); 86 | 87 | char with_nulls[] = { 'a', 'b', '\0', 'c' }; 88 | printbuf_reset(pb); 89 | printbuf_memappend_fast(pb, with_nulls, (int)sizeof(with_nulls)); 90 | printf("With embedded \\0 character: %d, [%s]\n", printbuf_length(pb), pb->buf); 91 | 92 | printbuf_free(pb); 93 | pb = printbuf_new(); 94 | char *data = malloc(*before_resize); 95 | memset(data, 'X', *before_resize); 96 | printbuf_memappend_fast(pb, data, *before_resize); 97 | printf("Append to just before resize: %d, [%s]\n", printbuf_length(pb), pb->buf); 98 | 99 | free(data); 100 | printbuf_free(pb); 101 | 102 | pb = printbuf_new(); 103 | data = malloc(*before_resize + 1); 104 | memset(data, 'X', *before_resize + 1); 105 | printbuf_memappend_fast(pb, data, *before_resize + 1); 106 | printf("Append to just after resize: %d, [%s]\n", printbuf_length(pb), pb->buf); 107 | 108 | free(data); 109 | 110 | printbuf_free(pb); 111 | printf("%s: end test\n", __func__); 112 | } 113 | 114 | static void test_sprintbuf(int before_resize); 115 | static void test_sprintbuf(int before_resize) 116 | { 117 | struct printbuf *pb; 118 | 119 | printf("%s: starting test\n", __func__); 120 | pb = printbuf_new(); 121 | printf("Buffer length: %d\n", printbuf_length(pb)); 122 | 123 | char *data = malloc(before_resize + 1 + 1); 124 | memset(data, 'X', before_resize + 1 + 1); 125 | data[before_resize + 1] = '\0'; 126 | sprintbuf(pb, "%s", data); 127 | free(data); 128 | printf("sprintbuf to just after resize(%d+1): %d, [%s], strlen(buf)=%d\n", before_resize, printbuf_length(pb), pb->buf, (int)strlen(pb->buf)); 129 | 130 | printbuf_reset(pb); 131 | sprintbuf(pb, "plain"); 132 | printf("%d, [%s]\n", printbuf_length(pb), pb->buf); 133 | 134 | sprintbuf(pb, "%d", 1); 135 | printf("%d, [%s]\n", printbuf_length(pb), pb->buf); 136 | 137 | sprintbuf(pb, "%d", INT_MAX); 138 | printf("%d, [%s]\n", printbuf_length(pb), pb->buf); 139 | 140 | sprintbuf(pb, "%d", INT_MIN); 141 | printf("%d, [%s]\n", printbuf_length(pb), pb->buf); 142 | 143 | sprintbuf(pb, "%s", "%s"); 144 | printf("%d, [%s]\n", printbuf_length(pb), pb->buf); 145 | 146 | printbuf_free(pb); 147 | printf("%s: end test\n", __func__); 148 | } 149 | 150 | int main(int __attribute__((unused)) argc, char __attribute__((unused)) **argv) 151 | { 152 | int before_resize = 0; 153 | 154 | mc_set_debug(1); 155 | 156 | test_basic_printbuf_memset(); 157 | printf("========================================\n"); 158 | test_printbuf_memset_length(); 159 | printf("========================================\n"); 160 | test_printbuf_memappend(&before_resize); 161 | printf("========================================\n"); 162 | test_sprintbuf(before_resize); 163 | printf("========================================\n"); 164 | 165 | return 0; 166 | } 167 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ(2.52) 2 | 3 | # Process this file with autoconf to produce a configure script. 4 | AC_INIT([libfastjson], [1.2305.0.master], [rsyslog@lists.adiscon.com]) 5 | # AIXPORT START: Detect the underlying OS 6 | unamestr=$(uname) 7 | AM_CONDITIONAL([AIX], [test x$unamestr = xAIX]) 8 | # AIXPORT END 9 | 10 | # AIXPORT : Set the required variables for AIX config script 11 | if test "$unamestr" = "AIX"; then 12 | export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig" 13 | LIBS="-lbsd -lm" 14 | CPPFLAGS="-D_AIX -D_THREAD_SAFE -D_BSD=43" 15 | LDFLAGS="-qcpluscmt -brtl -bexpall " 16 | CC="xlc" 17 | AC_PREFIX_DEFAULT(/usr) 18 | fi 19 | # AIXPORT END 20 | 21 | 22 | AC_CONFIG_HEADER(config.h) 23 | 24 | AM_INIT_AUTOMAKE([subdir-objects]) 25 | AC_CONFIG_MACRO_DIR([m4]) 26 | 27 | AC_PROG_CC_C99 28 | AC_PROG_MAKE_SET 29 | m4_ifdef([AC_USE_SYSTEM_EXTENSIONS], [AC_USE_SYSTEM_EXTENSIONS]) 30 | 31 | if test "$GCC" = "yes" 32 | then 33 | m4_ifdef([AX_IS_RELEASE], [ 34 | AX_IS_RELEASE([git-directory]) 35 | m4_ifdef([AX_COMPILER_FLAGS], [ 36 | AX_COMPILER_FLAGS() 37 | ], [ 38 | CFLAGS="$CFLAGS -W -Wall -Wformat-security -Wshadow -Wcast-align -Wpointer-arith -Wmissing-format-attribute -g" 39 | AC_MSG_WARN([missing AX_COMPILER_FLAGS macro, not using it]) 40 | ]) 41 | ], [ 42 | CFLAGS="$CFLAGS -W -Wall -Wformat-security -Wshadow -Wcast-align -Wpointer-arith -Wmissing-format-attribute -g" 43 | AC_MSG_WARN([missing AX_IS_RELEASE macro, not using AX_COMPILER_FLAGS macro because of this]) 44 | ]) 45 | else 46 | AC_MSG_WARN([compiler is not GCC or close compatible, not using ax_compiler_flags because of this (CC=$CC)]) 47 | fi 48 | 49 | AC_ARG_ENABLE(rdrand, 50 | AS_HELP_STRING([--enable-rdrand], 51 | [Enable RDRAND Hardware RNG Hash Seed generation on supported x86/x64 platforms.]), 52 | [if test x$enableval = xyes; then 53 | enable_rdrand=yes 54 | AC_DEFINE(ENABLE_RDRAND, 1, [Enable RDRANR Hardware RNG Hash Seed]) 55 | fi]) 56 | 57 | if test "x$enable_rdrand" = "xyes"; then 58 | AC_MSG_RESULT([RDRAND Hardware RNG Hash Seed enabled on supported x86/x64 platforms]) 59 | else 60 | AC_MSG_RESULT([RDRAND Hardware RNG Hash Seed disabled. Use --enable-rdrand to enable]) 61 | fi 62 | 63 | # enable silent build by default 64 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) 65 | 66 | AC_ARG_ENABLE(valgrind, 67 | [AS_HELP_STRING([--enable-valgrind],[enable running the testbench under valgrind memcheck tool @<:@default=no@:>@])], 68 | [case "${enableval}" in 69 | yes) enable_valgrind="yes" ;; 70 | no) enable_valgrind="no" ;; 71 | *) AC_MSG_ERROR(bad value ${enableval} for --enable-valgrind) ;; 72 | esac], 73 | [enable_valgrind="no"] 74 | ) 75 | if test "$enable_valgrind" = "yes"; then 76 | AC_CHECK_PROG(VALGRIND, [valgrind], [valgrind], [no]) 77 | if test "x$VALGRIND" = "xno"; then 78 | AC_MSG_ERROR([--enable-valgrind given, but valgrind is not present on system]) 79 | fi 80 | AC_SUBST(VALGRIND) 81 | AC_DEFINE(VALGRIND, 1, [valgrind enabled]) 82 | fi 83 | 84 | # Checks for programs. 85 | 86 | # Checks for libraries. 87 | 88 | # Checks for header files. 89 | AM_PROG_CC_C_O 90 | AC_HEADER_STDC 91 | AC_CHECK_HEADERS(fcntl.h limits.h strings.h syslog.h unistd.h [sys/cdefs.h] [sys/param.h] stdarg.h locale.h endian.h) 92 | 93 | # Checks for typedefs, structures, and compiler characteristics. 94 | AC_C_CONST 95 | AC_TYPE_SIZE_T 96 | 97 | 98 | # check for availability of atomic operations 99 | RS_ATOMIC_OPERATIONS 100 | RS_ATOMIC_OPERATIONS_64BIT 101 | 102 | 103 | # Checks for library functions. 104 | AC_FUNC_VPRINTF 105 | AC_FUNC_MEMCMP 106 | AC_CHECK_FUNCS(strcasecmp strdup strerror snprintf vsnprintf vasprintf open vsyslog strncasecmp setlocale) 107 | 108 | if test "$ac_cv_have_decl_isnan" = "yes" ; then 109 | AC_TRY_LINK([#include ], [float f = 0.0; return isnan(f)], [], [LIBS="$LIBS -lm"]) 110 | fi 111 | 112 | #check if .section.gnu.warning accepts long strings (for __warn_references) 113 | AC_LANG_PUSH([C]) 114 | 115 | AC_MSG_CHECKING([if .gnu.warning accepts long strings]) 116 | AC_LINK_IFELSE([AC_LANG_SOURCE([[ 117 | extern void json_object_get(); 118 | __asm__(".section .gnu.json_object_get,\n\t.ascii \"Please link against libfastjson instead of libjson\"\n\t.text"); 119 | 120 | int main(int c,char* v) {return 0;} 121 | ]])], [ 122 | AC_DEFINE(HAS_GNU_WARNING_LONG, 1, [Define if .gnu.warning accepts long strings.]) 123 | AC_MSG_RESULT(yes) 124 | ], [ 125 | AC_MSG_RESULT(no) 126 | ]) 127 | 128 | AC_LANG_POP([C]) 129 | 130 | AM_PROG_LIBTOOL 131 | 132 | # Check for the -Bsymbolic-functions linker flag 133 | AC_ARG_ENABLE([Bsymbolic], 134 | [AS_HELP_STRING([--disable-Bsymbolic], [Avoid linking with -Bsymbolic-function])], 135 | [], 136 | [enable_Bsymbolic=check]) 137 | 138 | AS_IF([test "x$enable_Bsymbolic" = "xcheck"], 139 | [ 140 | saved_LDFLAGS="${LDFLAGS}" 141 | AC_MSG_CHECKING([for -Bsymbolic-functions linker flag]) 142 | LDFLAGS=-Wl,-Bsymbolic-functions 143 | AC_TRY_LINK([], [int main (void) { return 0; }], 144 | [ 145 | AC_MSG_RESULT([yes]) 146 | enable_Bsymbolic=yes 147 | ], 148 | [ 149 | AC_MSG_RESULT([no]) 150 | enable_Bsymbolic=no 151 | ]) 152 | LDFLAGS="${saved_LDFLAGS}" 153 | ]) 154 | 155 | AS_IF([test "x$enable_Bsymbolic" = "xyes"], [JSON_BSYMBOLIC_LDFLAGS=-Wl[,]-Bsymbolic-functions]) 156 | AC_SUBST(JSON_BSYMBOLIC_LDFLAGS) 157 | 158 | AC_CONFIG_FILES([ 159 | Makefile 160 | libfastjson.pc 161 | libfastjson-uninstalled.pc 162 | tests/Makefile 163 | ]) 164 | 165 | AC_OUTPUT 166 | 167 | -------------------------------------------------------------------------------- /json_object_iterator.c: -------------------------------------------------------------------------------- 1 | /** 2 | ******************************************************************************* 3 | * @file fjson_object_iterator.c 4 | * 5 | * Copyright (c) 2009-2012 Hewlett-Packard Development Company, L.P. 6 | * Copyright (c) 2016 Adiscon GmbH 7 | * Rainer Gerhards 8 | * 9 | * This library is free software; you can redistribute it and/or modify 10 | * it under the terms of the MIT license. See COPYING for details. 11 | * 12 | * @brief json-c forces clients to use its private data 13 | * structures for JSON Object iteration. This API 14 | * implementation corrects that by abstracting the 15 | * private json-c details. 16 | * 17 | ******************************************************************************* 18 | */ 19 | 20 | #include "config.h" 21 | #include 22 | #include "json.h" 23 | #include "json_object_private.h" 24 | #include "json_object_iterator.h" 25 | #include "debug.h" 26 | 27 | /** 28 | * How It Works 29 | * 30 | * For each JSON Object, json-c maintains a linked list of zero 31 | * or more lh_entry (link-hash entry) structures inside the 32 | * Object's link-hash table (lh_table). 33 | * 34 | * Each lh_entry structure on the JSON Object's linked list 35 | * represents a single name/value pair. The "next" field of the 36 | * last lh_entry in the list is set to NULL, which terminates 37 | * the list. 38 | * 39 | * We represent a valid iterator that refers to an actual 40 | * name/value pair via a pointer to the pair's lh_entry 41 | * structure set as the iterator's opaque_ field. 42 | * 43 | * We follow json-c's current pair list representation by 44 | * representing a valid "end" iterator (one that refers past the 45 | * last pair) with a NULL value in the iterator's opaque_ field. 46 | * 47 | * A JSON Object without any pairs in it will have the "head" 48 | * field of its lh_table structure set to NULL. For such an 49 | * object, fjson_object_iter_begin will return an iterator with 50 | * the opaque_ field set to NULL, which is equivalent to the 51 | * "end" iterator. 52 | * 53 | * When iterating, we simply update the iterator's opaque_ field 54 | * to point to the next lh_entry structure in the linked list. 55 | * opaque_ will become NULL once we iterate past the last pair 56 | * in the list, which makes the iterator equivalent to the "end" 57 | * iterator. 58 | */ 59 | 60 | 61 | /** 62 | * **************************************************************************** 63 | */ 64 | struct fjson_object_iterator 65 | fjson_object_iter_begin(struct fjson_object *const __restrict__ obj) 66 | { 67 | struct fjson_object_iterator iter = { 68 | .objs_remain = 0, 69 | .curr_idx = 0, 70 | .pg = NULL 71 | }; 72 | 73 | if(obj->o_type == fjson_type_object) { 74 | iter.objs_remain = obj->o.c_obj.nelem; 75 | if(iter.objs_remain > 0) { 76 | iter.curr_idx = 0; 77 | iter.pg = &obj->o.c_obj.pg; 78 | /* check if first slot is empty, if so, advance */ 79 | if(iter.pg->children[0].k == NULL) { 80 | ++iter.objs_remain; /* correct _iter_next decrement */ 81 | fjson_object_iter_next(&iter); 82 | } 83 | } 84 | } 85 | return iter; 86 | } 87 | 88 | /** 89 | * **************************************************************************** 90 | */ 91 | struct fjson_object_iterator 92 | fjson_object_iter_end(const struct fjson_object __attribute__((unused)) *obj) 93 | { 94 | struct fjson_object_iterator iter = { 95 | .objs_remain = 0, 96 | .curr_idx = 0, 97 | .pg = NULL 98 | }; 99 | return iter; 100 | } 101 | 102 | /** 103 | * **************************************************************************** 104 | */ 105 | void 106 | fjson_object_iter_next(struct fjson_object_iterator *const __restrict__ iter) 107 | { 108 | JASSERT(NULL != iter); 109 | 110 | if(iter->objs_remain > 0) { 111 | --iter->objs_remain; 112 | if(iter->objs_remain > 0) { 113 | ++iter->curr_idx; 114 | if(iter->curr_idx == FJSON_OBJECT_CHLD_PG_SIZE) { 115 | iter->pg = iter->pg->next; 116 | iter->curr_idx = 0; 117 | } 118 | /* check empty slots; TODO: recurse or iterate? */ 119 | if(iter->pg->children[iter->curr_idx].k == NULL) { 120 | ++iter->objs_remain; /* correct */ 121 | fjson_object_iter_next(iter); 122 | } 123 | } 124 | } 125 | } 126 | 127 | 128 | /** 129 | * **************************************************************************** 130 | */ 131 | const char* 132 | fjson_object_iter_peek_name(const struct fjson_object_iterator *const __restrict__ iter) 133 | { 134 | JASSERT(NULL != iter); 135 | return iter->pg->children[iter->curr_idx].k; 136 | } 137 | 138 | 139 | /** 140 | * **************************************************************************** 141 | */ 142 | struct fjson_object* 143 | fjson_object_iter_peek_value(const struct fjson_object_iterator *const __restrict__ iter) 144 | { 145 | JASSERT(NULL != iter); 146 | return iter->pg->children[iter->curr_idx].v; 147 | } 148 | 149 | /** 150 | * **************************************************************************** 151 | */ 152 | struct _fjson_child* 153 | _fjson_object_iter_peek_child(const struct fjson_object_iterator *const __restrict__ iter) 154 | { 155 | JASSERT(NULL != iter); 156 | return (struct _fjson_child*) &(iter->pg->children[iter->curr_idx]); 157 | } 158 | 159 | 160 | /** 161 | * **************************************************************************** 162 | */ 163 | fjson_bool 164 | fjson_object_iter_equal(const struct fjson_object_iterator* iter1, 165 | const struct fjson_object_iterator* iter2) 166 | { 167 | int is_eq; 168 | JASSERT(NULL != iter1); 169 | JASSERT(NULL != iter2); 170 | 171 | if (iter1->objs_remain == iter2->objs_remain) { 172 | if (iter1->objs_remain == 0) { 173 | is_eq = 1; 174 | } else { 175 | if ( (iter1->curr_idx == iter2->curr_idx) && 176 | (iter1->pg == iter2->pg) ) { 177 | is_eq = 1; 178 | } else { 179 | is_eq = 0; 180 | } 181 | } 182 | } else { 183 | is_eq= 0; 184 | } 185 | 186 | return is_eq; 187 | } 188 | 189 | 190 | /** 191 | * **************************************************************************** 192 | */ 193 | struct fjson_object_iterator 194 | fjson_object_iter_init_default(void) 195 | { 196 | struct fjson_object_iterator iter; 197 | 198 | /** 199 | * @note Make this an invalid value, such that 200 | * accidental access to it would likely be trapped by the 201 | * hardware as an invalid address. 202 | */ 203 | iter.pg = NULL; 204 | iter.curr_idx = 0; 205 | iter.objs_remain = 1; 206 | 207 | return iter; 208 | } 209 | -------------------------------------------------------------------------------- /printbuf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. 3 | * Michael Clark 4 | * 5 | * This library is free software; you can redistribute it and/or modify 6 | * it under the terms of the MIT license. See COPYING for details. 7 | * 8 | * 9 | * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. 10 | * The copyrights to the contents of this file are licensed under the MIT License 11 | * (http://www.opensource.org/licenses/mit-license.php) 12 | */ 13 | 14 | #include "config.h" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #ifdef HAVE_STDARG_H 22 | # include 23 | #else /* !HAVE_STDARG_H */ 24 | # error Not enough var arg support! 25 | #endif /* HAVE_STDARG_H */ 26 | 27 | #include "json.h" 28 | #include "debug.h" 29 | #include "printbuf.h" 30 | 31 | static int printbuf_initial_size = 32; 32 | static int printbuf_extend(struct printbuf *p, int min_size); 33 | 34 | void fjson_global_set_printbuf_initial_size(int size) 35 | { 36 | printbuf_initial_size = size; 37 | } 38 | 39 | struct printbuf* printbuf_new(void) 40 | { 41 | struct printbuf *p; 42 | 43 | p = (struct printbuf*)malloc(sizeof(struct printbuf)); 44 | if(!p) return NULL; 45 | /* note: *ALL* data items must be initialized! */ 46 | p->size = printbuf_initial_size; 47 | p->bpos = 0; 48 | if(!(p->buf = (char*)malloc(p->size))) { 49 | free(p); 50 | return NULL; 51 | } 52 | return p; 53 | } 54 | 55 | 56 | /** 57 | * Extend the buffer p so it has a size of at least min_size. 58 | * 59 | * If the current size is large enough, nothing is changed. 60 | * 61 | * Note: this does not check the available space! The caller 62 | * is responsible for performing those calculations. 63 | */ 64 | static int printbuf_extend(struct printbuf *p, int min_size) 65 | { 66 | char *t; 67 | int new_size; 68 | 69 | if (p->size >= min_size) 70 | return 0; 71 | 72 | /* Prevent signed integer overflows with large buffers. */ 73 | if (min_size > INT_MAX - 8) 74 | return -1; 75 | if (p->size > INT_MAX / 2) 76 | new_size = min_size + 8; 77 | else { 78 | new_size = p->size * 2; 79 | if (new_size < min_size + 8) 80 | new_size = min_size + 8; 81 | } 82 | #ifdef PRINTBUF_DEBUG 83 | MC_DEBUG("printbuf_memappend: realloc " 84 | "bpos=%d min_size=%d old_size=%d new_size=%d\n", 85 | p->bpos, min_size, p->size, new_size); 86 | #endif /* PRINTBUF_DEBUG */ 87 | if(!(t = (char*)realloc(p->buf, new_size))) 88 | return -1; 89 | p->size = new_size; 90 | p->buf = t; 91 | return 0; 92 | } 93 | 94 | int printbuf_memappend(struct printbuf *p, const char *buf, int size) 95 | { 96 | /* Prevent signed integer overflows with large buffers. */ 97 | if (size > INT_MAX - p->bpos - 1) 98 | return -1; 99 | if (p->size <= p->bpos + size + 1) { 100 | if (printbuf_extend(p, p->bpos + size + 1) < 0) 101 | return -1; 102 | } 103 | if(size > 1) 104 | memcpy(p->buf + p->bpos, buf, size); 105 | else 106 | p->buf[p->bpos]= *buf; 107 | p->bpos += size; 108 | p->buf[p->bpos]= '\0'; 109 | return size; 110 | } 111 | 112 | /* same as printbuf_memappend(), but contains some performance enhancements */ 113 | void printbuf_memappend_no_nul(struct printbuf *p, const char *buf, const int size) 114 | { 115 | if (p->size <= p->bpos + size) { 116 | if (printbuf_extend(p, p->bpos + size) < 0) 117 | /* ignore new data, best we can do */ 118 | return; 119 | } 120 | memcpy(p->buf + p->bpos, buf, size); 121 | p->bpos += size; 122 | } 123 | 124 | /* add a single character to printbuf */ 125 | void printbuf_memappend_char(struct printbuf *p, const char c) 126 | { 127 | if (p->size <= p->bpos + 1) { 128 | if (printbuf_extend(p, p->bpos + 1) < 0) 129 | /* ignore new data, best we can do */ 130 | return; 131 | } 132 | p->buf[p->bpos++]= c; 133 | } 134 | 135 | void printbuf_terminate_string(struct printbuf *const p) 136 | { 137 | if (p->size <= p->bpos + 1) { 138 | if (printbuf_extend(p, p->bpos + 1) < 0) 139 | --p->bpos; /* overwrite last byte, best we can do */ 140 | } 141 | p->buf[p->bpos]= '\0'; 142 | } 143 | 144 | int printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len) 145 | { 146 | int size_needed; 147 | 148 | if (offset == -1) 149 | offset = pb->bpos; 150 | /* Prevent signed integer overflows with large buffers. */ 151 | if (len > INT_MAX - offset) 152 | return -1; 153 | size_needed = offset + len; 154 | if (pb->size < size_needed) 155 | { 156 | if (printbuf_extend(pb, size_needed) < 0) 157 | return -1; 158 | } 159 | 160 | memset(pb->buf + offset, charvalue, len); 161 | if (pb->bpos < size_needed) 162 | pb->bpos = size_needed; 163 | 164 | return 0; 165 | } 166 | 167 | #if !defined(HAVE_VSNPRINTF) /* !HAVE_VSNPRINTF */ 168 | # error Need vsnprintf! 169 | #endif /* !HAVE_VSNPRINTF */ 170 | 171 | #if !defined(HAVE_VASPRINTF) 172 | /* CAW: compliant version of vasprintf */ 173 | /* Note: on OpenCSW, we have vasprintf() inside the headers, but not inside the lib. 174 | * So we need to use a different name, else we get issues with redefinitions. We 175 | * we solve this by using the macro below, which just renames the function BUT 176 | * does not affect the (variadic) arguments. 177 | * rgerhards, 2017-04-11 178 | */ 179 | #define vasprintf rs_vasprintf 180 | #ifndef _AIX 181 | #pragma GCC diagnostic push 182 | #pragma GCC diagnostic ignored "-Wformat-nonliteral" 183 | #endif 184 | static int rs_vasprintf(char **buf, const char *fmt, va_list ap) 185 | { 186 | int chars; 187 | char *b; 188 | static char _T_emptybuffer = '\0'; 189 | 190 | if(!buf) { return -1; } 191 | 192 | /* CAW: RAWR! We have to hope to god here that vsnprintf doesn't overwrite 193 | our buffer like on some 64bit sun systems.... but hey, its time to move on */ 194 | chars = vsnprintf(&_T_emptybuffer, 0, fmt, ap)+1; 195 | if(chars < 0) { chars *= -1; } /* CAW: old glibc versions have this problem */ 196 | 197 | b = (char*)malloc(sizeof(char)*chars); 198 | if(!b) { return -1; } 199 | 200 | if((chars = vsprintf(b, fmt, ap)) < 0) { 201 | free(b); 202 | } else { 203 | *buf = b; 204 | } 205 | 206 | return chars; 207 | } 208 | #ifndef _AIX 209 | #pragma GCC diagnostic pop 210 | #endif 211 | #endif /* !HAVE_VASPRINTF */ 212 | 213 | int sprintbuf(struct printbuf *p, const char *msg, ...) 214 | { 215 | va_list ap; 216 | char *t; 217 | int size; 218 | char buf[128]; 219 | 220 | /* user stack buffer first */ 221 | va_start(ap, msg); 222 | size = vsnprintf(buf, 128, msg, ap); 223 | va_end(ap); 224 | /* if string is greater than stack buffer, then use dynamic string 225 | with vasprintf. Note: some implementation of vsnprintf return -1 226 | if output is truncated whereas some return the number of bytes that 227 | would have been written - this code handles both cases. */ 228 | if(size == -1 || size > 127) { 229 | va_start(ap, msg); 230 | if((size = vasprintf(&t, msg, ap)) < 0) { va_end(ap); return -1; } 231 | va_end(ap); 232 | printbuf_memappend(p, t, size); 233 | free(t); 234 | return size; 235 | } else { 236 | printbuf_memappend(p, buf, size); 237 | return size; 238 | } 239 | } 240 | 241 | void printbuf_reset(struct printbuf *p) 242 | { 243 | p->buf[0] = '\0'; 244 | p->bpos = 0; 245 | } 246 | 247 | void printbuf_free(struct printbuf *p) 248 | { 249 | if(p) { 250 | free(p->buf); 251 | free(p); 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /json_tokener.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. 3 | * Michael Clark 4 | * 5 | * This library is free software; you can redistribute it and/or modify 6 | * it under the terms of the MIT license. See COPYING for details. 7 | * 8 | */ 9 | 10 | #ifndef _fj_json_tokener_h_ 11 | #define _fj_json_tokener_h_ 12 | 13 | #include 14 | #include "json_object.h" 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | enum fjson_tokener_error { 21 | fjson_tokener_success, 22 | fjson_tokener_continue, 23 | fjson_tokener_error_depth, 24 | fjson_tokener_error_parse_eof, 25 | fjson_tokener_error_parse_unexpected, 26 | fjson_tokener_error_parse_null, 27 | fjson_tokener_error_parse_boolean, 28 | fjson_tokener_error_parse_number, 29 | fjson_tokener_error_parse_array, 30 | fjson_tokener_error_parse_object_key_name, 31 | fjson_tokener_error_parse_object_key_sep, 32 | fjson_tokener_error_parse_object_value_sep, 33 | fjson_tokener_error_parse_string, 34 | fjson_tokener_error_parse_comment, 35 | fjson_tokener_error_size 36 | }; 37 | 38 | enum fjson_tokener_state { 39 | fjson_tokener_state_eatws, 40 | fjson_tokener_state_start, 41 | fjson_tokener_state_finish, 42 | fjson_tokener_state_null, 43 | fjson_tokener_state_comment_start, 44 | fjson_tokener_state_comment, 45 | fjson_tokener_state_comment_eol, 46 | fjson_tokener_state_comment_end, 47 | fjson_tokener_state_string, 48 | fjson_tokener_state_string_escape, 49 | fjson_tokener_state_escape_unicode, 50 | fjson_tokener_state_boolean, 51 | fjson_tokener_state_number, 52 | fjson_tokener_state_array, 53 | fjson_tokener_state_array_add, 54 | fjson_tokener_state_array_sep, 55 | fjson_tokener_state_object_field_start, 56 | fjson_tokener_state_object_field, 57 | fjson_tokener_state_object_field_end, 58 | fjson_tokener_state_object_value, 59 | fjson_tokener_state_object_value_add, 60 | fjson_tokener_state_object_sep, 61 | fjson_tokener_state_array_after_sep, 62 | fjson_tokener_state_object_field_start_after_sep, 63 | fjson_tokener_state_inf 64 | }; 65 | 66 | struct fjson_tokener_srec 67 | { 68 | enum fjson_tokener_state state, saved_state; 69 | struct fjson_object *obj; 70 | struct fjson_object *current; 71 | char *obj_field_name; 72 | }; 73 | 74 | #define FJSON_TOKENER_DEFAULT_DEPTH 32 75 | 76 | struct fjson_tokener 77 | { 78 | char *str; 79 | struct printbuf *pb; 80 | int max_depth, depth, is_double, st_pos, char_offset; 81 | enum fjson_tokener_error err; 82 | unsigned int ucs_char; 83 | char quote_char; 84 | struct fjson_tokener_srec *stack; 85 | int flags; 86 | }; 87 | 88 | /** 89 | * Be strict when parsing JSON input. Use caution with 90 | * this flag as what is considered valid may become more 91 | * restrictive from one release to the next, causing your 92 | * code to fail on previously working input. 93 | * 94 | * This flag is not set by default. 95 | * 96 | * @see fjson_tokener_set_flags() 97 | */ 98 | #define FJSON_TOKENER_STRICT 0x01 99 | 100 | /** 101 | * Given an error previously returned by fjson_tokener_get_error(), 102 | * return a human readable description of the error. 103 | * 104 | * @return a generic error message is returned if an invalid error value is provided. 105 | */ 106 | const char *fjson_tokener_error_desc(enum fjson_tokener_error jerr); 107 | 108 | /** 109 | * Retrieve the error caused by the last call to fjson_tokener_parse_ex(), 110 | * or fjson_tokener_success if there is no error. 111 | * 112 | * When parsing a JSON string in pieces, if the tokener is in the middle 113 | * of parsing this will return fjson_tokener_continue. 114 | * 115 | * See also fjson_tokener_error_desc(). 116 | */ 117 | enum fjson_tokener_error fjson_tokener_get_error(struct fjson_tokener *tok); 118 | 119 | extern struct fjson_tokener* fjson_tokener_new(void); 120 | extern struct fjson_tokener* fjson_tokener_new_ex(int depth); 121 | extern void fjson_tokener_free(struct fjson_tokener *tok); 122 | extern void fjson_tokener_reset(struct fjson_tokener *tok); 123 | extern struct fjson_object* fjson_tokener_parse(const char *str); 124 | extern struct fjson_object* fjson_tokener_parse_verbose(const char *str, enum fjson_tokener_error *error); 125 | 126 | /** 127 | * Set flags that control how parsing will be done. 128 | */ 129 | extern void fjson_tokener_set_flags(struct fjson_tokener *tok, int flags); 130 | 131 | /** 132 | * Parse a string and return a non-NULL fjson_object if a valid JSON value 133 | * is found. The string does not need to be a JSON object or array; 134 | * it can also be a string, number or boolean value. 135 | * 136 | * A partial JSON string can be parsed. If the parsing is incomplete, 137 | * NULL will be returned and fjson_tokener_get_error() will be return 138 | * fjson_tokener_continue. 139 | * fjson_tokener_parse_ex() can then be called with additional bytes in str 140 | * to continue the parsing. 141 | * 142 | * If fjson_tokener_parse_ex() returns NULL and the error anything other than 143 | * fjson_tokener_continue, a fatal error has occurred and parsing must be 144 | * halted. Then tok object must not be re-used until fjson_tokener_reset() is 145 | * called. 146 | * 147 | * When a valid JSON value is parsed, a non-NULL fjson_object will be 148 | * returned. Also, fjson_tokener_get_error() will return fjson_tokener_success. 149 | * Be sure to check the type with fjson_object_is_type() or 150 | * fjson_object_get_type() before using the object. 151 | * 152 | * @b XXX this shouldn't use internal fields: 153 | * Trailing characters after the parsed value do not automatically cause an 154 | * error. It is up to the caller to decide whether to treat this as an 155 | * error or to handle the additional characters, perhaps by parsing another 156 | * json value starting from that point. 157 | * 158 | * Extra characters can be detected by comparing the tok->char_offset against 159 | * the length of the last len parameter passed in. 160 | * 161 | * The tokener does \b not maintain an internal buffer so the caller is 162 | * responsible for calling fjson_tokener_parse_ex with an appropriate str 163 | * parameter starting with the extra characters. 164 | * 165 | * This interface is presently not 64-bit clean due to the int len argument 166 | * so the function limits the maximum string size to INT32_MAX (2GB). 167 | * If the function is called with len == -1 then strlen is called to check 168 | * the string length is less than INT32_MAX (2GB) 169 | * 170 | * Example: 171 | * @code 172 | fjson_object *jobj = NULL; 173 | const char *mystring = NULL; 174 | int stringlen = 0; 175 | enum fjson_tokener_error jerr; 176 | do { 177 | mystring = ... // get JSON string, e.g. read from file, etc... 178 | stringlen = strlen(mystring); 179 | jobj = fjson_tokener_parse_ex(tok, mystring, stringlen); 180 | } while ((jerr = fjson_tokener_get_error(tok)) == fjson_tokener_continue); 181 | if (jerr != fjson_tokener_success) 182 | { 183 | fprintf(stderr, "Error: %s\n", fjson_tokener_error_desc(jerr)); 184 | // Handle errors, as appropriate for your application. 185 | } 186 | if (tok->char_offset < stringlen) // XXX shouldn't access internal fields 187 | { 188 | // Handle extra characters after parsed object as desired. 189 | // e.g. issue an error, parse another object from that point, etc... 190 | } 191 | // Success, use jobj here. 192 | 193 | @endcode 194 | * 195 | * @param tok a fjson_tokener previously allocated with fjson_tokener_new() 196 | * @param str an string with any valid JSON expression, or portion of. This does not need to be null terminated. 197 | * @param len the length of str 198 | */ 199 | extern struct fjson_object* fjson_tokener_parse_ex(struct fjson_tokener *tok, 200 | const char *str, int len); 201 | 202 | #ifndef FJSON_NATIVE_API_ONLY 203 | #define json_tokener fjson_tokener 204 | #define json_tokener_error fjson_tokener_error 205 | extern const char* fjson_tokener_errors[15]; 206 | #define json_tokener_errors fjson_tokener_errors 207 | #define json_tokener_continue fjson_tokener_continue 208 | #define json_tokener_reset fjson_tokener_reset 209 | 210 | #define json_tokener_new() fjson_tokener_new() 211 | #define json_tokener_parse fjson_tokener_parse 212 | #define json_tokener_parse_ex(a, b, c) fjson_tokener_parse_ex((a), (b), (c)) 213 | #define json_tokener_free(a) fjson_tokener_free((a)) 214 | #define json_tokener_error_desc(a) fjson_tokener_error_desc((a)) 215 | #endif 216 | 217 | #ifdef __cplusplus 218 | } 219 | #endif 220 | 221 | #endif 222 | -------------------------------------------------------------------------------- /json_util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. 3 | * Michael Clark 4 | * Copyright (c) 2016 Adiscon GmbH 5 | * Rainer Gerhards 6 | * 7 | * This library is free software; you can redistribute it and/or modify 8 | * it under the terms of the MIT license. See COPYING for details. 9 | * 10 | */ 11 | 12 | #include "config.h" 13 | #undef realloc 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #ifdef HAVE_SYS_TYPES_H 25 | #include 26 | #endif /* HAVE_SYS_TYPES_H */ 27 | 28 | #ifdef HAVE_SYS_STAT_H 29 | #include 30 | #endif /* HAVE_SYS_STAT_H */ 31 | 32 | #ifdef HAVE_FCNTL_H 33 | #include 34 | #endif /* HAVE_FCNTL_H */ 35 | 36 | #ifdef HAVE_UNISTD_H 37 | # include 38 | #endif /* HAVE_UNISTD_H */ 39 | 40 | #if !defined(HAVE_SNPRINTF) 41 | # error You do not have snprintf on your system. 42 | #endif /* HAVE_SNPRINTF */ 43 | 44 | #include "debug.h" 45 | #include "printbuf.h" 46 | #include "json_object.h" 47 | #include "json_tokener.h" 48 | #include "json_util.h" 49 | 50 | static int sscanf_is_broken = 0; 51 | static int sscanf_is_broken_testdone = 0; 52 | static void sscanf_is_broken_test(void); 53 | 54 | /* 55 | * Create a JSON object from already opened file descriptor. 56 | * 57 | * This function can be helpful, when you opened the file already, 58 | * e.g. when you have a temp file. 59 | * Note, that the fd must be readable at the actual position, i.e. 60 | * use lseek(fd, 0, SEEK_SET) before. 61 | */ 62 | struct fjson_object* fjson_object_from_fd(int fd) 63 | { 64 | struct printbuf *pb; 65 | struct fjson_object *obj; 66 | char buf[FJSON_FILE_BUF_SIZE]; 67 | int ret; 68 | 69 | if(!(pb = printbuf_new())) { 70 | MC_ERROR("fjson_object_from_file: printbuf_new failed\n"); 71 | return NULL; 72 | } 73 | while((ret = read(fd, buf, FJSON_FILE_BUF_SIZE)) > 0) { 74 | printbuf_memappend(pb, buf, ret); 75 | } 76 | if(ret < 0) { 77 | MC_ERROR("fjson_object_from_fd: error reading fd %d: %s\n", fd, strerror(errno)); 78 | printbuf_free(pb); 79 | return NULL; 80 | } 81 | obj = fjson_tokener_parse(pb->buf); 82 | printbuf_free(pb); 83 | return obj; 84 | } 85 | 86 | struct fjson_object* fjson_object_from_file(const char *filename) 87 | { 88 | struct fjson_object *obj; 89 | int fd; 90 | 91 | if((fd = open(filename, O_RDONLY)) < 0) { 92 | MC_ERROR("fjson_object_from_file: error opening file %s: %s\n", 93 | filename, strerror(errno)); 94 | return NULL; 95 | } 96 | obj = fjson_object_from_fd(fd); 97 | close(fd); 98 | return obj; 99 | } 100 | 101 | /* extended "format and write to file" function */ 102 | 103 | int fjson_object_to_file_ext(const char *filename, struct fjson_object *obj, int flags) 104 | { 105 | const char *fjson_str; 106 | int fd, ret; 107 | unsigned int wpos, wsize; 108 | 109 | if(!obj) { 110 | MC_ERROR("fjson_object_to_file: object is null\n"); 111 | return -1; 112 | } 113 | 114 | if((fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0) { 115 | MC_ERROR("fjson_object_to_file: error opening file %s: %s\n", 116 | filename, strerror(errno)); 117 | return -1; 118 | } 119 | 120 | if(!(fjson_str = fjson_object_to_json_string_ext(obj,flags))) { 121 | close(fd); 122 | return -1; 123 | } 124 | 125 | wsize = (unsigned int)(strlen(fjson_str) & UINT_MAX); /* CAW: probably unnecessary, but the most 64bit safe */ 126 | wpos = 0; 127 | while(wpos < wsize) { 128 | if((ret = write(fd, fjson_str + wpos, wsize-wpos)) < 0) { 129 | close(fd); 130 | MC_ERROR("fjson_object_to_file: error writing file %s: %s\n", 131 | filename, strerror(errno)); 132 | return -1; 133 | } 134 | 135 | /* because of the above check for ret < 0, we can safely cast and add */ 136 | wpos += (unsigned int)ret; 137 | } 138 | 139 | close(fd); 140 | return 0; 141 | } 142 | 143 | // backwards compatible "format and write to file" function 144 | 145 | int fjson_object_to_file(const char *filename, struct fjson_object *obj) 146 | { 147 | return fjson_object_to_file_ext(filename, obj, FJSON_TO_STRING_PLAIN); 148 | } 149 | 150 | int fjson_parse_double(const char *buf, double *retval) 151 | { 152 | return (sscanf(buf, "%lf", retval)==1 ? 0 : 1); 153 | } 154 | 155 | /* 156 | * Not all implementations of sscanf actually work properly. 157 | * Check whether the one we're currently using does, and if 158 | * it's broken, enable the workaround code. 159 | */ 160 | static void sscanf_is_broken_test(void) 161 | { 162 | int64_t num64; 163 | int ret_errno, is_int64_min, ret_errno2, is_int64_max; 164 | 165 | (void)sscanf(" -01234567890123456789012345", "%" SCNd64, &num64); 166 | ret_errno = errno; 167 | is_int64_min = (num64 == INT64_MIN); 168 | 169 | (void)sscanf(" 01234567890123456789012345", "%" SCNd64, &num64); 170 | ret_errno2 = errno; 171 | is_int64_max = (num64 == INT64_MAX); 172 | 173 | if (ret_errno != ERANGE || !is_int64_min || 174 | ret_errno2 != ERANGE || !is_int64_max) 175 | { 176 | MC_DEBUG("sscanf_is_broken_test failed, enabling workaround code\n"); 177 | sscanf_is_broken = 1; 178 | } 179 | } 180 | 181 | int fjson_parse_int64(const char *buf, int64_t *retval) 182 | { 183 | int64_t num64; 184 | const char *buf_sig_digits; 185 | int orig_has_neg; 186 | int saved_errno; 187 | 188 | if (!sscanf_is_broken_testdone) 189 | { 190 | sscanf_is_broken_test(); 191 | sscanf_is_broken_testdone = 1; 192 | } 193 | 194 | // Skip leading spaces 195 | while (isspace((int)*buf) && *buf) 196 | buf++; 197 | 198 | errno = 0; // sscanf won't always set errno, so initialize 199 | if (sscanf(buf, "%" SCNd64, &num64) != 1) 200 | { 201 | MC_DEBUG("Failed to parse, sscanf != 1\n"); 202 | return 1; 203 | } 204 | 205 | saved_errno = errno; 206 | buf_sig_digits = buf; 207 | orig_has_neg = 0; 208 | if (*buf_sig_digits == '-') 209 | { 210 | buf_sig_digits++; 211 | orig_has_neg = 1; 212 | } 213 | 214 | // Not all sscanf implementations actually work 215 | if (sscanf_is_broken && saved_errno != ERANGE) 216 | { 217 | char buf_cmp[100]; 218 | char *buf_cmp_start = buf_cmp; 219 | int recheck_has_neg = 0; 220 | int buf_cmp_len; 221 | 222 | // Skip leading zeros, but keep at least one digit 223 | while (buf_sig_digits[0] == '0' && buf_sig_digits[1] != '\0') 224 | buf_sig_digits++; 225 | if (num64 == 0) // assume all sscanf impl's will parse -0 to 0 226 | orig_has_neg = 0; // "-0" is the same as just plain "0" 227 | 228 | snprintf(buf_cmp_start, sizeof(buf_cmp), "%" PRId64, num64); 229 | if (*buf_cmp_start == '-') 230 | { 231 | recheck_has_neg = 1; 232 | buf_cmp_start++; 233 | } 234 | // No need to skip leading spaces or zeros here. 235 | 236 | buf_cmp_len = strlen(buf_cmp_start); 237 | /** 238 | * If the sign is different, or 239 | * some of the digits are different, or 240 | * there is another digit present in the original string 241 | * then we have NOT successfully parsed the value. 242 | */ 243 | if (orig_has_neg != recheck_has_neg || 244 | strncmp(buf_sig_digits, buf_cmp_start, strlen(buf_cmp_start)) != 0 || 245 | ((int)strlen(buf_sig_digits) != buf_cmp_len && 246 | isdigit((int)buf_sig_digits[buf_cmp_len]) 247 | ) 248 | ) 249 | { 250 | saved_errno = ERANGE; 251 | } 252 | } 253 | 254 | // Not all sscanf impl's set the value properly when out of range. 255 | // Always do this, even for properly functioning implementations, 256 | // since it shouldn't slow things down much. 257 | if (saved_errno == ERANGE) 258 | { 259 | if (orig_has_neg) 260 | num64 = INT64_MIN; 261 | else 262 | num64 = INT64_MAX; 263 | } 264 | *retval = num64; 265 | return 0; 266 | } 267 | 268 | #define NELEM(a) (sizeof(a) / sizeof(a[0])) 269 | static const char* fjson_type_name[] = { 270 | /* If you change this, be sure to update the enum fjson_type definition too */ 271 | "null", 272 | "boolean", 273 | "double", 274 | "int", 275 | "object", 276 | "array", 277 | "string", 278 | }; 279 | 280 | const char *fjson_type_to_name(enum fjson_type o_type) 281 | { 282 | int o_type_int = (int)o_type; 283 | if (o_type_int < 0 || o_type_int >= (int)NELEM(fjson_type_name)) 284 | { 285 | MC_ERROR("fjson_type_to_name: type %d is out of range [0,%zu]\n", o_type, NELEM(fjson_type_name)); 286 | return NULL; 287 | } 288 | return fjson_type_name[o_type]; 289 | } 290 | 291 | -------------------------------------------------------------------------------- /atomic.h: -------------------------------------------------------------------------------- 1 | /* This header supplies atomic operations. So far, we rely on GCC's 2 | * atomic builtins. During configure, we check if atomic operatons are 3 | * available. If they are not, I am making the necessary provisioning to live without them if 4 | * they are not available. Please note that you should only use the macros 5 | * here if you think you can actually live WITHOUT an explicit atomic operation, 6 | * because in the non-presence of them, we simply do it without atomicitiy. 7 | * Which, for word-aligned data types, usually (but only usually!) should work. 8 | * 9 | * We are using the functions described in 10 | * http:/gcc.gnu.org/onlinedocs/gcc/Atomic-Builtins.html 11 | * 12 | * THESE MACROS MUST ONLY BE USED WITH WORD-SIZED DATA TYPES! 13 | * 14 | * Note: this file was obtained at 2015-12-16 from the rsyslog project. 15 | * 16 | * Copyright 2008-2015 Rainer Gerhards and Adiscon GmbH. 17 | * 18 | * This file is part of the rsyslog runtime library. 19 | * 20 | * Licensed under the Apache License, Version 2.0 (the "License"); 21 | * you may not use this file except in compliance with the License. 22 | * You may obtain a copy of the License at 23 | * 24 | * http://www.apache.org/licenses/LICENSE-2.0 25 | * -or- 26 | * see COPYING.ASL20 in the source distribution 27 | * 28 | * Unless required by applicable law or agreed to in writing, software 29 | * distributed under the License is distributed on an "AS IS" BASIS, 30 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 31 | * See the License for the specific language governing permissions and 32 | * limitations under the License. 33 | */ 34 | #ifndef FJ_INCLUDED_ATOMIC_H 35 | #define FJ_INCLUDED_ATOMIC_H 36 | 37 | #ifdef HAVE_ATOMIC_BUILTINS 38 | # define ATOMIC_SUB(data, val, phlpmut) __sync_fetch_and_sub(data, val) 39 | # define ATOMIC_ADD(data, val) __sync_fetch_and_add(&(data), val) 40 | # define ATOMIC_INC(data, phlpmut) ((void) __sync_fetch_and_add(data, 1)) 41 | # define ATOMIC_INC_AND_FETCH_int(data, phlpmut) __sync_fetch_and_add(data, 1) 42 | # define ATOMIC_INC_AND_FETCH_unsigned(data, phlpmut) __sync_fetch_and_add(data, 1) 43 | # define ATOMIC_DEC(data, phlpmut) ((void) __sync_sub_and_fetch(data, 1)) 44 | # define ATOMIC_DEC_AND_FETCH(data, phlpmut) __sync_sub_and_fetch(data, 1) 45 | # define ATOMIC_FETCH_32BIT(data, phlpmut) ((unsigned) __sync_fetch_and_and(data, 0xffffffff)) 46 | # define ATOMIC_STORE_1_TO_32BIT(data) __sync_lock_test_and_set(&(data), 1) 47 | # define ATOMIC_STORE_0_TO_INT(data, phlpmut) __sync_fetch_and_and(data, 0) 48 | # define ATOMIC_STORE_1_TO_INT(data, phlpmut) __sync_fetch_and_or(data, 1) 49 | # define ATOMIC_STORE_INT_TO_INT(data, val) __sync_fetch_and_or(&(data), (val)) 50 | # define ATOMIC_CAS(data, oldVal, newVal, phlpmut) __sync_bool_compare_and_swap(data, (oldVal), (newVal)) 51 | # define ATOMIC_CAS_time_t(data, oldVal, newVal, phlpmut) __sync_bool_compare_and_swap(data, (oldVal), (newVal)) 52 | # define ATOMIC_CAS_VAL(data, oldVal, newVal, phlpmut) __sync_val_compare_and_swap(data, (oldVal), (newVal)); 53 | 54 | /* functions below are not needed if we have atomics */ 55 | # define DEF_ATOMIC_HELPER_MUT(x) 56 | # define INIT_ATOMIC_HELPER_MUT(x) 57 | # define DESTROY_ATOMIC_HELPER_MUT(x) 58 | 59 | /* the following operations should preferrably be done atomic, but it is 60 | * not fatal if not -- that means we can live with some missed updates. So be 61 | * sure to use these macros only if that really does not matter! 62 | */ 63 | # define PREFER_ATOMIC_INC(data) ((void) __sync_fetch_and_add(&(data), 1)) 64 | #else 65 | /* note that we gained parctical proof that theoretical problems DO occur 66 | * if we do not properly address them. See this blog post for details: 67 | * http://blog.gerhards.net/2009/01/rsyslog-data-race-analysis.html 68 | * The bottom line is that if there are no atomics available, we should NOT 69 | * simply go ahead and do without them - use mutexes or other things. 70 | * rgerhards, 2009-01-30 71 | */ 72 | #include 73 | # define ATOMIC_INC(data, phlpmut) { \ 74 | pthread_mutex_lock(phlpmut); \ 75 | ++(*(data)); \ 76 | pthread_mutex_unlock(phlpmut); \ 77 | } 78 | 79 | # define ATOMIC_STORE_0_TO_INT(data, hlpmut) { \ 80 | pthread_mutex_lock(hlpmut); \ 81 | *(data) = 0; \ 82 | pthread_mutex_unlock(hlpmut); \ 83 | } 84 | 85 | # define ATOMIC_STORE_1_TO_INT(data, hlpmut) { \ 86 | pthread_mutex_lock(hlpmut); \ 87 | *(data) = 1; \ 88 | pthread_mutex_unlock(hlpmut); \ 89 | } 90 | 91 | static inline int 92 | ATOMIC_CAS(int *data, int oldVal, int newVal, pthread_mutex_t *phlpmut) { 93 | int bSuccess; 94 | pthread_mutex_lock(phlpmut); 95 | if(*data == oldVal) { 96 | *data = newVal; 97 | bSuccess = 1; 98 | } else { 99 | bSuccess = 0; 100 | } 101 | pthread_mutex_unlock(phlpmut); 102 | return(bSuccess); 103 | } 104 | 105 | static inline int 106 | ATOMIC_CAS_time_t(time_t *data, time_t oldVal, time_t newVal, pthread_mutex_t *phlpmut) { 107 | int bSuccess; 108 | pthread_mutex_lock(phlpmut); 109 | if(*data == oldVal) { 110 | *data = newVal; 111 | bSuccess = 1; 112 | } else { 113 | bSuccess = 0; 114 | } 115 | pthread_mutex_unlock(phlpmut); 116 | return(bSuccess); 117 | } 118 | 119 | 120 | static inline int 121 | ATOMIC_CAS_VAL(int *data, int oldVal, int newVal, pthread_mutex_t *phlpmut) { 122 | int val; 123 | pthread_mutex_lock(phlpmut); 124 | if(*data == oldVal) { 125 | *data = newVal; 126 | } 127 | val = *data; 128 | pthread_mutex_unlock(phlpmut); 129 | return(val); 130 | } 131 | 132 | # define ATOMIC_DEC(data, phlpmut) { \ 133 | pthread_mutex_lock(phlpmut); \ 134 | --(*(data)); \ 135 | pthread_mutex_unlock(phlpmut); \ 136 | } 137 | 138 | static inline int 139 | ATOMIC_INC_AND_FETCH_int(int *data, pthread_mutex_t *phlpmut) { 140 | int val; 141 | pthread_mutex_lock(phlpmut); 142 | val = ++(*data); 143 | pthread_mutex_unlock(phlpmut); 144 | return(val); 145 | } 146 | 147 | static inline unsigned 148 | ATOMIC_INC_AND_FETCH_unsigned(unsigned *data, pthread_mutex_t *phlpmut) { 149 | unsigned val; 150 | pthread_mutex_lock(phlpmut); 151 | val = ++(*data); 152 | pthread_mutex_unlock(phlpmut); 153 | return(val); 154 | } 155 | 156 | static inline int 157 | ATOMIC_DEC_AND_FETCH(int *data, pthread_mutex_t *phlpmut) { 158 | int val; 159 | pthread_mutex_lock(phlpmut); 160 | val = --(*data); 161 | pthread_mutex_unlock(phlpmut); 162 | return(val); 163 | } 164 | 165 | static inline int 166 | ATOMIC_FETCH_32BIT(int *data, pthread_mutex_t *phlpmut) { 167 | int val; 168 | pthread_mutex_lock(phlpmut); 169 | val = (*data); 170 | pthread_mutex_unlock(phlpmut); 171 | return(val); 172 | } 173 | 174 | static inline void 175 | ATOMIC_SUB(int *data, int val, pthread_mutex_t *phlpmut) { 176 | pthread_mutex_lock(phlpmut); 177 | (*data) -= val; 178 | pthread_mutex_unlock(phlpmut); 179 | } 180 | # define DEF_ATOMIC_HELPER_MUT(x) pthread_mutex_t x; 181 | # define INIT_ATOMIC_HELPER_MUT(x) pthread_mutex_init(&(x), NULL); 182 | # define DESTROY_ATOMIC_HELPER_MUT(x) pthread_mutex_destroy(&(x)); 183 | 184 | # define PREFER_ATOMIC_INC(data) ((void) ++data) 185 | 186 | #endif 187 | 188 | /* we need to handle 64bit atomics seperately as some platforms have 189 | * 32 bit atomics, but not 64 bit ones... -- rgerhards, 2010-12-01 190 | */ 191 | #if 0 /* currently disabled, we don't need it now and dont' have the data types present */ 192 | #ifdef HAVE_ATOMIC_BUILTINS64 193 | # define ATOMIC_INC_uint64(data, phlpmut) ((void) __sync_fetch_and_add(data, 1)) 194 | # define ATOMIC_DEC_unit64(data, phlpmut) ((void) __sync_sub_and_fetch(data, 1)) 195 | # define ATOMIC_INC_AND_FETCH_uint64(data, phlpmut) __sync_fetch_and_add(data, 1) 196 | 197 | # define DEF_ATOMIC_HELPER_MUT64(x) 198 | # define INIT_ATOMIC_HELPER_MUT64(x) 199 | # define DESTROY_ATOMIC_HELPER_MUT64(x) 200 | #else 201 | # define ATOMIC_INC_uint64(data, phlpmut) { \ 202 | pthread_mutex_lock(phlpmut); \ 203 | ++(*(data)); \ 204 | pthread_mutex_unlock(phlpmut); \ 205 | } 206 | # define ATOMIC_DEC_uint64(data, phlpmut) { \ 207 | pthread_mutex_lock(phlpmut); \ 208 | --(*(data)); \ 209 | pthread_mutex_unlock(phlpmut); \ 210 | } 211 | 212 | static inline unsigned 213 | ATOMIC_INC_AND_FETCH_uint64(uint64 *data, pthread_mutex_t *phlpmut) { 214 | uint64 val; 215 | pthread_mutex_lock(phlpmut); 216 | val = ++(*data); 217 | pthread_mutex_unlock(phlpmut); 218 | return(val); 219 | } 220 | 221 | # define DEF_ATOMIC_HELPER_MUT64(x) pthread_mutex_t x; 222 | # define INIT_ATOMIC_HELPER_MUT64(x) pthread_mutex_init(&(x), NULL) 223 | # define DESTROY_ATOMIC_HELPER_MUT64(x) pthread_mutex_destroy(&(x)) 224 | #endif /* #ifdef HAVE_ATOMIC_BUILTINS64 */ 225 | #endif 226 | 227 | #endif /* #ifndef INCLUDED_ATOMIC_H */ 228 | -------------------------------------------------------------------------------- /json_object_iterator.h: -------------------------------------------------------------------------------- 1 | /** 2 | ******************************************************************************* 3 | * @file fjson_object_iterator.h 4 | * 5 | * Copyright (c) 2009-2012 Hewlett-Packard Development Company, L.P. 6 | * 7 | * This library is free software; you can redistribute it and/or modify 8 | * it under the terms of the MIT license. See COPYING for details. 9 | * 10 | * @brief json-c forces clients to use its private data 11 | * structures for JSON Object iteration. This API 12 | * corrects that by abstracting the private json-c 13 | * details. 14 | * 15 | * API attributes:
16 | * * Thread-safe: NO
17 | * * Reentrant: NO 18 | * 19 | ******************************************************************************* 20 | */ 21 | 22 | 23 | #ifndef FJ_JSON_OBJECT_ITERATOR_H 24 | #define FJ_JSON_OBJECT_ITERATOR_H 25 | 26 | #include 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | /** 33 | * Forward declaration for the opaque iterator information. 34 | */ 35 | struct fjson_object_iter_info_; 36 | 37 | /** 38 | * The opaque iterator that references a name/value pair within 39 | * a JSON Object instance or the "end" iterator value. 40 | */ 41 | struct fjson_object_iterator { 42 | int objs_remain; 43 | int curr_idx; 44 | const struct _fjson_child_pg *pg; 45 | }; 46 | 47 | 48 | /** 49 | * forward declaration of json-c's JSON value instance structure 50 | */ 51 | struct fjson_object; 52 | 53 | 54 | /** 55 | * Initializes an iterator structure to a "default" value that 56 | * is convenient for initializing an iterator variable to a 57 | * default state (e.g., initialization list in a class' 58 | * constructor). 59 | * 60 | * @code 61 | * struct fjson_object_iterator iter = fjson_object_iter_init_default(); 62 | * MyClass() : iter_(fjson_object_iter_init_default()) 63 | * @endcode 64 | * 65 | * @note The initialized value doesn't reference any specific 66 | * pair, is considered an invalid iterator, and MUST NOT 67 | * be passed to any json-c API that expects a valid 68 | * iterator. 69 | * 70 | * @note User and internal code MUST NOT make any assumptions 71 | * about and dependencies on the value of the "default" 72 | * iterator value. 73 | * 74 | * @return fjson_object_iterator 75 | */ 76 | struct fjson_object_iterator 77 | fjson_object_iter_init_default(void); 78 | 79 | /** Retrieves an iterator to the first pair of the JSON Object. 80 | * 81 | * @warning Any modification of the underlying pair invalidates all 82 | * iterators to that pair. 83 | * 84 | * @param obj JSON Object instance (MUST be of type fjson_object) 85 | * 86 | * @return fjson_object_iterator If the JSON Object has at 87 | * least one pair, on return, the iterator refers 88 | * to the first pair. If the JSON Object doesn't 89 | * have any pairs, the returned iterator is 90 | * equivalent to the "end" iterator for the same 91 | * JSON Object instance. 92 | * 93 | * @code 94 | * struct fjson_object_iterator it; 95 | * struct fjson_object_iterator itEnd; 96 | * struct fjson_object* obj; 97 | * 98 | * obj = fjson_tokener_parse("{'first':'george', 'age':100}"); 99 | * it = fjson_object_iter_begin(obj); 100 | * itEnd = fjson_object_iter_end(obj); 101 | * 102 | * while (!fjson_object_iter_equal(&it, &itEnd)) { 103 | * printf("%s\n", 104 | * fjson_object_iter_peek_name(&it)); 105 | * fjson_object_iter_next(&it); 106 | * } 107 | * 108 | * @endcode 109 | */ 110 | struct fjson_object_iterator 111 | fjson_object_iter_begin(struct fjson_object* obj); 112 | 113 | /** Retrieves the iterator that represents the position beyond the 114 | * last pair of the given JSON Object instance. 115 | * 116 | * @warning Do NOT write code that assumes that the "end" 117 | * iterator value is NULL, even if it is so in a 118 | * particular instance of the implementation. 119 | * 120 | * @note The reason we do not (and MUST NOT) provide 121 | * "fjson_object_iter_is_end(fjson_object_iterator* iter)" 122 | * type of API is because it would limit the underlying 123 | * representation of name/value containment (or force us 124 | * to add additional, otherwise unnecessary, fields to 125 | * the iterator structure). The "end" iterator and the 126 | * equality test method, on the other hand, permit us to 127 | * cleanly abstract pretty much any reasonable underlying 128 | * representation without burdening the iterator 129 | * structure with unnecessary data. 130 | * 131 | * @note For performance reasons, memorize the "end" iterator prior 132 | * to any loop. 133 | * 134 | * @param obj JSON Object instance (MUST be of type fjson_object) 135 | * 136 | * @return fjson_object_iterator On return, the iterator refers 137 | * to the "end" of the Object instance's pairs 138 | * (i.e., NOT the last pair, but "beyond the last 139 | * pair" value) 140 | */ 141 | struct fjson_object_iterator 142 | fjson_object_iter_end(const struct fjson_object* obj); 143 | 144 | /** Returns an iterator to the next pair, if any 145 | * 146 | * @warning Any modification of the underlying pair 147 | * invalidates all iterators to that pair. 148 | * 149 | * @param iter [IN/OUT] Pointer to iterator that references a 150 | * name/value pair; MUST be a valid, non-end iterator. 151 | * WARNING: bad things will happen if invalid or "end" 152 | * iterator is passed. Upon return will contain the 153 | * reference to the next pair if there is one; if there 154 | * are no more pairs, will contain the "end" iterator 155 | * value, which may be compared against the return value 156 | * of fjson_object_iter_end() for the same JSON Object 157 | * instance. 158 | */ 159 | void 160 | fjson_object_iter_next(struct fjson_object_iterator* iter); 161 | 162 | 163 | /** Returns a const pointer to the name of the pair referenced 164 | * by the given iterator. 165 | * 166 | * @param iter pointer to iterator that references a name/value 167 | * pair; MUST be a valid, non-end iterator. 168 | * 169 | * @warning bad things will happen if an invalid or 170 | * "end" iterator is passed. 171 | * 172 | * @return const char* Pointer to the name of the referenced 173 | * name/value pair. The name memory belongs to the 174 | * name/value pair, will be freed when the pair is 175 | * deleted or modified, and MUST NOT be modified or 176 | * freed by the user. 177 | */ 178 | const char* 179 | fjson_object_iter_peek_name(const struct fjson_object_iterator* iter); 180 | 181 | 182 | /** Returns a pointer to the json-c instance representing the 183 | * value of the referenced name/value pair, without altering 184 | * the instance's reference count. 185 | * 186 | * @param iter pointer to iterator that references a name/value 187 | * pair; MUST be a valid, non-end iterator. 188 | * 189 | * @warning bad things will happen if invalid or 190 | * "end" iterator is passed. 191 | * 192 | * @return struct fjson_object* Pointer to the json-c value 193 | * instance of the referenced name/value pair; the 194 | * value's reference count is not changed by this 195 | * function: if you plan to hold on to this json-c node, 196 | * take a look at fjson_object_get() and 197 | * fjson_object_put(). IMPORTANT: json-c API represents 198 | * the JSON Null value as a NULL fjson_object instance 199 | * pointer. 200 | */ 201 | struct fjson_object* 202 | fjson_object_iter_peek_value(const struct fjson_object_iterator* iter); 203 | 204 | 205 | /** Tests two iterators for equality. Typically used to test 206 | * for end of iteration by comparing an iterator to the 207 | * corresponding "end" iterator (that was derived from the same 208 | * JSON Object instance). 209 | * 210 | * @note The reason we do not (and MUST NOT) provide 211 | * "fjson_object_iter_is_end(fjson_object_iterator* iter)" 212 | * type of API is because it would limit the underlying 213 | * representation of name/value containment (or force us 214 | * to add additional, otherwise unnecessary, fields to 215 | * the iterator structure). The equality test method, on 216 | * the other hand, permits us to cleanly abstract pretty 217 | * much any reasonable underlying representation. 218 | * 219 | * @param iter1 Pointer to first valid, non-NULL iterator 220 | * @param iter2 POinter to second valid, non-NULL iterator 221 | * 222 | * @warning if a NULL iterator pointer or an uninitialized 223 | * or invalid iterator, or iterators derived from 224 | * different JSON Object instances are passed, bad things 225 | * will happen! 226 | * 227 | * @return fjson_bool non-zero if iterators are equal (i.e., both 228 | * reference the same name/value pair or are both at 229 | * "end"); zero if they are not equal. 230 | */ 231 | fjson_bool 232 | fjson_object_iter_equal(const struct fjson_object_iterator* iter1, 233 | const struct fjson_object_iterator* iter2); 234 | 235 | /* some private functions -- TODO: move to their own header */ 236 | struct _fjson_child* 237 | _fjson_object_iter_peek_child(const struct fjson_object_iterator *const __restrict__ iter); 238 | 239 | 240 | #ifndef FJSON_NATIVE_API_ONLY 241 | #define json_object_iter_info_ fjson_object_iter_info_ 242 | #define json_object_iterator fjson_object_iterator 243 | #define json_object_iter_init_default fjson_object_iter_init_default 244 | #define json_object_iter_begin fjson_object_iter_begin 245 | #define json_object_iter_end fjson_object_iter_end 246 | #define json_object_iter_next fjson_object_iter_next 247 | #define json_object_iter_peek_name fjson_object_iter_peek_name 248 | #define json_object_iter_peek_value fjson_object_iter_peek_value 249 | #define json_object_iter_equal fjson_object_iter_equal 250 | #endif 251 | 252 | #ifdef __cplusplus 253 | } 254 | #endif 255 | 256 | 257 | #endif /* FJSON_OBJECT_ITERATOR_H */ 258 | -------------------------------------------------------------------------------- /tests/test_many_subobj.expected: -------------------------------------------------------------------------------- 1 | STEP1: { "key-0": 0, "key-1": 1, "key-2": 2, "key-3": 3, "key-4": 4, "key-5": 5, "key-6": 6, "key-7": 7, "key-8": 8, "key-9": 9, "key-10": 10, "key-11": 11, "key-12": 12, "key-13": 13, "key-14": 14, "key-15": 15, "key-16": 16, "key-17": 17, "key-18": 18, "key-19": 19, "key-20": 20, "key-21": 21, "key-22": 22, "key-23": 23, "key-24": 24, "key-25": 25, "key-26": 26, "key-27": 27, "key-28": 28, "key-29": 29, "key-30": 30, "key-31": 31, "key-32": 32, "key-33": 33, "key-34": 34, "key-35": 35, "key-36": 36, "key-37": 37, "key-38": 38, "key-39": 39, "key-40": 40, "key-41": 41, "key-42": 42, "key-43": 43, "key-44": 44, "key-45": 45, "key-46": 46, "key-47": 47, "key-48": 48, "key-49": 49, "key-50": 50, "key-51": 51, "key-52": 52, "key-53": 53, "key-54": 54, "key-55": 55, "key-56": 56, "key-57": 57, "key-58": 58, "key-59": 59, "key-60": 60, "key-61": 61, "key-62": 62, "key-63": 63, "key-64": 64, "key-65": 65, "key-66": 66, "key-67": 67, "key-68": 68, "key-69": 69, "key-70": 70, "key-71": 71, "key-72": 72, "key-73": 73, "key-74": 74, "key-75": 75, "key-76": 76, "key-77": 77, "key-78": 78, "key-79": 79, "key-80": 80, "key-81": 81, "key-82": 82, "key-83": 83, "key-84": 84, "key-85": 85, "key-86": 86, "key-87": 87, "key-88": 88, "key-89": 89, "key-90": 90, "key-91": 91, "key-92": 92, "key-93": 93, "key-94": 94, "key-95": 95, "key-96": 96, "key-97": 97, "key-98": 98, "key-99": 99, "key-100": 100, "key-101": 101, "key-102": 102, "key-103": 103, "key-104": 104, "key-105": 105, "key-106": 106, "key-107": 107, "key-108": 108, "key-109": 109, "key-110": 110, "key-111": 111, "key-112": 112, "key-113": 113, "key-114": 114, "key-115": 115, "key-116": 116, "key-117": 117, "key-118": 118, "key-119": 119, "key-120": 120, "key-121": 121, "key-122": 122, "key-123": 123, "key-124": 124, "key-125": 125, "key-126": 126, "key-127": 127, "key-128": 128, "key-129": 129, "key-130": 130, "key-131": 131, "key-132": 132, "key-133": 133, "key-134": 134, "key-135": 135, "key-136": 136, "key-137": 137, "key-138": 138, "key-139": 139, "key-140": 140, "key-141": 141, "key-142": 142, "key-143": 143, "key-144": 144, "key-145": 145, "key-146": 146, "key-147": 147, "key-148": 148, "key-149": 149, "key-150": 150, "key-151": 151, "key-152": 152, "key-153": 153, "key-154": 154, "key-155": 155, "key-156": 156, "key-157": 157, "key-158": 158, "key-159": 159, "key-160": 160, "key-161": 161, "key-162": 162, "key-163": 163, "key-164": 164, "key-165": 165, "key-166": 166, "key-167": 167, "key-168": 168, "key-169": 169, "key-170": 170, "key-171": 171, "key-172": 172, "key-173": 173, "key-174": 174, "key-175": 175, "key-176": 176, "key-177": 177, "key-178": 178, "key-179": 179, "key-180": 180, "key-181": 181, "key-182": 182, "key-183": 183, "key-184": 184, "key-185": 185, "key-186": 186, "key-187": 187, "key-188": 188, "key-189": 189, "key-190": 190, "key-191": 191, "key-192": 192, "key-193": 193, "key-194": 194, "key-195": 195, "key-196": 196, "key-197": 197, "key-198": 198, "key-199": 199 } 2 | STEP2: { "key-0": 0, "key-1": 1, "key-2": 2, "key-3": 3, "key-4": 4, "key-5": 5, "key-6": 6, "key-7": 7, "key-8": 8, "key-9": 9, "key-10": 10, "key-11": 11, "key-12": 12, "key-13": 13, "key-14": 14, "key-15": 15, "key-16": 16, "key-17": 17, "key-18": 18, "key-19": 19, "key-20": 20, "key-21": 21, "key-22": 22, "key-23": 23, "key-24": 24, "key-25": 25, "key-26": 26, "key-27": 27, "key-28": 28, "key-29": 29, "key-30": 30, "key-31": 31, "key-32": 32, "key-33": 33, "key-34": 34, "key-35": 35, "key-36": 36, "key-37": 37, "key-38": 38, "key-39": 39, "key-40": 40, "key-41": 41, "key-42": 42, "key-43": 43, "key-44": 44, "key-45": 45, "key-46": 46, "key-47": 47, "key-48": 48, "key-49": 49, "key-150": 150, "key-151": 151, "key-152": 152, "key-153": 153, "key-154": 154, "key-155": 155, "key-156": 156, "key-157": 157, "key-158": 158, "key-159": 159, "key-160": 160, "key-161": 161, "key-162": 162, "key-163": 163, "key-164": 164, "key-165": 165, "key-166": 166, "key-167": 167, "key-168": 168, "key-169": 169, "key-170": 170, "key-171": 171, "key-172": 172, "key-173": 173, "key-174": 174, "key-175": 175, "key-176": 176, "key-177": 177, "key-178": 178, "key-179": 179, "key-180": 180, "key-181": 181, "key-182": 182, "key-183": 183, "key-184": 184, "key-185": 185, "key-186": 186, "key-187": 187, "key-188": 188, "key-189": 189, "key-190": 190, "key-191": 191, "key-192": 192, "key-193": 193, "key-194": 194, "key-195": 195, "key-196": 196, "key-197": 197, "key-198": 198, "key-199": 199 } 3 | STEP3: { "key-0": 0, "key-1": 1, "key-2": 2, "key-3": 3, "key-4": 4, "key-5": 5, "key-6": 6, "key-7": 7, "key-8": 8, "key-9": 9, "key-10": 10, "key-11": 11, "key-12": 12, "key-13": 13, "key-14": 14, "key-15": 15, "key-16": 16, "key-17": 17, "key-18": 18, "key-19": 19, "key-20": 20, "key-21": 21, "key-22": 22, "key-23": 23, "key-24": 24, "key-25": 25, "key-26": 26, "key-27": 27, "key-28": 28, "key-29": 29, "key-30": 30, "key-31": 31, "key-32": 32, "key-33": 33, "key-34": 34, "key-35": 35, "key-36": 36, "key-37": 37, "key-38": 38, "key-39": 39, "key-40": 40, "key-41": 41, "key-42": 42, "key-43": 43, "key-44": 44, "key-45": 45, "key-46": 46, "key-47": 47, "key-48": 48, "key-49": 49, "KEY-149": 149, "KEY-148": 148, "KEY-147": 147, "KEY-146": 146, "KEY-145": 145, "KEY-144": 144, "KEY-143": 143, "KEY-142": 142, "KEY-141": 141, "KEY-140": 140, "KEY-139": 139, "KEY-138": 138, "KEY-137": 137, "KEY-136": 136, "KEY-135": 135, "KEY-134": 134, "KEY-133": 133, "KEY-132": 132, "KEY-131": 131, "KEY-130": 130, "KEY-129": 129, "KEY-128": 128, "KEY-127": 127, "KEY-126": 126, "KEY-125": 125, "KEY-124": 124, "KEY-123": 123, "KEY-122": 122, "KEY-121": 121, "KEY-120": 120, "KEY-119": 119, "KEY-118": 118, "KEY-117": 117, "KEY-116": 116, "KEY-115": 115, "KEY-114": 114, "KEY-113": 113, "KEY-112": 112, "KEY-111": 111, "KEY-110": 110, "KEY-109": 109, "KEY-108": 108, "KEY-107": 107, "KEY-106": 106, "KEY-105": 105, "KEY-104": 104, "KEY-103": 103, "KEY-102": 102, "KEY-101": 101, "KEY-100": 100, "KEY-99": 99, "KEY-98": 98, "KEY-97": 97, "KEY-96": 96, "KEY-95": 95, "KEY-94": 94, "KEY-93": 93, "KEY-92": 92, "KEY-91": 91, "KEY-90": 90, "KEY-89": 89, "KEY-88": 88, "KEY-87": 87, "KEY-86": 86, "KEY-85": 85, "KEY-84": 84, "KEY-83": 83, "KEY-82": 82, "KEY-81": 81, "KEY-80": 80, "KEY-79": 79, "KEY-78": 78, "KEY-77": 77, "KEY-76": 76, "KEY-75": 75, "KEY-74": 74, "KEY-73": 73, "KEY-72": 72, "KEY-71": 71, "KEY-70": 70, "KEY-69": 69, "KEY-68": 68, "KEY-67": 67, "KEY-66": 66, "KEY-65": 65, "KEY-64": 64, "KEY-63": 63, "KEY-62": 62, "KEY-61": 61, "KEY-60": 60, "KEY-59": 59, "KEY-58": 58, "KEY-57": 57, "KEY-56": 56, "KEY-55": 55, "KEY-54": 54, "KEY-53": 53, "KEY-52": 52, "KEY-51": 51, "KEY-50": 50, "key-150": 150, "key-151": 151, "key-152": 152, "key-153": 153, "key-154": 154, "key-155": 155, "key-156": 156, "key-157": 157, "key-158": 158, "key-159": 159, "key-160": 160, "key-161": 161, "key-162": 162, "key-163": 163, "key-164": 164, "key-165": 165, "key-166": 166, "key-167": 167, "key-168": 168, "key-169": 169, "key-170": 170, "key-171": 171, "key-172": 172, "key-173": 173, "key-174": 174, "key-175": 175, "key-176": 176, "key-177": 177, "key-178": 178, "key-179": 179, "key-180": 180, "key-181": 181, "key-182": 182, "key-183": 183, "key-184": 184, "key-185": 185, "key-186": 186, "key-187": 187, "key-188": 188, "key-189": 189, "key-190": 190, "key-191": 191, "key-192": 192, "key-193": 193, "key-194": 194, "key-195": 195, "key-196": 196, "key-197": 197, "key-198": 198, "key-199": 199 } 4 | STEP4: { "key-0": 0, "key-1": 10, "key-2": 20, "key-3": 30, "key-4": 40, "key-5": 50, "key-6": 60, "key-7": 70, "key-8": 80, "key-9": 90, "key-10": 100, "key-11": 110, "key-12": 120, "key-13": 130, "key-14": 140, "key-15": 150, "key-16": 160, "key-17": 170, "key-18": 180, "key-19": 190, "key-20": 200, "key-21": 210, "key-22": 220, "key-23": 230, "key-24": 240, "key-25": 250, "key-26": 260, "key-27": 270, "key-28": 280, "key-29": 290, "key-30": 300, "key-31": 310, "key-32": 320, "key-33": 330, "key-34": 340, "key-35": 350, "key-36": 360, "key-37": 370, "key-38": 380, "key-39": 390, "key-40": 400, "key-41": 410, "key-42": 420, "key-43": 430, "key-44": 440, "key-45": 450, "key-46": 460, "key-47": 470, "key-48": 480, "key-49": 490, "key-50": 500, "key-51": 510, "key-52": 520, "key-53": 530, "key-54": 540, "key-55": 550, "key-56": 560, "key-57": 570, "key-58": 580, "key-59": 590, "key-60": 600, "key-61": 610, "key-62": 620, "key-63": 630, "key-64": 640, "key-65": 650, "key-66": 660, "key-67": 670, "key-68": 680, "key-69": 690, "key-70": 700, "key-71": 710, "key-72": 720, "key-73": 730, "key-74": 740, "key-75": 750, "key-76": 760, "key-77": 770, "key-78": 780, "key-79": 790, "key-80": 800, "key-81": 810, "key-82": 820, "key-83": 830, "key-84": 840, "key-85": 850, "key-86": 860, "key-87": 870, "key-88": 880, "key-89": 890, "key-90": 900, "key-91": 910, "key-92": 920, "key-93": 930, "key-94": 940, "key-95": 950, "key-96": 960, "key-97": 970, "key-98": 980, "key-99": 990, "key-100": 1000, "key-101": 1010, "key-102": 1020, "key-103": 1030, "key-104": 1040, "key-105": 1050, "key-106": 1060, "key-107": 1070, "key-108": 1080, "key-109": 1090, "key-110": 1100, "key-111": 1110, "key-112": 1120, "key-113": 1130, "key-114": 1140, "key-115": 1150, "key-116": 1160, "key-117": 1170, "key-118": 1180, "key-119": 1190, "key-120": 1200, "key-121": 1210, "key-122": 1220, "key-123": 1230, "key-124": 1240, "key-125": 1250, "key-126": 1260, "key-127": 1270, "key-128": 1280, "key-129": 1290, "key-130": 1300, "key-131": 1310, "key-132": 1320, "key-133": 1330, "key-134": 1340, "key-135": 1350, "key-136": 1360, "key-137": 1370, "key-138": 1380, "key-139": 1390, "key-140": 1400, "key-141": 1410, "key-142": 1420, "key-143": 1430, "key-144": 1440, "key-145": 1450, "key-146": 1460, "key-147": 1470, "key-148": 1480, "key-149": 1490, "key-150": 1500, "key-151": 1510, "key-152": 1520, "key-153": 1530, "key-154": 1540, "key-155": 1550, "key-156": 1560, "key-157": 1570, "key-158": 1580, "key-159": 1590, "key-160": 1600, "key-161": 1610, "key-162": 1620, "key-163": 1630, "key-164": 1640, "key-165": 1650, "key-166": 1660, "key-167": 1670, "key-168": 1680, "key-169": 1690, "key-170": 1700, "key-171": 1710, "key-172": 1720, "key-173": 1730, "key-174": 1740, "key-175": 1750, "key-176": 1760, "key-177": 1770, "key-178": 1780, "key-179": 1790, "key-180": 1800, "key-181": 1810, "key-182": 1820, "key-183": 1830, "key-184": 1840, "key-185": 1850, "key-186": 1860, "key-187": 1870, "key-188": 1880, "key-189": 1890, "key-190": 1900, "key-191": 1910, "key-192": 1920, "key-193": 1930, "key-194": 1940, "key-195": 1950, "key-196": 1960, "key-197": 1970, "key-198": 1980, "key-199": 1990 } 5 | STEP5:{ "key-0": 0, "key-1": 10, "key-2": 20, "key-3": 30, "key-4": 40, "key-5": 50, "key-6": 60, "key-7": 70, "key-8": 80, "key-9": 90, "key-10": 100, "key-11": 110, "key-12": 120, "key-13": 130, "key-14": 140, "key-15": 150, "key-16": 160, "key-17": 170, "key-18": 180, "key-19": 190, "key-20": 200, "key-21": 210, "key-22": 220, "key-23": 230, "key-24": 240, "key-25": 250, "key-26": 260, "key-27": 270, "key-28": 280, "key-29": 290, "key-30": 300, "key-31": 310, "key-32": 320, "key-33": 330, "key-34": 340, "key-35": 350, "key-36": 360, "key-37": 370, "key-38": 380, "key-39": 390, "key-40": 400, "key-41": 410, "key-42": 420, "key-43": 430, "key-44": 440, "key-45": 450, "key-46": 460, "key-47": 470, "key-48": 480, "key-49": 490, "key-50": 500, "key-51": 510, "key-52": 520, "key-53": 530, "key-54": 540, "key-55": 550, "key-56": 560, "key-57": 570, "key-58": 580, "key-59": 590, "key-60": 600, "key-61": 610, "key-62": 620, "key-63": 630, "key-64": 640, "key-65": 650, "key-66": 660, "key-67": 670, "key-68": 680, "key-69": 690, "key-70": 700, "key-71": 710, "key-72": 720, "key-73": 730, "key-74": 740, "key-75": 750, "key-76": 760, "key-77": 770, "key-78": 780, "key-79": 790, "key-80": 800, "key-81": 810, "key-82": 820, "key-83": 830, "key-84": 840, "key-85": 850, "key-86": 860, "key-87": 870, "key-88": 880, "key-89": 890, "key-90": 900, "key-91": 910, "key-92": 920, "key-93": 930, "key-94": 940, "key-95": 950, "key-96": 960, "key-97": 970, "key-98": 980, "key-99": 990, "key-100": 1000, "key-101": 1010, "key-102": 1020, "key-103": 1030, "key-104": 1040, "key-105": 1050, "key-106": 1060, "key-107": 1070, "key-108": 1080, "key-109": 1090, "key-110": 1100, "key-111": 1110, "key-112": 1120, "key-113": 1130, "key-114": 1140, "key-115": 1150, "key-116": 1160, "key-117": 1170, "key-118": 1180, "key-119": 1190, "key-120": 1200, "key-121": 1210, "key-122": 1220, "key-123": 1230, "key-124": 1240, "key-125": 1250, "key-126": 1260, "key-127": 1270, "key-128": 1280, "key-129": 1290, "key-130": 1300, "key-131": 1310, "key-132": 1320, "key-133": 1330, "key-134": 1340, "key-135": 1350, "key-136": 1360, "key-137": 1370, "key-138": 1380, "key-139": 1390, "key-140": 1400, "key-141": 1410, "key-142": 1420, "key-143": 1430, "key-144": 1440, "key-145": 1450, "key-146": 1460, "key-147": 1470, "key-148": 1480, "key-149": 1490, "key-150": 1500, "key-151": 1510, "key-152": 1520, "key-153": 1530, "key-154": 1540, "key-155": 1550, "key-156": 1560, "key-157": 1570, "key-158": 1580, "key-159": 1590, "key-160": 1600, "key-161": 1610, "key-162": 1620, "key-163": 1630, "key-164": 1640, "key-165": 1650, "key-166": 1660, "key-167": 1670, "key-168": 1680, "key-169": 1690, "key-170": 1700, "key-171": 1710, "key-172": 1720, "key-173": 1730, "key-174": 1740, "key-175": 1750, "key-176": 1760, "key-177": 1770, "key-178": 1780, "key-179": 1790, "key-180": 1800, "key-181": 1810, "key-182": 1820, "key-183": 1830, "key-184": 1840, "key-185": 1850, "key-186": 1860, "key-187": 1870, "key-188": 1880, "key-189": 1890, "key-190": 1900, "key-191": 1910, "key-192": 1920, "key-193": 1930, "key-194": 1940, "key-195": 1950, "key-196": 1960, "key-197": 1970, "key-198": 1980, "key-199": 1990, "key-200": 200 } 6 | -------------------------------------------------------------------------------- /tests/test_parse.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../json.h" 8 | #include "../json_tokener.h" 9 | #include "../debug.h" 10 | 11 | static void test_basic_parse(void); 12 | static void test_verbose_parse(void); 13 | static void test_incremental_parse(void); 14 | 15 | #define CHK(x) if (!(x)) { \ 16 | printf("%s:%d: unexpected result with '%s'\n", \ 17 | __FILE__, __LINE__, #x); \ 18 | exit(1); \ 19 | } 20 | 21 | int main(int __attribute__((unused)) argc, char __attribute__((unused)) **argv) 22 | { 23 | MC_SET_DEBUG(1); 24 | 25 | test_basic_parse(); 26 | printf("==================================\n"); 27 | test_verbose_parse(); 28 | printf("==================================\n"); 29 | test_incremental_parse(); 30 | printf("==================================\n"); 31 | return 0; 32 | } 33 | 34 | static void test_basic_parse(void) 35 | { 36 | fjson_object *new_obj; 37 | 38 | new_obj = fjson_tokener_parse("\"\003\""); 39 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 40 | fjson_object_put(new_obj); 41 | 42 | new_obj = fjson_tokener_parse("/* hello */\"foo\""); 43 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 44 | fjson_object_put(new_obj); 45 | 46 | new_obj = fjson_tokener_parse("// hello\n\"foo\""); 47 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 48 | fjson_object_put(new_obj); 49 | 50 | new_obj = fjson_tokener_parse("\"\\u0041\\u0042\\u0043\""); 51 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 52 | fjson_object_put(new_obj); 53 | 54 | new_obj = fjson_tokener_parse("null"); 55 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 56 | fjson_object_put(new_obj); 57 | 58 | new_obj = fjson_tokener_parse("NaN"); 59 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 60 | fjson_object_put(new_obj); 61 | 62 | new_obj = fjson_tokener_parse("-NaN"); /* non-sensical, returns null */ 63 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 64 | fjson_object_put(new_obj); 65 | 66 | new_obj = fjson_tokener_parse("Inf"); /* must use full string, returns null */ 67 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 68 | fjson_object_put(new_obj); 69 | 70 | new_obj = fjson_tokener_parse("inf"); /* must use full string, returns null */ 71 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 72 | fjson_object_put(new_obj); 73 | 74 | new_obj = fjson_tokener_parse("Infinity"); 75 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 76 | fjson_object_put(new_obj); 77 | 78 | new_obj = fjson_tokener_parse("infinity"); 79 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 80 | fjson_object_put(new_obj); 81 | 82 | new_obj = fjson_tokener_parse("-Infinity"); 83 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 84 | fjson_object_put(new_obj); 85 | 86 | new_obj = fjson_tokener_parse("-infinity"); 87 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 88 | fjson_object_put(new_obj); 89 | 90 | new_obj = fjson_tokener_parse("True"); 91 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 92 | fjson_object_put(new_obj); 93 | 94 | new_obj = fjson_tokener_parse("12"); 95 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 96 | fjson_object_put(new_obj); 97 | 98 | new_obj = fjson_tokener_parse("12.3"); 99 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 100 | fjson_object_put(new_obj); 101 | 102 | new_obj = fjson_tokener_parse("12.3.4"); /* non-sensical, returns null */ 103 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 104 | fjson_object_put(new_obj); 105 | 106 | /* was returning (int)2015 before patch, should return null */ 107 | new_obj = fjson_tokener_parse("2015-01-15"); 108 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 109 | fjson_object_put(new_obj); 110 | 111 | new_obj = fjson_tokener_parse("{\"FoO\" : -12.3E512}"); 112 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 113 | fjson_object_put(new_obj); 114 | 115 | new_obj = fjson_tokener_parse("{\"FoO\" : -12.3E51.2}"); /* non-sensical, returns null */ 116 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 117 | fjson_object_put(new_obj); 118 | 119 | new_obj = fjson_tokener_parse("[\"\\n\"]"); 120 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 121 | fjson_object_put(new_obj); 122 | 123 | new_obj = fjson_tokener_parse("[\"\\nabc\\n\"]"); 124 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 125 | fjson_object_put(new_obj); 126 | 127 | new_obj = fjson_tokener_parse("[null]"); 128 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 129 | fjson_object_put(new_obj); 130 | 131 | new_obj = fjson_tokener_parse("[]"); 132 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 133 | fjson_object_put(new_obj); 134 | 135 | new_obj = fjson_tokener_parse("[false]"); 136 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 137 | fjson_object_put(new_obj); 138 | 139 | new_obj = fjson_tokener_parse("[\"abc\",null,\"def\",12]"); 140 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 141 | fjson_object_put(new_obj); 142 | 143 | new_obj = fjson_tokener_parse("{}"); 144 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 145 | fjson_object_put(new_obj); 146 | 147 | new_obj = fjson_tokener_parse("{ \"foo\": \"bar\" }"); 148 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 149 | fjson_object_put(new_obj); 150 | 151 | new_obj = fjson_tokener_parse("{ \"foo\": \"bar\", \"baz\": null, \"bool0\": true }"); 152 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 153 | fjson_object_put(new_obj); 154 | 155 | new_obj = fjson_tokener_parse("{ \"foo\": [null, \"foo\"] }"); 156 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 157 | fjson_object_put(new_obj); 158 | 159 | new_obj = fjson_tokener_parse("{ \"abc\": 12, \"foo\": \"bar\", \"bool0\": false, \"bool1\": true, \"arr\": [ 1, 2, 3, null, 5 ] }"); 160 | printf("new_obj.to_string()=%s\n", fjson_object_to_json_string(new_obj)); 161 | fjson_object_put(new_obj); 162 | } 163 | 164 | static void test_verbose_parse(void) 165 | { 166 | fjson_object *new_obj; 167 | enum fjson_tokener_error error = fjson_tokener_success; 168 | 169 | new_obj = fjson_tokener_parse_verbose("{ foo }", &error); 170 | CHK (error == fjson_tokener_error_parse_object_key_name); 171 | CHK (new_obj == NULL); 172 | 173 | new_obj = fjson_tokener_parse("{ foo }"); 174 | CHK (new_obj == NULL); 175 | 176 | new_obj = fjson_tokener_parse("foo"); 177 | CHK (new_obj == NULL); 178 | new_obj = fjson_tokener_parse_verbose("foo", &error); 179 | CHK (new_obj == NULL); 180 | 181 | /* b/c the string starts with 'f' parsing return a boolean error */ 182 | CHK (error == fjson_tokener_error_parse_boolean); 183 | 184 | printf("fjson_tokener_parse_versbose() OK\n"); 185 | } 186 | 187 | struct incremental_step { 188 | const char *string_to_parse; 189 | int length; 190 | int char_offset; 191 | enum fjson_tokener_error expected_error; 192 | int reset_tokener; 193 | } incremental_steps[] = { 194 | 195 | /* Check that full json messages can be parsed, both w/ and w/o a reset */ 196 | { "{ \"foo\": 123 }", -1, -1, fjson_tokener_success, 0 }, 197 | { "{ \"foo\": 456 }", -1, -1, fjson_tokener_success, 1 }, 198 | { "{ \"foo\": 789 }", -1, -1, fjson_tokener_success, 1 }, 199 | 200 | /* Check a basic incremental parse */ 201 | { "{ \"foo", -1, -1, fjson_tokener_continue, 0 }, 202 | { "\": {\"bar", -1, -1, fjson_tokener_continue, 0 }, 203 | { "\":13}}", -1, -1, fjson_tokener_success, 1 }, 204 | 205 | /* Check that fjson_tokener_reset actually resets */ 206 | { "{ \"foo", -1, -1, fjson_tokener_continue, 1 }, 207 | { ": \"bar\"}", -1, 0, fjson_tokener_error_parse_unexpected, 1 }, 208 | 209 | /* Check incremental parsing with trailing characters */ 210 | { "{ \"foo", -1, -1, fjson_tokener_continue, 0 }, 211 | { "\": {\"bar", -1, -1, fjson_tokener_continue, 0 }, 212 | { "\":13}}XXXX", 10, 6, fjson_tokener_success, 0 }, 213 | { "XXXX", 4, 0, fjson_tokener_error_parse_unexpected, 1 }, 214 | 215 | /* Check that trailing characters can change w/o a reset */ 216 | { "{\"x\": 123 }\"X\"", -1, 11, fjson_tokener_success, 0 }, 217 | { "\"Y\"", -1, -1, fjson_tokener_success, 1 }, 218 | 219 | /* To stop parsing a number we need to reach a non-digit, e.g. a \0 */ 220 | { "1", 1, 1, fjson_tokener_continue, 0 }, 221 | { "2", 2, 1, fjson_tokener_success, 0 }, 222 | 223 | /* Some bad formatting. Check we get the correct error status */ 224 | { "2015-01-15", 10, 4, fjson_tokener_error_parse_number, 1 }, 225 | 226 | /* Strings have a well defined end point, so we can stop at the quote */ 227 | { "\"blue\"", -1, -1, fjson_tokener_success, 0 }, 228 | 229 | /* Check each of the escape sequences defined by the spec */ 230 | { "\"\\\"\"", -1, -1, fjson_tokener_success, 0 }, 231 | { "\"\\\\\"", -1, -1, fjson_tokener_success, 0 }, 232 | { "\"\\b\"", -1, -1, fjson_tokener_success, 0 }, 233 | { "\"\\f\"", -1, -1, fjson_tokener_success, 0 }, 234 | { "\"\\n\"", -1, -1, fjson_tokener_success, 0 }, 235 | { "\"\\r\"", -1, -1, fjson_tokener_success, 0 }, 236 | { "\"\\t\"", -1, -1, fjson_tokener_success, 0 }, 237 | 238 | { "[1,2,3]", -1, -1, fjson_tokener_success, 0 }, 239 | 240 | /* This behaviour doesn't entirely follow the json spec, but until we have 241 | a way to specify how strict to be we follow Postel's Law and be liberal 242 | in what we accept (up to a point). */ 243 | { "[1,2,3,]", -1, -1, fjson_tokener_success, 0 }, 244 | { "[1,2,,3,]", -1, 5, fjson_tokener_error_parse_unexpected, 0 }, 245 | 246 | { "[1,2,3,]", -1, 7, fjson_tokener_error_parse_unexpected, 3 }, 247 | { "{\"a\":1,}", -1, 7, fjson_tokener_error_parse_unexpected, 3 }, 248 | 249 | { NULL, -1, -1, fjson_tokener_success, 0 }, 250 | }; 251 | 252 | static void test_incremental_parse(void) 253 | { 254 | fjson_object *new_obj; 255 | enum fjson_tokener_error jerr; 256 | fjson_tokener *tok; 257 | const char *string_to_parse; 258 | int ii; 259 | int num_ok, num_error; 260 | 261 | num_ok = 0; 262 | num_error = 0; 263 | 264 | printf("Starting incremental tests.\n"); 265 | printf("Note: quotes and backslashes seen in the output here are literal values passed\n"); 266 | printf(" to the parse functions. e.g. this is 4 characters: \"\\f\"\n"); 267 | 268 | string_to_parse = "{ \"foo"; /* } */ 269 | printf("fjson_tokener_parse(%s) ... ", string_to_parse); 270 | new_obj = fjson_tokener_parse(string_to_parse); 271 | if (new_obj == NULL) printf("got error as expected\n"); 272 | 273 | /* test incremental parsing in various forms */ 274 | tok = fjson_tokener_new(); 275 | for (ii = 0; incremental_steps[ii].string_to_parse != NULL; ii++) 276 | { 277 | int this_step_ok = 0; 278 | struct incremental_step *step = &incremental_steps[ii]; 279 | int length = step->length; 280 | int expected_char_offset = step->char_offset; 281 | 282 | if (step->reset_tokener & 2) 283 | fjson_tokener_set_flags(tok, FJSON_TOKENER_STRICT); 284 | else 285 | fjson_tokener_set_flags(tok, 0); 286 | 287 | if (length == -1) 288 | length = strlen(step->string_to_parse); 289 | if (expected_char_offset == -1) 290 | expected_char_offset = length; 291 | 292 | printf("fjson_tokener_parse_ex(tok, %-12s, %3d) ... ", 293 | step->string_to_parse, length); 294 | new_obj = fjson_tokener_parse_ex(tok, step->string_to_parse, length); 295 | 296 | jerr = fjson_tokener_get_error(tok); 297 | if (step->expected_error != fjson_tokener_success) 298 | { 299 | if (new_obj != NULL) 300 | printf("ERROR: invalid object returned: %s\n", 301 | fjson_object_to_json_string(new_obj)); 302 | else if (jerr != step->expected_error) 303 | printf("ERROR: got wrong error: %s\n", 304 | fjson_tokener_error_desc(jerr)); 305 | else if (tok->char_offset != expected_char_offset) 306 | printf("ERROR: wrong char_offset %d != expected %d\n", 307 | tok->char_offset, 308 | expected_char_offset); 309 | else 310 | { 311 | printf("OK: got correct error: %s\n", fjson_tokener_error_desc(jerr)); 312 | this_step_ok = 1; 313 | } 314 | } 315 | else 316 | { 317 | if (new_obj == NULL) 318 | printf("ERROR: expected valid object, instead: %s\n", 319 | fjson_tokener_error_desc(jerr)); 320 | else if (tok->char_offset != expected_char_offset) 321 | printf("ERROR: wrong char_offset %d != expected %d\n", 322 | tok->char_offset, 323 | expected_char_offset); 324 | else 325 | { 326 | printf("OK: got object of type [%s]: %s\n", 327 | fjson_type_to_name(fjson_object_get_type(new_obj)), 328 | fjson_object_to_json_string(new_obj)); 329 | this_step_ok = 1; 330 | } 331 | } 332 | 333 | if (new_obj) 334 | fjson_object_put(new_obj); 335 | 336 | if (step->reset_tokener & 1) 337 | fjson_tokener_reset(tok); 338 | 339 | if (this_step_ok) 340 | num_ok++; 341 | else 342 | num_error++; 343 | } 344 | 345 | fjson_tokener_free(tok); 346 | 347 | printf("End Incremental Tests OK=%d ERROR=%d\n", num_ok, num_error); 348 | 349 | return; 350 | } 351 | --------------------------------------------------------------------------------