Index: modules/json/ChangeLog ================================================================== --- modules/json/ChangeLog +++ modules/json/ChangeLog @@ -1,5 +1,18 @@ +2013-12-11 Andreas Kupries + + * c: [Ticket 6efa4f571a]: Integrated a critcl binding of + * json.bench: the json-c parser. Moved to a standard + * json.man: switchable setup. json2dict and manyy-json2dict + * json.tcl: are now switchable to C. Everything else kept + * json.test: to Tcl. Note especially that Tcl 'validate' + * json.testsuite: (regexp-based) was still faster than json-c. + * json_tcl.tcl: Which has no mode for pure syntax checking, + * jsonc.tcl: thus overhead with construction of irelevant data + * pkgIndex.tcl: structures. Extended testsuite. New benchmark + * testdata: suite. Version bumped to 1.3. + 2013-06-19 Andreas Kupries * json.man: New command 'many-json2dict' to parse strings * json.pcx: containing more than one JSON entity. The existing * json.tcl: json2dict command will return only the first. Updated ADDED modules/json/c/AUTHORS Index: modules/json/c/AUTHORS ================================================================== --- /dev/null +++ modules/json/c/AUTHORS @@ -0,0 +1,5 @@ +Michael Clark +Jehiah Czebotar +Eric Haszlakiewicz +C. Watford (christopher.watford@gmail.com) + ADDED modules/json/c/COPYING Index: modules/json/c/COPYING ================================================================== --- /dev/null +++ modules/json/c/COPYING @@ -0,0 +1,42 @@ + +Copyright (c) 2009-2012 Eric Haszlakiewicz + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +---------------------------------------------------------------- + +Copyright (c) 2004, 2005 Metaparadigm Pte Ltd + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. ADDED modules/json/c/ChangeLog Index: modules/json/c/ChangeLog ================================================================== --- /dev/null +++ modules/json/c/ChangeLog @@ -0,0 +1,175 @@ + +0.11 + + * IMPORTANT: the name of the library has changed to libjson-c.so and + the header files are now in include/json-c. + The pkgconfig name has also changed from json to json-c. + You should change your build to use appropriate -I and -l options. + A compatibility shim is in place so builds using the old name will + continue to work, but that will be removed in the next release. + * Maximum recursion depth is now a runtime option. + json_tokener_new() is provided for compatibility. + json_tokener_new_ex(depth) + * Include json_object_iterator.h in the installed headers. + * Add support for building on Android. + * Rewrite json_object_object_add to replace just the value if the key already exists so keys remain valid. + * Make it safe to delete keys while iterating with the json_object_object_foreach macro. + * Add a json_set_serializer() function to allow the string output of a json_object to be customized. + * Make float parsing locale independent. + * Add a json_tokener_set_flags() function and a JSON_TOKENER_STRICT flag. + * Enable -Werror when building. + * speed improvements to parsing 64-bit integers on systems with working sscanf + * Add a json_object_object_length function. + * Fix a bug (buffer overrun) when expanding arrays to more than 64 entries. + +0.10 + + * Add a json_object_to_json_string_ext() function to allow output to be + formatted in a more human readable form. + * Add json_object_object_get_ex(), a NULL-safe get object method, to be able + to distinguish between a key not present and the value being NULL. + * Add an alternative iterator implementation, see json_object_iterator.h + * Make json_object_iter public to enable external use of the + json_object_object_foreachC macro. + * Add a printbuf_memset() function to provide an effecient way to set and + append things like whitespace indentation. + * Adjust json_object_is_type and json_object_get_type so they return + json_type_null for NULL objects and handle NULL passed to + json_objct_object_get(). + * Rename boolean type to json_bool. + * Fix various compile issues for Visual Studio and MinGW. + * Allow json_tokener_parse_ex() to be re-used to parse multiple object. + Also, fix some parsing issues with capitalized hexadecimal numbers and + number in E notation. + * Add json_tokener_get_error() and json_tokener_error_desc() to better + encapsulate the process of retrieving errors while parsing. + * Various improvements to the documentation of many functions. + * Add new json_object_array_sort() function. + * Fix a bug in json_object_get_int(), which would incorrectly return 0 + when called on a string type object. + Eric Haszlakiewicz + * Add a json_type_to_name() function. + Eric Haszlakiewicz + * Add a json_tokener_parse_verbose() function. + Jehiah Czebotar + * Improve support for null bytes within JSON strings. + Jehiah Czebotar + * Fix file descriptor leak if memory allocation fails in json_util + Zachary Blair, zack_blair at hotmail dot com + * Add int64 support. Two new functions json_object_net_int64 and + json_object_get_int64. Binary compatibility preserved. + Eric Haszlakiewicz, EHASZLA at transunion com + Rui Miguel Silva Seabra, rms at 1407 dot org + * Fix subtle bug in linkhash where lookup could hang after all slots + were filled then successively freed. + Spotted by Jean-Marc Naud, j dash m at newtraxtech dot com + * Make json_object_from_file take const char *filename + Spotted by Vikram Raj V, vsagar at attinteractive dot com + * Add handling of surrogate pairs (json_tokener.c, test4.c, Makefile.am) + Brent Miller, bdmiller at yahoo dash inc dot com + * Correction to comment describing printbuf_memappend in printbuf.h + Brent Miller, bdmiller at yahoo dash inc dot com + +0.9 + * Add README.html README-WIN32.html config.h.win32 to Makefile.am + Michael Clark, + * Add const qualifier to the json_tokener_parse functions + Eric Haszlakiewicz, EHASZLA at transunion dot com + * Rename min and max so we can never clash with C or C++ std library + Ian Atha, thatha at yahoo dash inc dot com + * Fix any noticeable spelling or grammar errors. + * Make sure every va_start has a va_end. + * Check all pointers for validity. + Erik Hovland, erik at hovland dot org + * Fix json_object_get_boolean to return false for empty string + Spotted by Vitaly Kruglikov, Vitaly dot Kruglikov at palm dot com + * optimizations to json_tokener_parse_ex(), printbuf_memappend() + Brent Miller, bdmiller at yahoo dash inc dot com + * Disable REFCOUNT_DEBUG by default in json_object.c + * Don't use this as a variable, so we can compile with a C++ compiler + * Add casts from void* to type of assignment when using malloc + * Add #ifdef __cplusplus guards to all of the headers + * Add typedefs for json_object, json_tokener, array_list, printbuf, lh_table + Michael Clark, + * Null pointer dereference fix. Fix json_object_get_boolean strlen test + to not return TRUE for zero length string. Remove redundant includes. + Erik Hovland, erik at hovland dot org + * Fixed warning reported by adding -Wstrict-prototypes + -Wold-style-definition to the compilatin flags. + Dotan Barak, dotanba at gmail dot com + * Add const correctness to public interfaces + Gerard Krol, g dot c dot krol at student dot tudelft dot nl + +0.8 + * Add va_end for every va_start + Dotan Barak, dotanba at gmail dot com + * Add macros to enable compiling out debug code + Geoffrey Young, geoff at modperlcookbook dot org + * Fix bug with use of capital E in numbers with exponents + Mateusz Loskot, mateusz at loskot dot net + * Add stddef.h include + * Patch allows for json-c compile with -Werror and not fail due to + -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations + Geoffrey Young, geoff at modperlcookbook dot org + +0.7 + * Add escaping of backslash to json output + * Add escaping of foward slash on tokenizing and output + * Changes to internal tokenizer from using recursion to + using a depth state structure to allow incremental parsing + +0.6 + * Fix bug in escaping of control characters + Johan Björklund, johbjo09 at kth dot se + * Remove include "config.h" from headers (should only + be included from .c files) + Michael Clark + +0.5 + * Make headers C++ compatible by change *this to *obj + * Add ifdef C++ extern "C" to headers + * Use simpler definition of min and max in bits.h + Larry Lansing, llansing at fuzzynerd dot com + + * Remove automake 1.6 requirement + * Move autogen commands into autogen.sh. Update README + * Remove error pointer special case for Windows + * Change license from LGPL to MIT + Michael Clark + +0.4 + * Fix additional error case in object parsing + * Add back sign reversal in nested object parse as error pointer + value is negative, while error value is positive. + Michael Clark + +0.3 + * fix pointer arithmetic bug for error pointer check in is_error() macro + * fix type passed to printbuf_memappend in json_tokener + * update autotools bootstrap instructions in README + Michael Clark + +0.2 + * printbuf.c - C. Watford (christopher.watford@gmail.com) + Added a Win32/Win64 compliant implementation of vasprintf + * debug.c - C. Watford (christopher.watford@gmail.com) + Removed usage of vsyslog on Win32/Win64 systems, needs to be handled + by a configure script + * json_object.c - C. Watford (christopher.watford@gmail.com) + Added scope operator to wrap usage of json_object_object_foreach, this + needs to be rethought to be more ANSI C friendly + * json_object.h - C. Watford (christopher.watford@gmail.com) + Added Microsoft C friendly version of json_object_object_foreach + * json_tokener.c - C. Watford (christopher.watford@gmail.com) + Added a Win32/Win64 compliant implementation of strndup + * json_util.c - C. Watford (christopher.watford@gmail.com) + Added cast and mask to suffice size_t v. unsigned int conversion + correctness + * json_tokener.c - sign reversal issue on error info for nested object parse + spotted by Johan Björklund (johbjo09 at kth.se) + * json_object.c - escape " in json_escape_str + * Change to automake and libtool to build shared and static library + Michael Clark + +0.1 + * initial release ADDED modules/json/c/NEWS Index: modules/json/c/NEWS ================================================================== --- /dev/null +++ modules/json/c/NEWS ADDED modules/json/c/README Index: modules/json/c/README ================================================================== --- /dev/null +++ modules/json/c/README @@ -0,0 +1,14 @@ +Integration Notes... + +This directory contains part of the json-c project maintained at + + https://github.com/json-c/json-c/wiki + +Reduced to the pure sources and relevant incidental files for proper +attribution and licensing it represents the 0.11 release whose tarball +was retrieved from + + https://s3.amazonaws.com/json-c_releases/releases/index.html + +The various build systems are not part of the integration as their +role was here taken over by Critcl. ADDED modules/json/c/arraylist.c Index: modules/json/c/arraylist.c ================================================================== --- /dev/null +++ modules/json/c/arraylist.c @@ -0,0 +1,101 @@ +/* + * $Id: arraylist.c,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#ifdef STDC_HEADERS +# include +# include +#endif /* STDC_HEADERS */ + +#if defined(HAVE_STRINGS_H) && !defined(_STRING_H) && !defined(__USE_BSD) +# include +#endif /* HAVE_STRINGS_H */ + +#include "bits.h" +#include "arraylist.h" + +struct array_list* +array_list_new(array_list_free_fn *free_fn) +{ + struct array_list *arr; + + arr = (struct array_list*)calloc(1, sizeof(struct array_list)); + if(!arr) return NULL; + arr->size = ARRAY_LIST_DEFAULT_SIZE; + arr->length = 0; + arr->free_fn = free_fn; + if(!(arr->array = (void**)calloc(sizeof(void*), arr->size))) { + free(arr); + return NULL; + } + return arr; +} + +extern void +array_list_free(struct array_list *arr) +{ + int i; + for(i = 0; i < arr->length; i++) + if(arr->array[i]) arr->free_fn(arr->array[i]); + free(arr->array); + free(arr); +} + +void* +array_list_get_idx(struct array_list *arr, int i) +{ + if(i >= arr->length) return NULL; + return arr->array[i]; +} + +static int array_list_expand_internal(struct array_list *arr, int max) +{ + void *t; + int new_size; + + if(max < arr->size) return 0; + new_size = json_max(arr->size << 1, max); + if(!(t = realloc(arr->array, new_size*sizeof(void*)))) return -1; + arr->array = (void**)t; + (void)memset(arr->array + arr->size, 0, (new_size-arr->size)*sizeof(void*)); + arr->size = new_size; + return 0; +} + +int +array_list_put_idx(struct array_list *arr, int idx, void *data) +{ + if(array_list_expand_internal(arr, idx+1)) return -1; + if(arr->array[idx]) arr->free_fn(arr->array[idx]); + arr->array[idx] = data; + if(arr->length <= idx) arr->length = idx + 1; + return 0; +} + +int +array_list_add(struct array_list *arr, void *data) +{ + return array_list_put_idx(arr, arr->length, data); +} + +void +array_list_sort(struct array_list *arr, int(*sort_fn)(const void *, const void *)) +{ + qsort(arr->array, arr->length, sizeof(arr->array[0]), + (int (*)(const void *, const void *))sort_fn); +} + +int +array_list_length(struct array_list *arr) +{ + return arr->length; +} ADDED modules/json/c/arraylist.h Index: modules/json/c/arraylist.h ================================================================== --- /dev/null +++ modules/json/c/arraylist.h @@ -0,0 +1,56 @@ +/* + * $Id: arraylist.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _arraylist_h_ +#define _arraylist_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define ARRAY_LIST_DEFAULT_SIZE 32 + +typedef void (array_list_free_fn) (void *data); + +struct array_list +{ + void **array; + int length; + int size; + array_list_free_fn *free_fn; +}; + +extern struct array_list* +array_list_new(array_list_free_fn *free_fn); + +extern void +array_list_free(struct array_list *al); + +extern void* +array_list_get_idx(struct array_list *al, int i); + +extern int +array_list_put_idx(struct array_list *al, int i, void *data); + +extern int +array_list_add(struct array_list *al, void *data); + +extern int +array_list_length(struct array_list *al); + +extern void +array_list_sort(struct array_list *arr, int(*compar)(const void *, const void *)); + +#ifdef __cplusplus +} +#endif + +#endif ADDED modules/json/c/bits.h Index: modules/json/c/bits.h ================================================================== --- /dev/null +++ modules/json/c/bits.h @@ -0,0 +1,28 @@ +/* + * $Id: bits.h,v 1.10 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _bits_h_ +#define _bits_h_ + +#ifndef json_min +#define json_min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef json_max +#define json_max(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#define hexdigit(x) (((x) <= '9') ? (x) - '0' : ((x) & 7) + 9) +#define error_ptr(error) ((void*)error) +#define error_description(error) (json_tokener_errors[error]) +#define is_error(ptr) (ptr == NULL) + +#endif ADDED modules/json/c/config.h Index: modules/json/c/config.h ================================================================== --- /dev/null +++ modules/json/c/config.h @@ -0,0 +1,149 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* Define if .gnu.warning accepts long strings. */ +/* #undef HAS_GNU_WARNING_LONG */ + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `open' function. */ +#define HAVE_OPEN 1 + +/* Define to 1 if your system has a GNU libc compatible `realloc' function, + and to 0 otherwise. */ +#define HAVE_REALLOC 1 + +/* Define to 1 if you have the `setlocale' function. */ +#define HAVE_SETLOCALE 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strcasecmp' function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strncasecmp' function. */ +#define HAVE_STRNCASECMP 1 + +/* Define to 1 if you have the `strndup' function. */ +#define HAVE_STRNDUP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYSLOG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_CDEFS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vasprintf' function. */ +#define HAVE_VASPRINTF 1 + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Define to 1 if you have the `vsyslog' function. */ +#define HAVE_VSYSLOG 1 + +/* Public define for json_inttypes.h */ +#define JSON_C_HAVE_INTTYPES_H 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "json-c" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "json-c@googlegroups.com" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "json-c" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "json-c 0.11" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "json-c" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.11" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "0.11" + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to rpl_malloc if the replacement function should be used. */ +/* #undef malloc */ + +/* Define to rpl_realloc if the replacement function should be used. */ +/* #undef realloc */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ ADDED modules/json/c/debug.c Index: modules/json/c/debug.c ================================================================== --- /dev/null +++ modules/json/c/debug.c @@ -0,0 +1,98 @@ +/* + * $Id: debug.c,v 1.5 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include +#include +#include +#include + +#if HAVE_SYSLOG_H +# include +#endif /* HAVE_SYSLOG_H */ + +#if HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ + +#if HAVE_SYS_PARAM_H +#include +#endif /* HAVE_SYS_PARAM_H */ + +#include "debug.h" + +static int _syslog = 0; +static int _debug = 0; + +void mc_set_debug(int debug) { _debug = debug; } +int mc_get_debug(void) { return _debug; } + +extern void mc_set_syslog(int syslog) +{ + _syslog = syslog; +} + +void mc_abort(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_ERR, msg, ap); + } else +#endif + vprintf(msg, ap); + va_end(ap); + exit(1); +} + + +void mc_debug(const char *msg, ...) +{ + va_list ap; + if(_debug) { + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_DEBUG, msg, ap); + } else +#endif + vprintf(msg, ap); + va_end(ap); + } +} + +void mc_error(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_ERR, msg, ap); + } else +#endif + vfprintf(stderr, msg, ap); + va_end(ap); +} + +void mc_info(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_INFO, msg, ap); + } else +#endif + vfprintf(stderr, msg, ap); + va_end(ap); +} ADDED modules/json/c/debug.h Index: modules/json/c/debug.h ================================================================== --- /dev/null +++ modules/json/c/debug.h @@ -0,0 +1,72 @@ +/* + * $Id: debug.h,v 1.5 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void mc_set_debug(int debug); +extern int mc_get_debug(void); + +extern void mc_set_syslog(int syslog); +extern void mc_abort(const char *msg, ...); +extern void mc_debug(const char *msg, ...); +extern void mc_error(const char *msg, ...); +extern void mc_info(const char *msg, ...); + +#ifndef __STRING +#define __STRING(x) #x +#endif + +#ifndef PARSER_BROKEN_FIXED + +#define JASSERT(cond) do {} while(0) + +#else + +#define JASSERT(cond) do { \ + if (!(cond)) { \ + mc_error("cjson assert failure %s:%d : cond \"" __STRING(cond) "failed\n", __FILE__, __LINE__); \ + *(int *)0 = 1;\ + abort(); \ + }\ + } while(0) + +#endif + +#define MC_ABORT(x, ...) mc_abort(x, ##__VA_ARGS__) +#define MC_ERROR(x, ...) mc_error(x, ##__VA_ARGS__) + +#ifdef MC_MAINTAINER_MODE +#define MC_SET_DEBUG(x) mc_set_debug(x) +#define MC_GET_DEBUG() mc_get_debug() +#define MC_SET_SYSLOG(x) mc_set_syslog(x) +#define MC_DEBUG(x, ...) mc_debug(x, ##__VA_ARGS__) +#define MC_INFO(x, ...) mc_info(x, ##__VA_ARGS__) +#else +#define MC_SET_DEBUG(x) if (0) mc_set_debug(x) +#define MC_GET_DEBUG() (0) +#define MC_SET_SYSLOG(x) if (0) mc_set_syslog(x) +#define MC_DEBUG(x, ...) if (0) mc_debug(x, ##__VA_ARGS__) +#define MC_INFO(x, ...) if (0) mc_info(x, ##__VA_ARGS__) +#endif + +#ifdef __cplusplus +} +#endif + +#endif ADDED modules/json/c/json.h Index: modules/json/c/json.h ================================================================== --- /dev/null +++ modules/json/c/json.h @@ -0,0 +1,34 @@ +/* + * $Id: json.h,v 1.6 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_h_ +#define _json_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bits.h" +#include "debug.h" +#include "linkhash.h" +#include "arraylist.h" +#include "json_util.h" +#include "json_object.h" +#include "json_tokener.h" +#include "json_object_iterator.h" +#include "json_c_version.h" + +#ifdef __cplusplus +} +#endif + +#endif ADDED modules/json/c/json_c_version.c Index: modules/json/c/json_c_version.c ================================================================== --- /dev/null +++ modules/json/c/json_c_version.c @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2012 Eric Haszlakiewicz + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + */ +#include "config.h" + +#include "json_c_version.h" + +const char *json_c_version(void) +{ + return JSON_C_VERSION; +} + +int json_c_version_num(void) +{ + return JSON_C_VERSION_NUM; +} + ADDED modules/json/c/json_c_version.h Index: modules/json/c/json_c_version.h ================================================================== --- /dev/null +++ modules/json/c/json_c_version.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2012 Eric Haszlakiewicz + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + */ + +#ifndef _json_c_version_h_ +#define _json_c_version_h_ + +#define JSON_C_MAJOR_VERSION 0 +#define JSON_C_MINOR_VERSION 11 +#define JSON_C_MICRO_VERSION 0 +#define JSON_C_VERSION_NUM ((JSON_C_MAJOR_VERSION << 16) | \ + (JSON_C_MINOR_VERSION << 8) | \ + JSON_C_MICRO_VERSION) +#define JSON_C_VERSION "0.11" + +const char *json_c_version(void); /* Returns JSON_C_VERSION */ +int json_c_version_num(void); /* Returns JSON_C_VERSION_NUM */ + +#endif ADDED modules/json/c/json_config.h Index: modules/json/c/json_config.h ================================================================== --- /dev/null +++ modules/json/c/json_config.h @@ -0,0 +1,4 @@ +/* json_config.h. Generated from json_config.h.in by configure. */ + +/* Define to 1 if you have the header file. */ +#define JSON_C_HAVE_INTTYPES_H 1 ADDED modules/json/c/json_inttypes.h Index: modules/json/c/json_inttypes.h ================================================================== --- /dev/null +++ modules/json/c/json_inttypes.h @@ -0,0 +1,28 @@ + +#ifndef _json_inttypes_h_ +#define _json_inttypes_h_ + +#include "json_config.h" + +#if defined(_MSC_VER) && _MSC_VER < 1700 + +/* Anything less than Visual Studio C++ 10 is missing stdint.h and inttypes.h */ +typedef __int32 int32_t; +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX ((int32_t)_I32_MAX) +typedef __int64 int64_t; +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX ((int64_t)_I64_MAX) +#define PRId64 "I64d" +#define SCNd64 "I64d" + +#else + +#ifdef JSON_C_HAVE_INTTYPES_H +#include +#endif +/* inttypes.h includes stdint.h */ + +#endif + +#endif ADDED modules/json/c/json_object.c Index: modules/json/c/json_object.c ================================================================== --- /dev/null +++ modules/json/c/json_object.c @@ -0,0 +1,782 @@ +/* + * $Id: json_object.c,v 1.17 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "debug.h" +#include "printbuf.h" +#include "linkhash.h" +#include "arraylist.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_object_private.h" +#include "json_util.h" + +#if !defined(HAVE_STRDUP) && defined(_MSC_VER) + /* MSC has the version as _strdup */ +# define strdup _strdup +#elif !defined(HAVE_STRDUP) +# error You do not have strdup on your system. +#endif /* HAVE_STRDUP */ + +#if !defined(HAVE_STRNDUP) + char* strndup(const char* str, size_t n); +#endif /* !HAVE_STRNDUP */ + +// Don't define this. It's not thread-safe. +/* #define REFCOUNT_DEBUG 1 */ + +const char *json_number_chars = "0123456789.+-eE"; +const char *json_hex_chars = "0123456789abcdefABCDEF"; + +static void json_object_generic_delete(struct json_object* jso); +static struct json_object* json_object_new(enum json_type o_type); + +static json_object_to_json_string_fn json_object_object_to_json_string; +static json_object_to_json_string_fn json_object_boolean_to_json_string; +static json_object_to_json_string_fn json_object_int_to_json_string; +static json_object_to_json_string_fn json_object_double_to_json_string; +static json_object_to_json_string_fn json_object_string_to_json_string; +static json_object_to_json_string_fn json_object_array_to_json_string; + + +/* ref count debugging */ + +#ifdef REFCOUNT_DEBUG + +static struct lh_table *json_object_table; + +static void json_object_init(void) __attribute__ ((constructor)); +static void json_object_init(void) { + MC_DEBUG("json_object_init: creating object table\n"); + json_object_table = lh_kptr_table_new(128, "json_object_table", NULL); +} + +static void json_object_fini(void) __attribute__ ((destructor)); +static void json_object_fini(void) { + struct lh_entry *ent; + if(MC_GET_DEBUG()) { + if (json_object_table->count) { + MC_DEBUG("json_object_fini: %d referenced objects at exit\n", + json_object_table->count); + lh_foreach(json_object_table, ent) { + struct json_object* obj = (struct json_object*)ent->v; + MC_DEBUG("\t%s:%p\n", json_type_to_name(obj->o_type), obj); + } + } + } + MC_DEBUG("json_object_fini: freeing object table\n"); + lh_table_free(json_object_table); +} +#endif /* REFCOUNT_DEBUG */ + + +/* string escaping */ + +static int json_escape_str(struct printbuf *pb, char *str, int len) +{ + int pos = 0, start_offset = 0; + unsigned char c; + while (len--) { + c = str[pos]; + switch(c) { + case '\b': + case '\n': + case '\r': + case '\t': + case '\f': + case '"': + case '\\': + case '/': + if(pos - start_offset > 0) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + if(c == '\b') printbuf_memappend(pb, "\\b", 2); + else if(c == '\n') printbuf_memappend(pb, "\\n", 2); + else if(c == '\r') printbuf_memappend(pb, "\\r", 2); + else if(c == '\t') printbuf_memappend(pb, "\\t", 2); + else if(c == '\f') printbuf_memappend(pb, "\\f", 2); + else if(c == '"') printbuf_memappend(pb, "\\\"", 2); + else if(c == '\\') printbuf_memappend(pb, "\\\\", 2); + else if(c == '/') printbuf_memappend(pb, "\\/", 2); + start_offset = ++pos; + break; + default: + if(c < ' ') { + if(pos - start_offset > 0) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + sprintbuf(pb, "\\u00%c%c", + json_hex_chars[c >> 4], + json_hex_chars[c & 0xf]); + start_offset = ++pos; + } else pos++; + } + } + if(pos - start_offset > 0) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + return 0; +} + + +/* reference counting */ + +extern struct json_object* json_object_get(struct json_object *jso) +{ + if(jso) { + jso->_ref_count++; + } + return jso; +} + +int json_object_put(struct json_object *jso) +{ + if(jso) + { + jso->_ref_count--; + if(!jso->_ref_count) + { + if (jso->_user_delete) + jso->_user_delete(jso, jso->_userdata); + jso->_delete(jso); + return 1; + } + } + return 0; +} + + +/* generic object construction and destruction parts */ + +static void json_object_generic_delete(struct json_object* jso) +{ +#ifdef REFCOUNT_DEBUG + MC_DEBUG("json_object_delete_%s: %p\n", + json_type_to_name(jso->o_type), jso); + lh_table_delete(json_object_table, jso); +#endif /* REFCOUNT_DEBUG */ + printbuf_free(jso->_pb); + free(jso); +} + +static struct json_object* json_object_new(enum json_type o_type) +{ + struct json_object *jso; + + jso = (struct json_object*)calloc(sizeof(struct json_object), 1); + if(!jso) return NULL; + jso->o_type = o_type; + jso->_ref_count = 1; + jso->_delete = &json_object_generic_delete; +#ifdef REFCOUNT_DEBUG + lh_table_insert(json_object_table, jso, jso); + MC_DEBUG("json_object_new_%s: %p\n", json_type_to_name(jso->o_type), jso); +#endif /* REFCOUNT_DEBUG */ + return jso; +} + + +/* type checking functions */ + +int json_object_is_type(struct json_object *jso, enum json_type type) +{ + if (!jso) + return (type == json_type_null); + return (jso->o_type == type); +} + +enum json_type json_object_get_type(struct json_object *jso) +{ + if (!jso) + return json_type_null; + return jso->o_type; +} + +/* set a custom conversion to string */ + +void json_object_set_serializer(json_object *jso, + json_object_to_json_string_fn to_string_func, + void *userdata, + json_object_delete_fn *user_delete) +{ + // First, clean up any previously existing user info + if (jso->_user_delete) + { + jso->_user_delete(jso, jso->_userdata); + } + jso->_userdata = NULL; + jso->_user_delete = NULL; + + if (to_string_func == NULL) + { + // Reset to the standard serialization function + switch(jso->o_type) + { + case json_type_null: + jso->_to_json_string = NULL; + break; + case json_type_boolean: + jso->_to_json_string = &json_object_boolean_to_json_string; + break; + case json_type_double: + jso->_to_json_string = &json_object_double_to_json_string; + break; + case json_type_int: + jso->_to_json_string = &json_object_int_to_json_string; + break; + case json_type_object: + jso->_to_json_string = &json_object_object_to_json_string; + break; + case json_type_array: + jso->_to_json_string = &json_object_array_to_json_string; + break; + case json_type_string: + jso->_to_json_string = &json_object_string_to_json_string; + break; + } + return; + } + + jso->_to_json_string = to_string_func; + jso->_userdata = userdata; + jso->_user_delete = user_delete; +} + + +/* extended conversion to string */ + +const char* json_object_to_json_string_ext(struct json_object *jso, int flags) +{ + if (!jso) + return "null"; + + if ((!jso->_pb) && !(jso->_pb = printbuf_new())) + return NULL; + + printbuf_reset(jso->_pb); + + if(jso->_to_json_string(jso, jso->_pb, 0, flags) < 0) + return NULL; + + return jso->_pb->buf; +} + +/* backwards-compatible conversion to string */ + +const char* json_object_to_json_string(struct json_object *jso) +{ + return json_object_to_json_string_ext(jso, JSON_C_TO_STRING_SPACED); +} + +static void indent(struct printbuf *pb, int level, int flags) +{ + if (flags & JSON_C_TO_STRING_PRETTY) + { + printbuf_memset(pb, -1, ' ', level * 2); + } +} + +/* json_object_object */ + +static int json_object_object_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + int had_children = 0; + struct json_object_iter iter; + + sprintbuf(pb, "{" /*}*/); + if (flags & JSON_C_TO_STRING_PRETTY) + sprintbuf(pb, "\n"); + json_object_object_foreachC(jso, iter) + { + if (had_children) + { + sprintbuf(pb, ","); + if (flags & JSON_C_TO_STRING_PRETTY) + sprintbuf(pb, "\n"); + } + had_children = 1; + if (flags & JSON_C_TO_STRING_SPACED) + sprintbuf(pb, " "); + indent(pb, level+1, flags); + sprintbuf(pb, "\""); + json_escape_str(pb, iter.key, strlen(iter.key)); + if (flags & JSON_C_TO_STRING_SPACED) + sprintbuf(pb, "\": "); + else + sprintbuf(pb, "\":"); + if(iter.val == NULL) + sprintbuf(pb, "null"); + else + iter.val->_to_json_string(iter.val, pb, level+1,flags); + } + if (flags & JSON_C_TO_STRING_PRETTY) + { + if (had_children) + sprintbuf(pb, "\n"); + indent(pb,level,flags); + } + if (flags & JSON_C_TO_STRING_SPACED) + return sprintbuf(pb, /*{*/ " }"); + else + return sprintbuf(pb, /*{*/ "}"); +} + + +static void json_object_lh_entry_free(struct lh_entry *ent) +{ + free(ent->k); + json_object_put((struct json_object*)ent->v); +} + +static void json_object_object_delete(struct json_object* jso) +{ + lh_table_free(jso->o.c_object); + json_object_generic_delete(jso); +} + +struct json_object* json_object_new_object(void) +{ + struct json_object *jso = json_object_new(json_type_object); + if(!jso) return NULL; + jso->_delete = &json_object_object_delete; + jso->_to_json_string = &json_object_object_to_json_string; + jso->o.c_object = lh_kchar_table_new(JSON_OBJECT_DEF_HASH_ENTRIES, + NULL, &json_object_lh_entry_free); + return jso; +} + +struct lh_table* json_object_get_object(struct json_object *jso) +{ + if(!jso) return NULL; + switch(jso->o_type) { + case json_type_object: + return jso->o.c_object; + default: + return NULL; + } +} + +void json_object_object_add(struct json_object* jso, const char *key, + struct json_object *val) +{ + // We lookup the entry and replace the value, rather than just deleting + // and re-adding it, so the existing key remains valid. + json_object *existing_value = NULL; + struct lh_entry *existing_entry; + existing_entry = lh_table_lookup_entry(jso->o.c_object, (void*)key); + if (!existing_entry) + { + lh_table_insert(jso->o.c_object, strdup(key), val); + return; + } + existing_value = (void *)existing_entry->v; + if (existing_value) + json_object_put(existing_value); + existing_entry->v = val; +} + +int json_object_object_length(struct json_object *jso) +{ + return lh_table_length(jso->o.c_object); +} + +struct json_object* json_object_object_get(struct json_object* jso, const char *key) +{ + struct json_object *result = NULL; + json_object_object_get_ex(jso, key, &result); + return result; +} + +json_bool json_object_object_get_ex(struct json_object* jso, const char *key, struct json_object **value) +{ + if (value != NULL) + *value = NULL; + + if (NULL == jso) + return FALSE; + + switch(jso->o_type) + { + case json_type_object: + return lh_table_lookup_ex(jso->o.c_object, (void*)key, (void**)value); + default: + if (value != NULL) + *value = NULL; + return FALSE; + } +} + +void json_object_object_del(struct json_object* jso, const char *key) +{ + lh_table_delete(jso->o.c_object, key); +} + + +/* json_object_boolean */ + +static int json_object_boolean_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + if(jso->o.c_boolean) return sprintbuf(pb, "true"); + else return sprintbuf(pb, "false"); +} + +struct json_object* json_object_new_boolean(json_bool b) +{ + struct json_object *jso = json_object_new(json_type_boolean); + if(!jso) return NULL; + jso->_to_json_string = &json_object_boolean_to_json_string; + jso->o.c_boolean = b; + return jso; +} + +json_bool json_object_get_boolean(struct json_object *jso) +{ + if(!jso) return FALSE; + switch(jso->o_type) { + case json_type_boolean: + return jso->o.c_boolean; + case json_type_int: + return (jso->o.c_int64 != 0); + case json_type_double: + return (jso->o.c_double != 0); + case json_type_string: + return (jso->o.c_string.len != 0); + default: + return FALSE; + } +} + + +/* json_object_int */ + +static int json_object_int_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + return sprintbuf(pb, "%"PRId64, jso->o.c_int64); +} + +struct json_object* json_object_new_int(int32_t i) +{ + struct json_object *jso = json_object_new(json_type_int); + if(!jso) return NULL; + jso->_to_json_string = &json_object_int_to_json_string; + jso->o.c_int64 = i; + return jso; +} + +int32_t json_object_get_int(struct json_object *jso) +{ + int64_t cint64; + enum json_type o_type; + + if(!jso) return 0; + + o_type = jso->o_type; + cint64 = jso->o.c_int64; + + if (o_type == json_type_string) + { + /* + * Parse strings into 64-bit numbers, then use the + * 64-to-32-bit number handling below. + */ + if (json_parse_int64(jso->o.c_string.str, &cint64) != 0) + return 0; /* whoops, it didn't work. */ + o_type = json_type_int; + } + + switch(o_type) { + case json_type_int: + /* Make sure we return the correct values for out of range numbers. */ + if (cint64 <= INT32_MIN) + return INT32_MIN; + else if (cint64 >= INT32_MAX) + return INT32_MAX; + else + return (int32_t)cint64; + case json_type_double: + return (int32_t)jso->o.c_double; + case json_type_boolean: + return jso->o.c_boolean; + default: + return 0; + } +} + +struct json_object* json_object_new_int64(int64_t i) +{ + struct json_object *jso = json_object_new(json_type_int); + if(!jso) return NULL; + jso->_to_json_string = &json_object_int_to_json_string; + jso->o.c_int64 = i; + return jso; +} + +int64_t json_object_get_int64(struct json_object *jso) +{ + int64_t cint; + + if(!jso) return 0; + switch(jso->o_type) { + case json_type_int: + return jso->o.c_int64; + case json_type_double: + return (int64_t)jso->o.c_double; + case json_type_boolean: + return jso->o.c_boolean; + case json_type_string: + if (json_parse_int64(jso->o.c_string.str, &cint) == 0) return cint; + default: + return 0; + } +} + + +/* json_object_double */ + +static int json_object_double_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + char buf[128], *p, *q; + int size; + + size = snprintf(buf, 128, "%f", jso->o.c_double); + p = strchr(buf, ','); + if (p) { + *p = '.'; + } else { + p = strchr(buf, '.'); + } + if (p && (flags & JSON_C_TO_STRING_NOZERO)) { + /* last useful digit, always keep 1 zero */ + p++; + for (q=p ; *q ; q++) { + if (*q!='0') p=q; + } + /* drop trailing zeroes */ + *(++p) = 0; + size = p-buf; + } + printbuf_memappend(pb, buf, size); + return size; +} + +struct json_object* json_object_new_double(double d) +{ + struct json_object *jso = json_object_new(json_type_double); + if(!jso) return NULL; + jso->_to_json_string = &json_object_double_to_json_string; + jso->o.c_double = d; + return jso; +} + +double json_object_get_double(struct json_object *jso) +{ + double cdouble; + + if(!jso) return 0.0; + switch(jso->o_type) { + case json_type_double: + return jso->o.c_double; + case json_type_int: + return jso->o.c_int64; + case json_type_boolean: + return jso->o.c_boolean; + case json_type_string: + if(sscanf(jso->o.c_string.str, "%lf", &cdouble) == 1) return cdouble; + default: + return 0.0; + } +} + + +/* json_object_string */ + +static int json_object_string_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + sprintbuf(pb, "\""); + json_escape_str(pb, jso->o.c_string.str, jso->o.c_string.len); + sprintbuf(pb, "\""); + return 0; +} + +static void json_object_string_delete(struct json_object* jso) +{ + free(jso->o.c_string.str); + json_object_generic_delete(jso); +} + +struct json_object* json_object_new_string(const char *s) +{ + struct json_object *jso = json_object_new(json_type_string); + if(!jso) return NULL; + jso->_delete = &json_object_string_delete; + jso->_to_json_string = &json_object_string_to_json_string; + jso->o.c_string.str = strdup(s); + jso->o.c_string.len = strlen(s); + return jso; +} + +struct json_object* json_object_new_string_len(const char *s, int len) +{ + struct json_object *jso = json_object_new(json_type_string); + if(!jso) return NULL; + jso->_delete = &json_object_string_delete; + jso->_to_json_string = &json_object_string_to_json_string; + jso->o.c_string.str = (char*)malloc(len + 1); + memcpy(jso->o.c_string.str, (void *)s, len); + jso->o.c_string.str[len] = '\0'; + jso->o.c_string.len = len; + return jso; +} + +const char* json_object_get_string(struct json_object *jso) +{ + if(!jso) return NULL; + switch(jso->o_type) { + case json_type_string: + return jso->o.c_string.str; + default: + return json_object_to_json_string(jso); + } +} + +int json_object_get_string_len(struct json_object *jso) { + if(!jso) return 0; + switch(jso->o_type) { + case json_type_string: + return jso->o.c_string.len; + default: + return 0; + } +} + + +/* json_object_array */ + +static int json_object_array_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + int had_children = 0; + int ii; + sprintbuf(pb, "["); + if (flags & JSON_C_TO_STRING_PRETTY) + sprintbuf(pb, "\n"); + for(ii=0; ii < json_object_array_length(jso); ii++) + { + struct json_object *val; + if (had_children) + { + sprintbuf(pb, ","); + if (flags & JSON_C_TO_STRING_PRETTY) + sprintbuf(pb, "\n"); + } + had_children = 1; + if (flags & JSON_C_TO_STRING_SPACED) + sprintbuf(pb, " "); + indent(pb, level + 1, flags); + val = json_object_array_get_idx(jso, ii); + if(val == NULL) + sprintbuf(pb, "null"); + else + val->_to_json_string(val, pb, level+1, flags); + } + if (flags & JSON_C_TO_STRING_PRETTY) + { + if (had_children) + sprintbuf(pb, "\n"); + indent(pb,level,flags); + } + + if (flags & JSON_C_TO_STRING_SPACED) + return sprintbuf(pb, " ]"); + else + return sprintbuf(pb, "]"); +} + +static void json_object_array_entry_free(void *data) +{ + json_object_put((struct json_object*)data); +} + +static void json_object_array_delete(struct json_object* jso) +{ + array_list_free(jso->o.c_array); + json_object_generic_delete(jso); +} + +struct json_object* json_object_new_array(void) +{ + struct json_object *jso = json_object_new(json_type_array); + if(!jso) return NULL; + jso->_delete = &json_object_array_delete; + jso->_to_json_string = &json_object_array_to_json_string; + jso->o.c_array = array_list_new(&json_object_array_entry_free); + return jso; +} + +struct array_list* json_object_get_array(struct json_object *jso) +{ + if(!jso) return NULL; + switch(jso->o_type) { + case json_type_array: + return jso->o.c_array; + default: + return NULL; + } +} + +void json_object_array_sort(struct json_object *jso, int(*sort_fn)(const void *, const void *)) +{ + array_list_sort(jso->o.c_array, sort_fn); +} + +int json_object_array_length(struct json_object *jso) +{ + return array_list_length(jso->o.c_array); +} + +int json_object_array_add(struct json_object *jso,struct json_object *val) +{ + return array_list_add(jso->o.c_array, val); +} + +int json_object_array_put_idx(struct json_object *jso, int idx, + struct json_object *val) +{ + return array_list_put_idx(jso->o.c_array, idx, val); +} + +struct json_object* json_object_array_get_idx(struct json_object *jso, + int idx) +{ + return (struct json_object*)array_list_get_idx(jso->o.c_array, idx); +} + ADDED modules/json/c/json_object.h Index: modules/json/c/json_object.h ================================================================== --- /dev/null +++ modules/json/c/json_object.h @@ -0,0 +1,562 @@ +/* + * $Id: json_object.h,v 1.12 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_object_h_ +#define _json_object_h_ + +#include "json_inttypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define JSON_OBJECT_DEF_HASH_ENTRIES 16 + +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes the output + * to have no extra whitespace or formatting applied. + */ +#define JSON_C_TO_STRING_PLAIN 0 +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes the output to have + * minimal whitespace inserted to make things slightly more readable. + */ +#define JSON_C_TO_STRING_SPACED (1<<0) +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes + * the output to be formatted. + * + * See the "Two Space Tab" option at http://jsonformatter.curiousconcept.com/ + * for an example of the format. + */ +#define JSON_C_TO_STRING_PRETTY (1<<1) +/** + * A flag to drop trailing zero for float values + */ +#define JSON_C_TO_STRING_NOZERO (1<<2) + +#undef FALSE +#define FALSE ((json_bool)0) + +#undef TRUE +#define TRUE ((json_bool)1) + +extern const char *json_number_chars; +extern const char *json_hex_chars; + +/* CAW: added for ANSI C iteration correctness */ +struct json_object_iter +{ + char *key; + struct json_object *val; + struct lh_entry *entry; +}; + +/* forward structure definitions */ + +typedef int json_bool; +typedef struct printbuf printbuf; +typedef struct lh_table lh_table; +typedef struct array_list array_list; +typedef struct json_object json_object; +typedef struct json_object_iter json_object_iter; +typedef struct json_tokener json_tokener; + +/** + * Type of custom user delete functions. See json_object_set_serializer. + */ +typedef void (json_object_delete_fn)(struct json_object *jso, void *userdata); + +/** + * Type of a custom serialization function. See json_object_set_serializer. + */ +typedef int (json_object_to_json_string_fn)(struct json_object *jso, + struct printbuf *pb, + int level, + int flags); + +/* supported object types */ + +typedef enum json_type { + /* If you change this, be sure to update json_type_to_name() too */ + json_type_null, + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string, +} json_type; + +/* reference counting functions */ + +/** + * Increment the reference count of json_object, thereby grabbing shared + * ownership of obj. + * + * @param obj the json_object instance + */ +extern struct json_object* json_object_get(struct json_object *obj); + +/** + * Decrement the reference count of json_object and free if it reaches zero. + * You must have ownership of obj prior to doing this or you will cause an + * imbalance in the reference count. + * + * @param obj the json_object instance + * @returns 1 if the object was freed. + */ +int json_object_put(struct json_object *obj); + +/** + * Check if the json_object is of a given type + * @param obj the json_object instance + * @param type one of: + json_type_null (i.e. obj == NULL), + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string, + */ +extern int json_object_is_type(struct json_object *obj, enum json_type type); + +/** + * Get the type of the json_object. See also json_type_to_name() to turn this + * into a string suitable, for instance, for logging. + * + * @param obj the json_object instance + * @returns type being one of: + json_type_null (i.e. obj == NULL), + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string, + */ +extern enum json_type json_object_get_type(struct json_object *obj); + + +/** Stringify object to json format. + * Equivalent to json_object_to_json_string_ext(obj, JSON_C_TO_STRING_SPACED) + * @param obj the json_object instance + * @returns a string in JSON format + */ +extern const char* json_object_to_json_string(struct json_object *obj); + +/** Stringify object to json format + * @param obj the json_object instance + * @param flags formatting options, see JSON_C_TO_STRING_PRETTY and other constants + * @returns a string in JSON format + */ +extern const char* json_object_to_json_string_ext(struct json_object *obj, int +flags); + +/** + * Set a custom serialization function to be used when this particular object + * is converted to a string by json_object_to_json_string. + * + * If a custom serializer is already set on this object, any existing + * user_delete function is called before the new one is set. + * + * If to_string_func is NULL, the other parameters are ignored + * and the default behaviour is reset. + * + * The userdata parameter is optional and may be passed as NULL. If provided, + * it is passed to to_string_func as-is. This parameter may be NULL even + * if user_delete is non-NULL. + * + * The user_delete parameter is optional and may be passed as NULL, even if + * the userdata parameter is non-NULL. It will be called just before the + * json_object is deleted, after it's reference count goes to zero + * (see json_object_put()). + * If this is not provided, it is up to the caller to free the userdata at + * an appropriate time. (i.e. after the json_object is deleted) + * + * @param jso the object to customize + * @param to_string_func the custom serialization function + * @param userdata an optional opaque cookie + * @param user_delete an optional function from freeing userdata + */ +void json_object_set_serializer(json_object *jso, + json_object_to_json_string_fn to_string_func, + void *userdata, + json_object_delete_fn *user_delete); + + + +/* object type methods */ + +/** Create a new empty object with a reference count of 1. The caller of + * this object initially has sole ownership. Remember, when using + * json_object_object_add or json_object_array_put_idx, ownership will + * transfer to the object/array. Call json_object_get if you want to maintain + * shared ownership or also add this object as a child of multiple objects or + * arrays. Any ownerships you acquired but did not transfer must be released + * through json_object_put. + * + * @returns a json_object of type json_type_object + */ +extern struct json_object* json_object_new_object(void); + +/** Get the hashtable of a json_object of type json_type_object + * @param obj the json_object instance + * @returns a linkhash + */ +extern struct lh_table* json_object_get_object(struct json_object *obj); + +/** Get the size of an object in terms of the number of fields it has. + * @param obj the json_object whose length to return + */ +extern int json_object_object_length(struct json_object* obj); + +/** Add an object field to a json_object of type json_type_object + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object, independent of the lifetime of obj, you must wrap the + * passed object with json_object_get. + * + * Upon calling this, the ownership of val transfers to obj. Thus you must + * make sure that you do in fact have ownership over this object. For instance, + * json_object_new_object will give you ownership until you transfer it, + * whereas json_object_object_get does not. + * + * @param obj the json_object instance + * @param key the object field name (a private copy will be duplicated) + * @param val a json_object or NULL member to associate with the given field + */ +extern void json_object_object_add(struct json_object* obj, const char *key, + struct json_object *val); + +/** Get the json_object associate with a given object field + * + * *No* reference counts will be changed. There is no need to manually adjust + * reference counts through the json_object_put/json_object_get methods unless + * you need to have the child (value) reference maintain a different lifetime + * than the owning parent (obj). Ownership of the returned value is retained + * by obj (do not do json_object_put unless you have done a json_object_get). + * If you delete the value from obj (json_object_object_del) and wish to access + * the returned reference afterwards, make sure you have first gotten shared + * ownership through json_object_get (& don't forget to do a json_object_put + * or transfer ownership to prevent a memory leak). + * + * @param obj the json_object instance + * @param key the object field name + * @returns the json_object associated with the given field name + * @deprecated Please use json_object_object_get_ex + */ +extern struct json_object* json_object_object_get(struct json_object* obj, + const char *key); + +/** Get the json_object associated with a given object field. + * + * This returns true if the key is found, false in all other cases (including + * if obj isn't a json_type_object). + * + * *No* reference counts will be changed. There is no need to manually adjust + * reference counts through the json_object_put/json_object_get methods unless + * you need to have the child (value) reference maintain a different lifetime + * than the owning parent (obj). Ownership of value is retained by obj. + * + * @param obj the json_object instance + * @param key the object field name + * @param value a pointer where to store a reference to the json_object + * associated with the given field name. + * + * It is safe to pass a NULL value. + * @returns whether or not the key exists + */ +extern json_bool json_object_object_get_ex(struct json_object* obj, + const char *key, + struct json_object **value); + +/** Delete the given json_object field + * + * The reference count will be decremented for the deleted object. If there + * are no more owners of the value represented by this key, then the value is + * freed. Otherwise, the reference to the value will remain in memory. + * + * @param obj the json_object instance + * @param key the object field name + */ +extern void json_object_object_del(struct json_object* obj, const char *key); + +/** + * Iterate through all keys and values of an object. + * + * Adding keys to the object while iterating is NOT allowed. + * + * Deleting an existing key, or replacing an existing key with a + * new value IS allowed. + * + * @param obj the json_object instance + * @param key the local name for the char* key variable defined in the body + * @param val the local name for the json_object* object variable defined in + * the body + */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && __STDC_VERSION__ >= 199901L + +# define json_object_object_foreach(obj,key,val) \ + char *key; \ + struct json_object *val __attribute__((__unused__)); \ + for(struct lh_entry *entry ## key = json_object_get_object(obj)->head, *entry_next ## key = NULL; \ + ({ if(entry ## key) { \ + key = (char*)entry ## key->k; \ + val = (struct json_object*)entry ## key->v; \ + entry_next ## key = entry ## key->next; \ + } ; entry ## key; }); \ + entry ## key = entry_next ## key ) + +#else /* ANSI C or MSC */ + +# define json_object_object_foreach(obj,key,val) \ + char *key;\ + struct json_object *val; \ + struct lh_entry *entry ## key; \ + struct lh_entry *entry_next ## key = NULL; \ + for(entry ## key = json_object_get_object(obj)->head; \ + (entry ## key ? ( \ + key = (char*)entry ## key->k, \ + val = (struct json_object*)entry ## key->v, \ + entry_next ## key = entry ## key->next, \ + entry ## key) : 0); \ + entry ## key = entry_next ## key) + +#endif /* defined(__GNUC__) && !defined(__STRICT_ANSI__) && __STDC_VERSION__ >= 199901L */ + +/** Iterate through all keys and values of an object (ANSI C Safe) + * @param obj the json_object instance + * @param iter the object iterator + */ +#define json_object_object_foreachC(obj,iter) \ + for(iter.entry = json_object_get_object(obj)->head; (iter.entry ? (iter.key = (char*)iter.entry->k, iter.val = (struct json_object*)iter.entry->v, iter.entry) : 0); iter.entry = iter.entry->next) + +/* Array type methods */ + +/** Create a new empty json_object of type json_type_array + * @returns a json_object of type json_type_array + */ +extern struct json_object* json_object_new_array(void); + +/** Get the arraylist of a json_object of type json_type_array + * @param obj the json_object instance + * @returns an arraylist + */ +extern struct array_list* json_object_get_array(struct json_object *obj); + +/** Get the length of a json_object of type json_type_array + * @param obj the json_object instance + * @returns an int + */ +extern int json_object_array_length(struct json_object *obj); + +/** Sorts the elements of jso of type json_type_array +* +* Pointers to the json_object pointers will be passed as the two arguments +* to @sort_fn +* +* @param obj the json_object instance +* @param sort_fn a sorting function +*/ +extern void json_object_array_sort(struct json_object *jso, int(*sort_fn)(const void *, const void *)); + +/** Add an element to the end of a json_object of type json_type_array + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object you must wrap the passed object with json_object_get + * + * @param obj the json_object instance + * @param val the json_object to be added + */ +extern int json_object_array_add(struct json_object *obj, + struct json_object *val); + +/** Insert or replace an element at a specified index in an array (a json_object of type json_type_array) + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object you must wrap the passed object with json_object_get + * + * The reference count of a replaced object will be decremented. + * + * The array size will be automatically be expanded to the size of the + * index if the index is larger than the current size. + * + * @param obj the json_object instance + * @param idx the index to insert the element at + * @param val the json_object to be added + */ +extern int json_object_array_put_idx(struct json_object *obj, int idx, + struct json_object *val); + +/** Get the element at specificed index of the array (a json_object of type json_type_array) + * @param obj the json_object instance + * @param idx the index to get the element at + * @returns the json_object at the specified index (or NULL) + */ +extern struct json_object* json_object_array_get_idx(struct json_object *obj, + int idx); + +/* json_bool type methods */ + +/** Create a new empty json_object of type json_type_boolean + * @param b a json_bool TRUE or FALSE (0 or 1) + * @returns a json_object of type json_type_boolean + */ +extern struct json_object* json_object_new_boolean(json_bool b); + +/** Get the json_bool value of a json_object + * + * The type is coerced to a json_bool if the passed object is not a json_bool. + * integer and double objects will return FALSE if there value is zero + * or TRUE otherwise. If the passed object is a string it will return + * TRUE if it has a non zero length. If any other object type is passed + * TRUE will be returned if the object is not NULL. + * + * @param obj the json_object instance + * @returns a json_bool + */ +extern json_bool json_object_get_boolean(struct json_object *obj); + + +/* int type methods */ + +/** Create a new empty json_object of type json_type_int + * Note that values are stored as 64-bit values internally. + * To ensure the full range is maintained, use json_object_new_int64 instead. + * @param i the integer + * @returns a json_object of type json_type_int + */ +extern struct json_object* json_object_new_int(int32_t i); + + +/** Create a new empty json_object of type json_type_int + * @param i the integer + * @returns a json_object of type json_type_int + */ +extern struct json_object* json_object_new_int64(int64_t i); + + +/** Get the int value of a json_object + * + * The type is coerced to a int if the passed object is not a int. + * double objects will return their integer conversion. Strings will be + * parsed as an integer. If no conversion exists then 0 is returned + * and errno is set to EINVAL. null is equivalent to 0 (no error values set) + * + * Note that integers are stored internally as 64-bit values. + * If the value of too big or too small to fit into 32-bit, INT32_MAX or + * INT32_MIN are returned, respectively. + * + * @param obj the json_object instance + * @returns an int + */ +extern int32_t json_object_get_int(struct json_object *obj); + +/** Get the int value of a json_object + * + * The type is coerced to a int64 if the passed object is not a int64. + * double objects will return their int64 conversion. Strings will be + * parsed as an int64. If no conversion exists then 0 is returned. + * + * NOTE: Set errno to 0 directly before a call to this function to determine + * whether or not conversion was successful (it does not clear the value for + * you). + * + * @param obj the json_object instance + * @returns an int64 + */ +extern int64_t json_object_get_int64(struct json_object *obj); + + +/* double type methods */ + +/** Create a new empty json_object of type json_type_double + * @param d the double + * @returns a json_object of type json_type_double + */ +extern struct json_object* json_object_new_double(double d); + +/** Get the double floating point value of a json_object + * + * The type is coerced to a double if the passed object is not a double. + * integer objects will return their double conversion. Strings will be + * parsed as a double. If no conversion exists then 0.0 is returned and + * errno is set to EINVAL. null is equivalent to 0 (no error values set) + * + * If the value is too big to fit in a double, then the value is set to + * the closest infinity with errno set to ERANGE. If strings cannot be + * converted to their double value, then EINVAL is set & NaN is returned. + * + * Arrays of length 0 are interpreted as 0 (with no error flags set). + * Arrays of length 1 are effectively cast to the equivalent object and + * converted using the above rules. All other arrays set the error to + * EINVAL & return NaN. + * + * NOTE: Set errno to 0 directly before a call to this function to + * determine whether or not conversion was successful (it does not clear + * the value for you). + * + * @param obj the json_object instance + * @returns a double floating point number + */ +extern double json_object_get_double(struct json_object *obj); + + +/* string type methods */ + +/** Create a new empty json_object of type json_type_string + * + * A copy of the string is made and the memory is managed by the json_object + * + * @param s the string + * @returns a json_object of type json_type_string + */ +extern struct json_object* json_object_new_string(const char *s); + +extern struct json_object* json_object_new_string_len(const char *s, int len); + +/** Get the string value of a json_object + * + * If the passed object is not of type json_type_string then the JSON + * representation of the object is returned. + * + * The returned string memory is managed by the json_object and will + * be freed when the reference count of the json_object drops to zero. + * + * @param obj the json_object instance + * @returns a string + */ +extern const char* json_object_get_string(struct json_object *obj); + +/** Get the string length of a json_object + * + * If the passed object is not of type json_type_string then zero + * will be returned. + * + * @param obj the json_object instance + * @returns int + */ +extern int json_object_get_string_len(struct json_object *obj); + +#ifdef __cplusplus +} +#endif + +#endif ADDED modules/json/c/json_object_iterator.c Index: modules/json/c/json_object_iterator.c ================================================================== --- /dev/null +++ modules/json/c/json_object_iterator.c @@ -0,0 +1,168 @@ +/** +******************************************************************************* +* @file json_object_iterator.c +* +* Copyright (c) 2009-2012 Hewlett-Packard Development Company, L.P. +* +* This library is free software; you can redistribute it and/or modify +* it under the terms of the MIT license. See COPYING for details. +* +* @brief json-c forces clients to use its private data +* structures for JSON Object iteration. This API +* implementation corrects that by abstracting the +* private json-c details. +* +******************************************************************************* +*/ + +#include + +#include "json.h" +#include "json_object_private.h" + +#include "json_object_iterator.h" + +/** + * How It Works + * + * For each JSON Object, json-c maintains a linked list of zero + * or more lh_entry (link-hash entry) structures inside the + * Object's link-hash table (lh_table). + * + * Each lh_entry structure on the JSON Object's linked list + * represents a single name/value pair. The "next" field of the + * last lh_entry in the list is set to NULL, which terminates + * the list. + * + * We represent a valid iterator that refers to an actual + * name/value pair via a pointer to the pair's lh_entry + * structure set as the iterator's opaque_ field. + * + * We follow json-c's current pair list representation by + * representing a valid "end" iterator (one that refers past the + * last pair) with a NULL value in the iterator's opaque_ field. + * + * A JSON Object without any pairs in it will have the "head" + * field of its lh_table structure set to NULL. For such an + * object, json_object_iter_begin will return an iterator with + * the opaque_ field set to NULL, which is equivalent to the + * "end" iterator. + * + * When iterating, we simply update the iterator's opaque_ field + * to point to the next lh_entry structure in the linked list. + * opaque_ will become NULL once we iterate past the last pair + * in the list, which makes the iterator equivalent to the "end" + * iterator. + */ + +/// Our current representation of the "end" iterator; +/// +/// @note May not always be NULL +static const void* kObjectEndIterValue = NULL; + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_begin(struct json_object* obj) +{ + struct json_object_iterator iter; + struct lh_table* pTable; + + /// @note json_object_get_object will return NULL if passed NULL + /// or a non-json_type_object instance + pTable = json_object_get_object(obj); + JASSERT(NULL != pTable); + + /// @note For a pair-less Object, head is NULL, which matches our + /// definition of the "end" iterator + iter.opaque_ = pTable->head; + return iter; +} + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_end(const struct json_object* obj) +{ + struct json_object_iterator iter; + + JASSERT(NULL != obj); + JASSERT(json_object_is_type(obj, json_type_object)); + + iter.opaque_ = kObjectEndIterValue; + + return iter; +} + +/** + * **************************************************************************** + */ +void +json_object_iter_next(struct json_object_iterator* iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + iter->opaque_ = ((struct lh_entry *)iter->opaque_)->next; +} + + +/** + * **************************************************************************** + */ +const char* +json_object_iter_peek_name(const struct json_object_iterator* iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + return (const char*)(((struct lh_entry *)iter->opaque_)->k); +} + + +/** + * **************************************************************************** + */ +struct json_object* +json_object_iter_peek_value(const struct json_object_iterator* iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + return (struct json_object*)(((struct lh_entry *)iter->opaque_)->v); +} + + +/** + * **************************************************************************** + */ +json_bool +json_object_iter_equal(const struct json_object_iterator* iter1, + const struct json_object_iterator* iter2) +{ + JASSERT(NULL != iter1); + JASSERT(NULL != iter2); + + return (iter1->opaque_ == iter2->opaque_); +} + + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_init_default(void) +{ + struct json_object_iterator iter; + + /** + * @note Make this a negative, invalid value, such that + * accidental access to it would likely be trapped by the + * hardware as an invalid address. + */ + iter.opaque_ = NULL; + + return iter; +} ADDED modules/json/c/json_object_iterator.h Index: modules/json/c/json_object_iterator.h ================================================================== --- /dev/null +++ modules/json/c/json_object_iterator.h @@ -0,0 +1,239 @@ +/** +******************************************************************************* +* @file json_object_iterator.h +* +* Copyright (c) 2009-2012 Hewlett-Packard Development Company, L.P. +* +* This library is free software; you can redistribute it and/or modify +* it under the terms of the MIT license. See COPYING for details. +* +* @brief json-c forces clients to use its private data +* structures for JSON Object iteration. This API +* corrects that by abstracting the private json-c +* details. +* +* API attributes:
+* * Thread-safe: NO
+* * Reentrant: NO +* +******************************************************************************* +*/ + + +#ifndef JSON_OBJECT_ITERATOR_H +#define JSON_OBJECT_ITERATOR_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Forward declaration for the opaque iterator information. + */ +struct json_object_iter_info_; + +/** + * The opaque iterator that references a name/value pair within + * a JSON Object instance or the "end" iterator value. + */ +struct json_object_iterator { + const void* opaque_; +}; + + +/** + * forward declaration of json-c's JSON value instance structure + */ +struct json_object; + + +/** + * Initializes an iterator structure to a "default" value that + * is convenient for initializing an iterator variable to a + * default state (e.g., initialization list in a class' + * constructor). + * + * @code + * struct json_object_iterator iter = json_object_iter_init_default(); + * MyClass() : iter_(json_object_iter_init_default()) + * @endcode + * + * @note The initialized value doesn't reference any specific + * pair, is considered an invalid iterator, and MUST NOT + * be passed to any json-c API that expects a valid + * iterator. + * + * @note User and internal code MUST NOT make any assumptions + * about and dependencies on the value of the "default" + * iterator value. + * + * @return json_object_iterator + */ +struct json_object_iterator +json_object_iter_init_default(void); + +/** Retrieves an iterator to the first pair of the JSON Object. + * + * @warning Any modification of the underlying pair invalidates all + * iterators to that pair. + * + * @param obj JSON Object instance (MUST be of type json_object) + * + * @return json_object_iterator If the JSON Object has at + * least one pair, on return, the iterator refers + * to the first pair. If the JSON Object doesn't + * have any pairs, the returned iterator is + * equivalent to the "end" iterator for the same + * JSON Object instance. + * + * @code + * struct json_object_iterator it; + * struct json_object_iterator itEnd; + * struct json_object* obj; + * + * obj = json_tokener_parse("{'first':'george', 'age':100}"); + * it = json_object_iter_begin(obj); + * itEnd = json_object_iter_end(obj); + * + * while (!json_object_iter_equal(&it, &itEnd)) { + * printf("%s\n", + * json_object_iter_peek_name(&it)); + * json_object_iter_next(&it); + * } + * + * @endcode + */ +struct json_object_iterator +json_object_iter_begin(struct json_object* obj); + +/** Retrieves the iterator that represents the position beyond the + * last pair of the given JSON Object instance. + * + * @warning Do NOT write code that assumes that the "end" + * iterator value is NULL, even if it is so in a + * particular instance of the implementation. + * + * @note The reason we do not (and MUST NOT) provide + * "json_object_iter_is_end(json_object_iterator* iter)" + * type of API is because it would limit the underlying + * representation of name/value containment (or force us + * to add additional, otherwise unnecessary, fields to + * the iterator structure). The "end" iterator and the + * equality test method, on the other hand, permit us to + * cleanly abstract pretty much any reasonable underlying + * representation without burdening the iterator + * structure with unnecessary data. + * + * @note For performance reasons, memorize the "end" iterator prior + * to any loop. + * + * @param obj JSON Object instance (MUST be of type json_object) + * + * @return json_object_iterator On return, the iterator refers + * to the "end" of the Object instance's pairs + * (i.e., NOT the last pair, but "beyond the last + * pair" value) + */ +struct json_object_iterator +json_object_iter_end(const struct json_object* obj); + +/** Returns an iterator to the next pair, if any + * + * @warning Any modification of the underlying pair + * invalidates all iterators to that pair. + * + * @param iter [IN/OUT] Pointer to iterator that references a + * name/value pair; MUST be a valid, non-end iterator. + * WARNING: bad things will happen if invalid or "end" + * iterator is passed. Upon return will contain the + * reference to the next pair if there is one; if there + * are no more pairs, will contain the "end" iterator + * value, which may be compared against the return value + * of json_object_iter_end() for the same JSON Object + * instance. + */ +void +json_object_iter_next(struct json_object_iterator* iter); + + +/** Returns a const pointer to the name of the pair referenced + * by the given iterator. + * + * @param iter pointer to iterator that references a name/value + * pair; MUST be a valid, non-end iterator. + * + * @warning bad things will happen if an invalid or + * "end" iterator is passed. + * + * @return const char* Pointer to the name of the referenced + * name/value pair. The name memory belongs to the + * name/value pair, will be freed when the pair is + * deleted or modified, and MUST NOT be modified or + * freed by the user. + */ +const char* +json_object_iter_peek_name(const struct json_object_iterator* iter); + + +/** Returns a pointer to the json-c instance representing the + * value of the referenced name/value pair, without altering + * the instance's reference count. + * + * @param iter pointer to iterator that references a name/value + * pair; MUST be a valid, non-end iterator. + * + * @warning bad things will happen if invalid or + * "end" iterator is passed. + * + * @return struct json_object* Pointer to the json-c value + * instance of the referenced name/value pair; the + * value's reference count is not changed by this + * function: if you plan to hold on to this json-c node, + * take a look at json_object_get() and + * json_object_put(). IMPORTANT: json-c API represents + * the JSON Null value as a NULL json_object instance + * pointer. + */ +struct json_object* +json_object_iter_peek_value(const struct json_object_iterator* iter); + + +/** Tests two iterators for equality. Typically used to test + * for end of iteration by comparing an iterator to the + * corresponding "end" iterator (that was derived from the same + * JSON Object instance). + * + * @note The reason we do not (and MUST NOT) provide + * "json_object_iter_is_end(json_object_iterator* iter)" + * type of API is because it would limit the underlying + * representation of name/value containment (or force us + * to add additional, otherwise unnecessary, fields to + * the iterator structure). The equality test method, on + * the other hand, permits us to cleanly abstract pretty + * much any reasonable underlying representation. + * + * @param iter1 Pointer to first valid, non-NULL iterator + * @param iter2 POinter to second valid, non-NULL iterator + * + * @warning if a NULL iterator pointer or an uninitialized + * or invalid iterator, or iterators derived from + * different JSON Object instances are passed, bad things + * will happen! + * + * @return json_bool non-zero if iterators are equal (i.e., both + * reference the same name/value pair or are both at + * "end"); zero if they are not equal. + */ +json_bool +json_object_iter_equal(const struct json_object_iterator* iter1, + const struct json_object_iterator* iter2); + + +#ifdef __cplusplus +} +#endif + + +#endif // JSON_OBJECT_ITERATOR_H ADDED modules/json/c/json_object_private.h Index: modules/json/c/json_object_private.h ================================================================== --- /dev/null +++ modules/json/c/json_object_private.h @@ -0,0 +1,47 @@ +/* + * $Id: json_object_private.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_object_private_h_ +#define _json_object_private_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (json_object_private_delete_fn)(struct json_object *o); + +struct json_object +{ + enum json_type o_type; + json_object_private_delete_fn *_delete; + json_object_to_json_string_fn *_to_json_string; + int _ref_count; + struct printbuf *_pb; + union data { + json_bool c_boolean; + double c_double; + int64_t c_int64; + struct lh_table *c_object; + struct array_list *c_array; + struct { + char *str; + int len; + } c_string; + } o; + json_object_delete_fn *_user_delete; + void *_userdata; +}; + +#ifdef __cplusplus +} +#endif + +#endif ADDED modules/json/c/json_tokener.c Index: modules/json/c/json_tokener.c ================================================================== --- /dev/null +++ modules/json/c/json_tokener.c @@ -0,0 +1,792 @@ +/* + * $Id: json_tokener.c,v 1.20 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + * + * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. + * The copyrights to the contents of this file are licensed under the MIT License + * (http://www.opensource.org/licenses/mit-license.php) + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "bits.h" +#include "debug.h" +#include "printbuf.h" +#include "arraylist.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_tokener.h" +#include "json_util.h" + +#ifdef HAVE_LOCALE_H +#include +#endif /* HAVE_LOCALE_H */ + +#if !HAVE_STRDUP && defined(_MSC_VER) + /* MSC has the version as _strdup */ +# define strdup _strdup +#elif !HAVE_STRDUP +# error You do not have strdup on your system. +#endif /* HAVE_STRDUP */ + +#if !HAVE_STRNCASECMP && defined(_MSC_VER) + /* MSC has the version as _strnicmp */ +# define strncasecmp _strnicmp +#elif !HAVE_STRNCASECMP +# error You do not have strncasecmp on your system. +#endif /* HAVE_STRNCASECMP */ + +static const char* json_null_str = "null"; +static const char* json_true_str = "true"; +static const char* json_false_str = "false"; + +// XXX after v0.10 this array will become static: +const char* json_tokener_errors[] = { + "success", + "continue", + "nesting too deep", + "unexpected end of data", + "unexpected character", + "null expected", + "boolean expected", + "number expected", + "array value separator ',' expected", + "quoted object property name expected", + "object property name separator ':' expected", + "object value separator ',' expected", + "invalid string sequence", + "expected comment", +}; + +const char *json_tokener_error_desc(enum json_tokener_error jerr) +{ + int jerr_int = (int)jerr; + if (jerr_int < 0 || jerr_int > (int)sizeof(json_tokener_errors)) + return "Unknown error, invalid json_tokener_error value passed to json_tokener_error_desc()"; + return json_tokener_errors[jerr]; +} + +enum json_tokener_error json_tokener_get_error(json_tokener *tok) +{ + return tok->err; +} + +/* Stuff for decoding unicode sequences */ +#define IS_HIGH_SURROGATE(uc) (((uc) & 0xFC00) == 0xD800) +#define IS_LOW_SURROGATE(uc) (((uc) & 0xFC00) == 0xDC00) +#define DECODE_SURROGATE_PAIR(hi,lo) ((((hi) & 0x3FF) << 10) + ((lo) & 0x3FF) + 0x10000) +static unsigned char utf8_replacement_char[3] = { 0xEF, 0xBF, 0xBD }; + +struct json_tokener* json_tokener_new_ex(int depth) +{ + struct json_tokener *tok; + + tok = (struct json_tokener*)calloc(1, sizeof(struct json_tokener)); + if (!tok) return NULL; + tok->stack = (struct json_tokener_srec *)calloc(depth, sizeof(struct json_tokener_srec)); + if (!tok->stack) { + free(tok); + return NULL; + } + tok->pb = printbuf_new(); + tok->max_depth = depth; + json_tokener_reset(tok); + return tok; +} + +struct json_tokener* json_tokener_new(void) +{ + return json_tokener_new_ex(JSON_TOKENER_DEFAULT_DEPTH); +} + +void json_tokener_free(struct json_tokener *tok) +{ + json_tokener_reset(tok); + if (tok->pb) printbuf_free(tok->pb); + if (tok->stack) free(tok->stack); + free(tok); +} + +static void json_tokener_reset_level(struct json_tokener *tok, int depth) +{ + tok->stack[depth].state = json_tokener_state_eatws; + tok->stack[depth].saved_state = json_tokener_state_start; + json_object_put(tok->stack[depth].current); + tok->stack[depth].current = NULL; + free(tok->stack[depth].obj_field_name); + tok->stack[depth].obj_field_name = NULL; +} + +void json_tokener_reset(struct json_tokener *tok) +{ + int i; + if (!tok) + return; + + for(i = tok->depth; i >= 0; i--) + json_tokener_reset_level(tok, i); + tok->depth = 0; + tok->err = json_tokener_success; +} + +struct json_object* json_tokener_parse(const char *str) +{ + enum json_tokener_error jerr_ignored; + struct json_object* obj; + obj = json_tokener_parse_verbose(str, &jerr_ignored); + return obj; +} + +struct json_object* json_tokener_parse_verbose(const char *str, enum json_tokener_error *error) +{ + struct json_tokener* tok; + struct json_object* obj; + + tok = json_tokener_new(); + if (!tok) + return NULL; + obj = json_tokener_parse_ex(tok, str, -1); + *error = tok->err; + if(tok->err != json_tokener_success) { + if (obj != NULL) + json_object_put(obj); + obj = NULL; + } + + json_tokener_free(tok); + return obj; +} + + +#if !HAVE_STRNDUP +/* CAW: compliant version of strndup() */ +char* strndup(const char* str, size_t n) +{ + if(str) { + size_t len = strlen(str); + size_t nn = json_min(len,n); + char* s = (char*)malloc(sizeof(char) * (nn + 1)); + + if(s) { + memcpy(s, str, nn); + s[nn] = '\0'; + } + + return s; + } + + return NULL; +} +#endif + + +#define state tok->stack[tok->depth].state +#define saved_state tok->stack[tok->depth].saved_state +#define current tok->stack[tok->depth].current +#define obj_field_name tok->stack[tok->depth].obj_field_name + +/* Optimization: + * json_tokener_parse_ex() consumed a lot of CPU in its main loop, + * iterating character-by character. A large performance boost is + * achieved by using tighter loops to locally handle units such as + * comments and strings. Loops that handle an entire token within + * their scope also gather entire strings and pass them to + * printbuf_memappend() in a single call, rather than calling + * printbuf_memappend() one char at a time. + * + * PEEK_CHAR() and ADVANCE_CHAR() macros are used for code that is + * common to both the main loop and the tighter loops. + */ + +/* PEEK_CHAR(dest, tok) macro: + * Peeks at the current char and stores it in dest. + * Returns 1 on success, sets tok->err and returns 0 if no more chars. + * Implicit inputs: str, len vars + */ +#define PEEK_CHAR(dest, tok) \ + (((tok)->char_offset == len) ? \ + (((tok)->depth == 0 && state == json_tokener_state_eatws && saved_state == json_tokener_state_finish) ? \ + (((tok)->err = json_tokener_success), 0) \ + : \ + (((tok)->err = json_tokener_continue), 0) \ + ) : \ + (((dest) = *str), 1) \ + ) + +/* ADVANCE_CHAR() macro: + * Incrementes str & tok->char_offset. + * For convenience of existing conditionals, returns the old value of c (0 on eof) + * Implicit inputs: c var + */ +#define ADVANCE_CHAR(str, tok) \ + ( ++(str), ((tok)->char_offset)++, c) + + +/* End optimization macro defs */ + + +struct json_object* json_tokener_parse_ex(struct json_tokener *tok, + const char *str, int len) +{ + struct json_object *obj = NULL; + char c = '\1'; +#ifdef HAVE_SETLOCALE + char *oldlocale=NULL, *tmplocale; + + tmplocale = setlocale(LC_NUMERIC, NULL); + if (tmplocale) oldlocale = strdup(tmplocale); + setlocale(LC_NUMERIC, "C"); +#endif + + tok->char_offset = 0; + tok->err = json_tokener_success; + + while (PEEK_CHAR(c, tok)) { + + redo_char: + switch(state) { + + case json_tokener_state_eatws: + /* Advance until we change state */ + while (isspace((int)c)) { + if ((!ADVANCE_CHAR(str, tok)) || (!PEEK_CHAR(c, tok))) + goto out; + } + if(c == '/') { + printbuf_reset(tok->pb); + printbuf_memappend_fast(tok->pb, &c, 1); + state = json_tokener_state_comment_start; + } else { + state = saved_state; + goto redo_char; + } + break; + + case json_tokener_state_start: + switch(c) { + case '{': + state = json_tokener_state_eatws; + saved_state = json_tokener_state_object_field_start; + current = json_object_new_object(); + break; + case '[': + state = json_tokener_state_eatws; + saved_state = json_tokener_state_array; + current = json_object_new_array(); + break; + case 'N': + case 'n': + state = json_tokener_state_null; + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; + case '"': + case '\'': + state = json_tokener_state_string; + printbuf_reset(tok->pb); + tok->quote_char = c; + break; + case 'T': + case 't': + case 'F': + case 'f': + state = json_tokener_state_boolean; + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; +#if defined(__GNUC__) + case '0' ... '9': +#else + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': +#endif + case '-': + state = json_tokener_state_number; + printbuf_reset(tok->pb); + tok->is_double = 0; + goto redo_char; + default: + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + break; + + case json_tokener_state_finish: + if(tok->depth == 0) goto out; + obj = json_object_get(current); + json_tokener_reset_level(tok, tok->depth); + tok->depth--; + goto redo_char; + + case json_tokener_state_null: + printbuf_memappend_fast(tok->pb, &c, 1); + if(strncasecmp(json_null_str, tok->pb->buf, + json_min(tok->st_pos+1, (int)strlen(json_null_str))) == 0) { + if(tok->st_pos == (int)strlen(json_null_str)) { + current = NULL; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_null; + goto out; + } + tok->st_pos++; + break; + + case json_tokener_state_comment_start: + if(c == '*') { + state = json_tokener_state_comment; + } else if(c == '/') { + state = json_tokener_state_comment_eol; + } else { + tok->err = json_tokener_error_parse_comment; + goto out; + } + printbuf_memappend_fast(tok->pb, &c, 1); + break; + + case json_tokener_state_comment: + { + /* Advance until we change state */ + const char *case_start = str; + while(c != '*') { + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + printbuf_memappend_fast(tok->pb, case_start, 1+str-case_start); + state = json_tokener_state_comment_end; + } + break; + + case json_tokener_state_comment_eol: + { + /* Advance until we change state */ + const char *case_start = str; + while(c != '\n') { + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf); + state = json_tokener_state_eatws; + } + break; + + case json_tokener_state_comment_end: + printbuf_memappend_fast(tok->pb, &c, 1); + if(c == '/') { + MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf); + state = json_tokener_state_eatws; + } else { + state = json_tokener_state_comment; + } + break; + + case json_tokener_state_string: + { + /* Advance until we change state */ + const char *case_start = str; + while(1) { + if(c == tok->quote_char) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + current = json_object_new_string_len(tok->pb->buf, tok->pb->bpos); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + break; + } else if(c == '\\') { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + saved_state = json_tokener_state_string; + state = json_tokener_state_string_escape; + break; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + } + break; + + case json_tokener_state_string_escape: + switch(c) { + case '"': + case '\\': + case '/': + printbuf_memappend_fast(tok->pb, &c, 1); + state = saved_state; + break; + case 'b': + case 'n': + case 'r': + case 't': + case 'f': + if(c == 'b') printbuf_memappend_fast(tok->pb, "\b", 1); + else if(c == 'n') printbuf_memappend_fast(tok->pb, "\n", 1); + else if(c == 'r') printbuf_memappend_fast(tok->pb, "\r", 1); + else if(c == 't') printbuf_memappend_fast(tok->pb, "\t", 1); + else if(c == 'f') printbuf_memappend_fast(tok->pb, "\f", 1); + state = saved_state; + break; + case 'u': + tok->ucs_char = 0; + tok->st_pos = 0; + state = json_tokener_state_escape_unicode; + break; + default: + tok->err = json_tokener_error_parse_string; + goto out; + } + break; + + case json_tokener_state_escape_unicode: + { + unsigned int got_hi_surrogate = 0; + + /* Handle a 4-byte sequence, or two sequences if a surrogate pair */ + while(1) { + if(strchr(json_hex_chars, c)) { + tok->ucs_char += ((unsigned int)hexdigit(c) << ((3-tok->st_pos++)*4)); + if(tok->st_pos == 4) { + unsigned char unescaped_utf[4]; + + if (got_hi_surrogate) { + if (IS_LOW_SURROGATE(tok->ucs_char)) { + /* Recalculate the ucs_char, then fall thru to process normally */ + tok->ucs_char = DECODE_SURROGATE_PAIR(got_hi_surrogate, tok->ucs_char); + } else { + /* Hi surrogate was not followed by a low surrogate */ + /* Replace the hi and process the rest normally */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } + got_hi_surrogate = 0; + } + + if (tok->ucs_char < 0x80) { + unescaped_utf[0] = tok->ucs_char; + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 1); + } else if (tok->ucs_char < 0x800) { + unescaped_utf[0] = 0xc0 | (tok->ucs_char >> 6); + unescaped_utf[1] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 2); + } else if (IS_HIGH_SURROGATE(tok->ucs_char)) { + /* Got a high surrogate. Remember it and look for the + * the beginning of another sequence, which should be the + * low surrogate. + */ + got_hi_surrogate = tok->ucs_char; + /* Not at end, and the next two chars should be "\u" */ + if ((tok->char_offset+1 != len) && + (tok->char_offset+2 != len) && + (str[1] == '\\') && + (str[2] == 'u')) + { + /* Advance through the 16 bit surrogate, and move on to the + * next sequence. The next step is to process the following + * characters. + */ + if( !ADVANCE_CHAR(str, tok) || !ADVANCE_CHAR(str, tok) ) { + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } + /* Advance to the first char of the next sequence and + * continue processing with the next sequence. + */ + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + goto out; + } + tok->ucs_char = 0; + tok->st_pos = 0; + continue; /* other json_tokener_state_escape_unicode */ + } else { + /* Got a high surrogate without another sequence following + * it. Put a replacement char in for the hi surrogate + * and pretend we finished. + */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } + } else if (IS_LOW_SURROGATE(tok->ucs_char)) { + /* Got a low surrogate not preceded by a high */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } else if (tok->ucs_char < 0x10000) { + unescaped_utf[0] = 0xe0 | (tok->ucs_char >> 12); + unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 6) & 0x3f); + unescaped_utf[2] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 3); + } else if (tok->ucs_char < 0x110000) { + unescaped_utf[0] = 0xf0 | ((tok->ucs_char >> 18) & 0x07); + unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 12) & 0x3f); + unescaped_utf[2] = 0x80 | ((tok->ucs_char >> 6) & 0x3f); + unescaped_utf[3] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 4); + } else { + /* Don't know what we got--insert the replacement char */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } + state = saved_state; + break; + } + } else { + tok->err = json_tokener_error_parse_string; + goto out; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + if (got_hi_surrogate) /* Clean up any pending chars */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + goto out; + } + } + } + break; + + case json_tokener_state_boolean: + printbuf_memappend_fast(tok->pb, &c, 1); + if(strncasecmp(json_true_str, tok->pb->buf, + json_min(tok->st_pos+1, (int)strlen(json_true_str))) == 0) { + if(tok->st_pos == (int)strlen(json_true_str)) { + current = json_object_new_boolean(1); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else if(strncasecmp(json_false_str, tok->pb->buf, + json_min(tok->st_pos+1, (int)strlen(json_false_str))) == 0) { + if(tok->st_pos == (int)strlen(json_false_str)) { + current = json_object_new_boolean(0); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_boolean; + goto out; + } + tok->st_pos++; + break; + + case json_tokener_state_number: + { + /* Advance until we change state */ + const char *case_start = str; + int case_len=0; + while(c && strchr(json_number_chars, c)) { + ++case_len; + if(c == '.' || c == 'e' || c == 'E') + tok->is_double = 1; + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, case_len); + goto out; + } + } + if (case_len>0) + printbuf_memappend_fast(tok->pb, case_start, case_len); + } + { + int64_t num64; + double numd; + if (!tok->is_double && json_parse_int64(tok->pb->buf, &num64) == 0) { + current = json_object_new_int64(num64); + } else if(tok->is_double && json_parse_double(tok->pb->buf, &numd) == 0) { + current = json_object_new_double(numd); + } else { + tok->err = json_tokener_error_parse_number; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + break; + + case json_tokener_state_array_after_sep: + case json_tokener_state_array: + if(c == ']') { + if (state == json_tokener_state_array_after_sep && + (tok->flags & JSON_TOKENER_STRICT)) + { + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else { + if(tok->depth >= tok->max_depth-1) { + tok->err = json_tokener_error_depth; + goto out; + } + state = json_tokener_state_array_add; + tok->depth++; + json_tokener_reset_level(tok, tok->depth); + goto redo_char; + } + break; + + case json_tokener_state_array_add: + json_object_array_add(current, obj); + saved_state = json_tokener_state_array_sep; + state = json_tokener_state_eatws; + goto redo_char; + + case json_tokener_state_array_sep: + if(c == ']') { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if(c == ',') { + saved_state = json_tokener_state_array_after_sep; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_array; + goto out; + } + break; + + case json_tokener_state_object_field_start: + case json_tokener_state_object_field_start_after_sep: + if(c == '}') { + if (state == json_tokener_state_object_field_start_after_sep && + (tok->flags & JSON_TOKENER_STRICT)) + { + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if (c == '"' || c == '\'') { + tok->quote_char = c; + printbuf_reset(tok->pb); + state = json_tokener_state_object_field; + } else { + tok->err = json_tokener_error_parse_object_key_name; + goto out; + } + break; + + case json_tokener_state_object_field: + { + /* Advance until we change state */ + const char *case_start = str; + while(1) { + if(c == tok->quote_char) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + obj_field_name = strdup(tok->pb->buf); + saved_state = json_tokener_state_object_field_end; + state = json_tokener_state_eatws; + break; + } else if(c == '\\') { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + saved_state = json_tokener_state_object_field; + state = json_tokener_state_string_escape; + break; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + } + break; + + case json_tokener_state_object_field_end: + if(c == ':') { + saved_state = json_tokener_state_object_value; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_object_key_sep; + goto out; + } + break; + + case json_tokener_state_object_value: + if(tok->depth >= tok->max_depth-1) { + tok->err = json_tokener_error_depth; + goto out; + } + state = json_tokener_state_object_value_add; + tok->depth++; + json_tokener_reset_level(tok, tok->depth); + goto redo_char; + + case json_tokener_state_object_value_add: + json_object_object_add(current, obj_field_name, obj); + free(obj_field_name); + obj_field_name = NULL; + saved_state = json_tokener_state_object_sep; + state = json_tokener_state_eatws; + goto redo_char; + + case json_tokener_state_object_sep: + if(c == '}') { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if(c == ',') { + saved_state = json_tokener_state_object_field_start_after_sep; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_object_value_sep; + goto out; + } + break; + + } + if (!ADVANCE_CHAR(str, tok)) + goto out; + } /* while(POP_CHAR) */ + + out: + if (!c) { /* We hit an eof char (0) */ + if(state != json_tokener_state_finish && + saved_state != json_tokener_state_finish) + tok->err = json_tokener_error_parse_eof; + } + +#ifdef HAVE_SETLOCALE + setlocale(LC_NUMERIC, oldlocale); + if (oldlocale) free(oldlocale); +#endif + + if (tok->err == json_tokener_success) + { + json_object *ret = json_object_get(current); + int ii; + + /* Partially reset, so we parse additional objects on subsequent calls. */ + for(ii = tok->depth; ii >= 0; ii--) + json_tokener_reset_level(tok, ii); + return ret; + } + + MC_DEBUG("json_tokener_parse_ex: error %s at offset %d\n", + json_tokener_errors[tok->err], tok->char_offset); + return NULL; +} + +void json_tokener_set_flags(struct json_tokener *tok, int flags) +{ + tok->flags = flags; +} ADDED modules/json/c/json_tokener.h Index: modules/json/c/json_tokener.h ================================================================== --- /dev/null +++ modules/json/c/json_tokener.h @@ -0,0 +1,209 @@ +/* + * $Id: json_tokener.h,v 1.10 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_tokener_h_ +#define _json_tokener_h_ + +#include +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum json_tokener_error { + json_tokener_success, + json_tokener_continue, + json_tokener_error_depth, + json_tokener_error_parse_eof, + json_tokener_error_parse_unexpected, + json_tokener_error_parse_null, + json_tokener_error_parse_boolean, + json_tokener_error_parse_number, + json_tokener_error_parse_array, + json_tokener_error_parse_object_key_name, + json_tokener_error_parse_object_key_sep, + json_tokener_error_parse_object_value_sep, + json_tokener_error_parse_string, + json_tokener_error_parse_comment +}; + +enum json_tokener_state { + json_tokener_state_eatws, + json_tokener_state_start, + json_tokener_state_finish, + json_tokener_state_null, + json_tokener_state_comment_start, + json_tokener_state_comment, + json_tokener_state_comment_eol, + json_tokener_state_comment_end, + json_tokener_state_string, + json_tokener_state_string_escape, + json_tokener_state_escape_unicode, + json_tokener_state_boolean, + json_tokener_state_number, + json_tokener_state_array, + json_tokener_state_array_add, + json_tokener_state_array_sep, + json_tokener_state_object_field_start, + json_tokener_state_object_field, + json_tokener_state_object_field_end, + json_tokener_state_object_value, + json_tokener_state_object_value_add, + json_tokener_state_object_sep, + json_tokener_state_array_after_sep, + json_tokener_state_object_field_start_after_sep +}; + +struct json_tokener_srec +{ + enum json_tokener_state state, saved_state; + struct json_object *obj; + struct json_object *current; + char *obj_field_name; +}; + +#define JSON_TOKENER_DEFAULT_DEPTH 32 + +struct json_tokener +{ + char *str; + struct printbuf *pb; + int max_depth, depth, is_double, st_pos, char_offset; + enum json_tokener_error err; + unsigned int ucs_char; + char quote_char; + struct json_tokener_srec *stack; + int flags; +}; + +/** + * Be strict when parsing JSON input. Use caution with + * this flag as what is considered valid may become more + * restrictive from one release to the next, causing your + * code to fail on previously working input. + * + * This flag is not set by default. + * + * @see json_tokener_set_flags() + */ +#define JSON_TOKENER_STRICT 0x01 + +/** + * Given an error previously returned by json_tokener_get_error(), + * return a human readable description of the error. + * + * @return a generic error message is returned if an invalid error value is provided. + */ +const char *json_tokener_error_desc(enum json_tokener_error jerr); + +/** + * @b XXX do not use json_tokener_errors directly. + * After v0.10 this will be removed. + * + * See json_tokener_error_desc() instead. + */ +extern const char* json_tokener_errors[]; + +/** + * Retrieve the error caused by the last call to json_tokener_parse_ex(), + * or json_tokener_success if there is no error. + * + * When parsing a JSON string in pieces, if the tokener is in the middle + * of parsing this will return json_tokener_continue. + * + * See also json_tokener_error_desc(). + */ +enum json_tokener_error json_tokener_get_error(struct json_tokener *tok); + +extern struct json_tokener* json_tokener_new(void); +extern struct json_tokener* json_tokener_new_ex(int depth); +extern void json_tokener_free(struct json_tokener *tok); +extern void json_tokener_reset(struct json_tokener *tok); +extern struct json_object* json_tokener_parse(const char *str); +extern struct json_object* json_tokener_parse_verbose(const char *str, enum json_tokener_error *error); + +/** + * Set flags that control how parsing will be done. + */ +extern void json_tokener_set_flags(struct json_tokener *tok, int flags); + +/** + * Parse a string and return a non-NULL json_object if a valid JSON value + * is found. The string does not need to be a JSON object or array; + * it can also be a string, number or boolean value. + * + * A partial JSON string can be parsed. If the parsing is incomplete, + * NULL will be returned and json_tokener_get_error() will be return + * json_tokener_continue. + * json_tokener_parse_ex() can then be called with additional bytes in str + * to continue the parsing. + * + * If json_tokener_parse_ex() returns NULL and the error anything other than + * json_tokener_continue, a fatal error has occurred and parsing must be + * halted. Then tok object must not be re-used until json_tokener_reset() is + * called. + * + * When a valid JSON value is parsed, a non-NULL json_object will be + * returned. Also, json_tokener_get_error() will return json_tokener_success. + * Be sure to check the type with json_object_is_type() or + * json_object_get_type() before using the object. + * + * @b XXX this shouldn't use internal fields: + * Trailing characters after the parsed value do not automatically cause an + * error. It is up to the caller to decide whether to treat this as an + * error or to handle the additional characters, perhaps by parsing another + * json value starting from that point. + * + * Extra characters can be detected by comparing the tok->char_offset against + * the length of the last len parameter passed in. + * + * The tokener does \b not maintain an internal buffer so the caller is + * responsible for calling json_tokener_parse_ex with an appropriate str + * parameter starting with the extra characters. + * + * Example: + * @code +json_object *jobj = NULL; +const char *mystring = NULL; +int stringlen = 0; +enum json_tokener_error jerr; +do { + mystring = ... // get JSON string, e.g. read from file, etc... + stringlen = strlen(mystring); + jobj = json_tokener_parse_ex(tok, mystring, stringlen); +} while ((jerr = json_tokener_get_error(tok)) == json_tokener_continue); +if (jerr != json_tokener_success) +{ + fprintf(stderr, "Error: %s\n", json_tokener_error_desc(jerr)); + // Handle errors, as appropriate for your application. +} +if (tok->char_offset < stringlen) // XXX shouldn't access internal fields +{ + // Handle extra characters after parsed object as desired. + // e.g. issue an error, parse another object from that point, etc... +} +// Success, use jobj here. + +@endcode + * + * @param tok a json_tokener previously allocated with json_tokener_new() + * @param str an string with any valid JSON expression, or portion of. This does not need to be null terminated. + * @param len the length of str + */ +extern struct json_object* json_tokener_parse_ex(struct json_tokener *tok, + const char *str, int len); + +#ifdef __cplusplus +} +#endif + +#endif ADDED modules/json/c/json_util.c Index: modules/json/c/json_util.c ================================================================== --- /dev/null +++ modules/json/c/json_util.c @@ -0,0 +1,299 @@ +/* + * $Id: json_util.c,v 1.4 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" +#undef realloc + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif /* HAVE_SYS_TYPES_H */ + +#ifdef HAVE_SYS_STAT_H +#include +#endif /* HAVE_SYS_STAT_H */ + +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ + +#ifdef HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ + +#ifdef WIN32 +# define WIN32_LEAN_AND_MEAN +# include +# include +#endif /* defined(WIN32) */ + +#if !defined(HAVE_OPEN) && defined(WIN32) +# define open _open +#endif + +#if !defined(HAVE_SNPRINTF) && defined(_MSC_VER) + /* MSC has the version as _snprintf */ +# define snprintf _snprintf +#elif !defined(HAVE_SNPRINTF) +# error You do not have snprintf on your system. +#endif /* HAVE_SNPRINTF */ + +#include "bits.h" +#include "debug.h" +#include "printbuf.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_tokener.h" +#include "json_util.h" + +static int sscanf_is_broken = 0; +static int sscanf_is_broken_testdone = 0; +static void sscanf_is_broken_test(void); + +struct json_object* json_object_from_file(const char *filename) +{ + struct printbuf *pb; + struct json_object *obj; + char buf[JSON_FILE_BUF_SIZE]; + int fd, ret; + + if((fd = open(filename, O_RDONLY)) < 0) { + MC_ERROR("json_object_from_file: error reading file %s: %s\n", + filename, strerror(errno)); + return NULL; + } + if(!(pb = printbuf_new())) { + close(fd); + MC_ERROR("json_object_from_file: printbuf_new failed\n"); + return NULL; + } + while((ret = read(fd, buf, JSON_FILE_BUF_SIZE)) > 0) { + printbuf_memappend(pb, buf, ret); + } + close(fd); + if(ret < 0) { + MC_ABORT("json_object_from_file: error reading file %s: %s\n", + filename, strerror(errno)); + printbuf_free(pb); + return NULL; + } + obj = json_tokener_parse(pb->buf); + printbuf_free(pb); + return obj; +} + +/* extended "format and write to file" function */ + +int json_object_to_file_ext(char *filename, struct json_object *obj, int flags) +{ + const char *json_str; + int fd, ret; + unsigned int wpos, wsize; + + if(!obj) { + MC_ERROR("json_object_to_file: object is null\n"); + return -1; + } + + if((fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0) { + MC_ERROR("json_object_to_file: error opening file %s: %s\n", + filename, strerror(errno)); + return -1; + } + + if(!(json_str = json_object_to_json_string_ext(obj,flags))) { + close(fd); + return -1; + } + + wsize = (unsigned int)(strlen(json_str) & UINT_MAX); /* CAW: probably unnecessary, but the most 64bit safe */ + wpos = 0; + while(wpos < wsize) { + if((ret = write(fd, json_str + wpos, wsize-wpos)) < 0) { + close(fd); + MC_ERROR("json_object_to_file: error writing file %s: %s\n", + filename, strerror(errno)); + return -1; + } + + /* because of the above check for ret < 0, we can safely cast and add */ + wpos += (unsigned int)ret; + } + + close(fd); + return 0; +} + +// backwards compatible "format and write to file" function + +int json_object_to_file(char *filename, struct json_object *obj) +{ + return json_object_to_file_ext(filename, obj, JSON_C_TO_STRING_PLAIN); +} + +int json_parse_double(const char *buf, double *retval) +{ + return (sscanf(buf, "%lf", retval)==1 ? 0 : 1); +} + +/* + * Not all implementations of sscanf actually work properly. + * Check whether the one we're currently using does, and if + * it's broken, enable the workaround code. + */ +static void sscanf_is_broken_test() +{ + int64_t num64; + + (void)sscanf(" -01234567890123456789012345", "%" SCNd64, &num64); + int ret_errno = errno; + int is_int64_min = (num64 == INT64_MIN); + + (void)sscanf(" 01234567890123456789012345", "%" SCNd64, &num64); + int ret_errno2 = errno; + int is_int64_max = (num64 == INT64_MAX); + + if (ret_errno != ERANGE || !is_int64_min || + ret_errno2 != ERANGE || !is_int64_max) + { + MC_DEBUG("sscanf_is_broken_test failed, enabling workaround code\n"); + sscanf_is_broken = 1; + } +} + +int json_parse_int64(const char *buf, int64_t *retval) +{ + int64_t num64; + const char *buf_sig_digits; + int orig_has_neg; + int saved_errno; + + if (!sscanf_is_broken_testdone) + { + sscanf_is_broken_test(); + sscanf_is_broken_testdone = 1; + } + + // Skip leading spaces + while (isspace((int)*buf) && *buf) + buf++; + + errno = 0; // sscanf won't always set errno, so initialize + if (sscanf(buf, "%" SCNd64, &num64) != 1) + { + MC_DEBUG("Failed to parse, sscanf != 1\n"); + return 1; + } + + saved_errno = errno; + buf_sig_digits = buf; + orig_has_neg = 0; + if (*buf_sig_digits == '-') + { + buf_sig_digits++; + orig_has_neg = 1; + } + + // Not all sscanf implementations actually work + if (sscanf_is_broken && saved_errno != ERANGE) + { + char buf_cmp[100]; + char *buf_cmp_start = buf_cmp; + int recheck_has_neg = 0; + int buf_cmp_len; + + // Skip leading zeros, but keep at least one digit + while (buf_sig_digits[0] == '0' && buf_sig_digits[1] != '\0') + buf_sig_digits++; + if (num64 == 0) // assume all sscanf impl's will parse -0 to 0 + orig_has_neg = 0; // "-0" is the same as just plain "0" + + snprintf(buf_cmp_start, sizeof(buf_cmp), "%" PRId64, num64); + if (*buf_cmp_start == '-') + { + recheck_has_neg = 1; + buf_cmp_start++; + } + // No need to skip leading spaces or zeros here. + + buf_cmp_len = strlen(buf_cmp_start); + /** + * If the sign is different, or + * some of the digits are different, or + * there is another digit present in the original string + * then we have NOT successfully parsed the value. + */ + if (orig_has_neg != recheck_has_neg || + strncmp(buf_sig_digits, buf_cmp_start, strlen(buf_cmp_start)) != 0 || + ((int)strlen(buf_sig_digits) != buf_cmp_len && + isdigit((int)buf_sig_digits[buf_cmp_len]) + ) + ) + { + saved_errno = ERANGE; + } + } + + // Not all sscanf impl's set the value properly when out of range. + // Always do this, even for properly functioning implementations, + // since it shouldn't slow things down much. + if (saved_errno == ERANGE) + { + if (orig_has_neg) + num64 = INT64_MIN; + else + num64 = INT64_MAX; + } + *retval = num64; + return 0; +} + +#ifndef HAVE_REALLOC +void* rpl_realloc(void* p, size_t n) +{ + if (n == 0) + n = 1; + if (p == 0) + return malloc(n); + return realloc(p, n); +} +#endif + +#define NELEM(a) (sizeof(a) / sizeof(a[0])) +static const char* json_type_name[] = { + /* If you change this, be sure to update the enum json_type definition too */ + "null", + "boolean", + "double", + "int", + "object", + "array", + "string", +}; + +const char *json_type_to_name(enum json_type o_type) +{ + int o_type_int = (int)o_type; + if (o_type_int < 0 || o_type_int >= (int)NELEM(json_type_name)) + { + MC_ERROR("json_type_to_name: type %d is out of range [0,%d]\n", o_type, NELEM(json_type_name)); + return NULL; + } + return json_type_name[o_type]; +} + ADDED modules/json/c/json_util.h Index: modules/json/c/json_util.h ================================================================== --- /dev/null +++ modules/json/c/json_util.h @@ -0,0 +1,41 @@ +/* + * $Id: json_util.h,v 1.4 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_util_h_ +#define _json_util_h_ + +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define JSON_FILE_BUF_SIZE 4096 + +/* utility functions */ +extern struct json_object* json_object_from_file(const char *filename); +extern int json_object_to_file(char *filename, struct json_object *obj); +extern int json_object_to_file_ext(char *filename, struct json_object *obj, int flags); +extern int json_parse_int64(const char *buf, int64_t *retval); +extern int json_parse_double(const char *buf, double *retval); + + +/** + * Return a string describing the type of the object. + * e.g. "int", or "object", etc... + */ +extern const char *json_type_to_name(enum json_type o_type); + +#ifdef __cplusplus +} +#endif + +#endif ADDED modules/json/c/libjson.c Index: modules/json/c/libjson.c ================================================================== --- /dev/null +++ modules/json/c/libjson.c @@ -0,0 +1,26 @@ + +/* dummy source file for compatibility purposes */ + +#if defined(HAVE_CDEFS_H) +#include +#endif + +#ifndef __warn_references + +#if defined(__GNUC__) && defined (HAS_GNU_WARNING_LONG) + +#define __warn_references(sym,msg) \ + __asm__(".section .gnu" #sym ",\n\t.ascii \"" msg "\"\n\t.text"); + +#else +#define __warn_references(sym,msg) /* nothing */ +#endif + +#endif + +#include "json_object.h" + +__warn_references(json_object_get, "Warning: please link against libjson-c instead of libjson"); + +/* __asm__(".section .gnu.warning." __STRING(sym) \ + " ; .ascii \"" msg "\" ; .text") */ ADDED modules/json/c/linkhash.c Index: modules/json/c/linkhash.c ================================================================== --- /dev/null +++ modules/json/c/linkhash.c @@ -0,0 +1,233 @@ +/* + * $Id: linkhash.c,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "linkhash.h" + +void lh_abort(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + vprintf(msg, ap); + va_end(ap); + exit(1); +} + +unsigned long lh_ptr_hash(const void *k) +{ + /* CAW: refactored to be 64bit nice */ + return (unsigned long)((((ptrdiff_t)k * LH_PRIME) >> 4) & ULONG_MAX); +} + +int lh_ptr_equal(const void *k1, const void *k2) +{ + return (k1 == k2); +} + +unsigned long lh_char_hash(const void *k) +{ + unsigned int h = 0; + const char* data = (const char*)k; + + while( *data!=0 ) h = h*129 + (unsigned int)(*data++) + LH_PRIME; + + return h; +} + +int lh_char_equal(const void *k1, const void *k2) +{ + return (strcmp((const char*)k1, (const char*)k2) == 0); +} + +struct lh_table* lh_table_new(int size, const char *name, + lh_entry_free_fn *free_fn, + lh_hash_fn *hash_fn, + lh_equal_fn *equal_fn) +{ + int i; + struct lh_table *t; + + t = (struct lh_table*)calloc(1, sizeof(struct lh_table)); + if(!t) lh_abort("lh_table_new: calloc failed\n"); + t->count = 0; + t->size = size; + t->name = name; + t->table = (struct lh_entry*)calloc(size, sizeof(struct lh_entry)); + if(!t->table) lh_abort("lh_table_new: calloc failed\n"); + t->free_fn = free_fn; + t->hash_fn = hash_fn; + t->equal_fn = equal_fn; + for(i = 0; i < size; i++) t->table[i].k = LH_EMPTY; + return t; +} + +struct lh_table* lh_kchar_table_new(int size, const char *name, + lh_entry_free_fn *free_fn) +{ + return lh_table_new(size, name, free_fn, lh_char_hash, lh_char_equal); +} + +struct lh_table* lh_kptr_table_new(int size, const char *name, + lh_entry_free_fn *free_fn) +{ + return lh_table_new(size, name, free_fn, lh_ptr_hash, lh_ptr_equal); +} + +void lh_table_resize(struct lh_table *t, int new_size) +{ + struct lh_table *new_t; + struct lh_entry *ent; + + new_t = lh_table_new(new_size, t->name, NULL, t->hash_fn, t->equal_fn); + ent = t->head; + while(ent) { + lh_table_insert(new_t, ent->k, ent->v); + ent = ent->next; + } + free(t->table); + t->table = new_t->table; + t->size = new_size; + t->head = new_t->head; + t->tail = new_t->tail; + t->resizes++; + free(new_t); +} + +void lh_table_free(struct lh_table *t) +{ + struct lh_entry *c; + for(c = t->head; c != NULL; c = c->next) { + if(t->free_fn) { + t->free_fn(c); + } + } + free(t->table); + free(t); +} + + +int lh_table_insert(struct lh_table *t, void *k, const void *v) +{ + unsigned long h, n; + + t->inserts++; + if(t->count >= t->size * LH_LOAD_FACTOR) lh_table_resize(t, t->size * 2); + + h = t->hash_fn(k); + n = h % t->size; + + while( 1 ) { + if(t->table[n].k == LH_EMPTY || t->table[n].k == LH_FREED) break; + t->collisions++; + if ((int)++n == t->size) n = 0; + } + + t->table[n].k = k; + t->table[n].v = v; + t->count++; + + if(t->head == NULL) { + t->head = t->tail = &t->table[n]; + t->table[n].next = t->table[n].prev = NULL; + } else { + t->tail->next = &t->table[n]; + t->table[n].prev = t->tail; + t->table[n].next = NULL; + t->tail = &t->table[n]; + } + + return 0; +} + + +struct lh_entry* lh_table_lookup_entry(struct lh_table *t, const void *k) +{ + unsigned long h = t->hash_fn(k); + unsigned long n = h % t->size; + int count = 0; + + t->lookups++; + while( count < t->size ) { + if(t->table[n].k == LH_EMPTY) return NULL; + if(t->table[n].k != LH_FREED && + t->equal_fn(t->table[n].k, k)) return &t->table[n]; + if ((int)++n == t->size) n = 0; + count++; + } + return NULL; +} + + +const void* lh_table_lookup(struct lh_table *t, const void *k) +{ + void *result; + lh_table_lookup_ex(t, k, &result); + return result; +} + +json_bool lh_table_lookup_ex(struct lh_table* t, const void* k, void **v) +{ + struct lh_entry *e = lh_table_lookup_entry(t, k); + if (e != NULL) { + if (v != NULL) *v = (void *)e->v; + return TRUE; /* key found */ + } + if (v != NULL) *v = NULL; + return FALSE; /* key not found */ +} + +int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e) +{ + ptrdiff_t n = (ptrdiff_t)(e - t->table); /* CAW: fixed to be 64bit nice, still need the crazy negative case... */ + + /* CAW: this is bad, really bad, maybe stack goes other direction on this machine... */ + if(n < 0) { return -2; } + + if(t->table[n].k == LH_EMPTY || t->table[n].k == LH_FREED) return -1; + t->count--; + if(t->free_fn) t->free_fn(e); + t->table[n].v = NULL; + t->table[n].k = LH_FREED; + if(t->tail == &t->table[n] && t->head == &t->table[n]) { + t->head = t->tail = NULL; + } else if (t->head == &t->table[n]) { + t->head->next->prev = NULL; + t->head = t->head->next; + } else if (t->tail == &t->table[n]) { + t->tail->prev->next = NULL; + t->tail = t->tail->prev; + } else { + t->table[n].prev->next = t->table[n].next; + t->table[n].next->prev = t->table[n].prev; + } + t->table[n].next = t->table[n].prev = NULL; + return 0; +} + + +int lh_table_delete(struct lh_table *t, const void *k) +{ + struct lh_entry *e = lh_table_lookup_entry(t, k); + if(!e) return -1; + return lh_table_delete_entry(t, e); +} + +int lh_table_length(struct lh_table *t) +{ + return t->count; +} ADDED modules/json/c/linkhash.h Index: modules/json/c/linkhash.h ================================================================== --- /dev/null +++ modules/json/c/linkhash.h @@ -0,0 +1,292 @@ +/* + * $Id: linkhash.h,v 1.6 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _linkhash_h_ +#define _linkhash_h_ + +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * golden prime used in hash functions + */ +#define LH_PRIME 0x9e370001UL + +/** + * The fraction of filled hash buckets until an insert will cause the table + * to be resized. + * This can range from just above 0 up to 1.0. + */ +#define LH_LOAD_FACTOR 0.66 + +/** + * sentinel pointer value for empty slots + */ +#define LH_EMPTY (void*)-1 + +/** + * sentinel pointer value for freed slots + */ +#define LH_FREED (void*)-2 + +struct lh_entry; + +/** + * callback function prototypes + */ +typedef void (lh_entry_free_fn) (struct lh_entry *e); +/** + * callback function prototypes + */ +typedef unsigned long (lh_hash_fn) (const void *k); +/** + * callback function prototypes + */ +typedef int (lh_equal_fn) (const void *k1, const void *k2); + +/** + * An entry in the hash table + */ +struct lh_entry { + /** + * The key. + */ + void *k; + /** + * The value. + */ + const void *v; + /** + * The next entry + */ + struct lh_entry *next; + /** + * The previous entry. + */ + struct lh_entry *prev; +}; + + +/** + * The hash table structure. + */ +struct lh_table { + /** + * Size of our hash. + */ + int size; + /** + * Numbers of entries. + */ + int count; + + /** + * Number of collisions. + */ + int collisions; + + /** + * Number of resizes. + */ + int resizes; + + /** + * Number of lookups. + */ + int lookups; + + /** + * Number of inserts. + */ + int inserts; + + /** + * Number of deletes. + */ + int deletes; + + /** + * Name of the hash table. + */ + const char *name; + + /** + * The first entry. + */ + struct lh_entry *head; + + /** + * The last entry. + */ + struct lh_entry *tail; + + struct lh_entry *table; + + /** + * A pointer onto the function responsible for freeing an entry. + */ + lh_entry_free_fn *free_fn; + lh_hash_fn *hash_fn; + lh_equal_fn *equal_fn; +}; + + +/** + * Pre-defined hash and equality functions + */ +extern unsigned long lh_ptr_hash(const void *k); +extern int lh_ptr_equal(const void *k1, const void *k2); + +extern unsigned long lh_char_hash(const void *k); +extern int lh_char_equal(const void *k1, const void *k2); + + +/** + * Convenience list iterator. + */ +#define lh_foreach(table, entry) \ +for(entry = table->head; entry; entry = entry->next) + +/** + * lh_foreach_safe allows calling of deletion routine while iterating. + */ +#define lh_foreach_safe(table, entry, tmp) \ +for(entry = table->head; entry && ((tmp = entry->next) || 1); entry = tmp) + + + +/** + * Create a new linkhash table. + * @param size initial table size. The table is automatically resized + * although this incurs a performance penalty. + * @param name the table name. + * @param free_fn callback function used to free memory for entries + * when lh_table_free or lh_table_delete is called. + * If NULL is provided, then memory for keys and values + * must be freed by the caller. + * @param hash_fn function used to hash keys. 2 standard ones are defined: + * lh_ptr_hash and lh_char_hash for hashing pointer values + * and C strings respectively. + * @param equal_fn comparison function to compare keys. 2 standard ones defined: + * lh_ptr_hash and lh_char_hash for comparing pointer values + * and C strings respectively. + * @return a pointer onto the linkhash table. + */ +extern struct lh_table* lh_table_new(int size, const char *name, + lh_entry_free_fn *free_fn, + lh_hash_fn *hash_fn, + lh_equal_fn *equal_fn); + +/** + * Convenience function to create a new linkhash + * table with char keys. + * @param size initial table size. + * @param name table name. + * @param free_fn callback function used to free memory for entries. + * @return a pointer onto the linkhash table. + */ +extern struct lh_table* lh_kchar_table_new(int size, const char *name, + lh_entry_free_fn *free_fn); + + +/** + * Convenience function to create a new linkhash + * table with ptr keys. + * @param size initial table size. + * @param name table name. + * @param free_fn callback function used to free memory for entries. + * @return a pointer onto the linkhash table. + */ +extern struct lh_table* lh_kptr_table_new(int size, const char *name, + lh_entry_free_fn *free_fn); + + +/** + * Free a linkhash table. + * If a callback free function is provided then it is called for all + * entries in the table. + * @param t table to free. + */ +extern void lh_table_free(struct lh_table *t); + + +/** + * Insert a record into the table. + * @param t the table to insert into. + * @param k a pointer to the key to insert. + * @param v a pointer to the value to insert. + */ +extern int lh_table_insert(struct lh_table *t, void *k, const void *v); + + +/** + * Lookup a record into the table. + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @return a pointer to the record structure of the value or NULL if it does not exist. + */ +extern struct lh_entry* lh_table_lookup_entry(struct lh_table *t, const void *k); + +/** + * Lookup a record into the table + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @return a pointer to the found value or NULL if it does not exist. + * @deprecated Use lh_table_lookup_ex instead. + */ +extern const void* lh_table_lookup(struct lh_table *t, const void *k); + +/** + * Lookup a record in the table + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @param v a pointer to a where to store the found value (set to NULL if it doesn't exist). + * @return whether or not the key was found + */ +extern json_bool lh_table_lookup_ex(struct lh_table *t, const void *k, void **v); + +/** + * Delete a record from the table. + * If a callback free function is provided then it is called for the + * for the item being deleted. + * @param t the table to delete from. + * @param e a pointer to the entry to delete. + * @return 0 if the item was deleted. + * @return -1 if it was not found. + */ +extern int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e); + + +/** + * Delete a record from the table. + * If a callback free function is provided then it is called for the + * for the item being deleted. + * @param t the table to delete from. + * @param k a pointer to the key to delete. + * @return 0 if the item was deleted. + * @return -1 if it was not found. + */ +extern int lh_table_delete(struct lh_table *t, const void *k); + +extern int lh_table_length(struct lh_table *t); + +void lh_abort(const char *msg, ...); +void lh_table_resize(struct lh_table *t, int new_size); + +#ifdef __cplusplus +} +#endif + +#endif ADDED modules/json/c/printbuf.c Index: modules/json/c/printbuf.c ================================================================== --- /dev/null +++ modules/json/c/printbuf.c @@ -0,0 +1,192 @@ +/* + * $Id: printbuf.c,v 1.5 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + * + * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. + * The copyrights to the contents of this file are licensed under the MIT License + * (http://www.opensource.org/licenses/mit-license.php) + */ + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_STDARG_H +# include +#else /* !HAVE_STDARG_H */ +# error Not enough var arg support! +#endif /* HAVE_STDARG_H */ + +#include "bits.h" +#include "debug.h" +#include "printbuf.h" + +static int printbuf_extend(struct printbuf *p, int min_size); + +struct printbuf* printbuf_new(void) +{ + struct printbuf *p; + + p = (struct printbuf*)calloc(1, sizeof(struct printbuf)); + if(!p) return NULL; + p->size = 32; + p->bpos = 0; + if(!(p->buf = (char*)malloc(p->size))) { + free(p); + return NULL; + } + return p; +} + + +/** + * Extend the buffer p so it has a size of at least min_size. + * + * If the current size is large enough, nothing is changed. + * + * Note: this does not check the available space! The caller + * is responsible for performing those calculations. + */ +static int printbuf_extend(struct printbuf *p, int min_size) +{ + char *t; + int new_size; + + if (p->size >= min_size) + return 0; + + new_size = json_max(p->size * 2, min_size + 8); +#ifdef PRINTBUF_DEBUG + MC_DEBUG("printbuf_memappend: realloc " + "bpos=%d min_size=%d old_size=%d new_size=%d\n", + p->bpos, min_size, p->size, new_size); +#endif /* PRINTBUF_DEBUG */ + if(!(t = (char*)realloc(p->buf, new_size))) + return -1; + p->size = new_size; + p->buf = t; + return 0; +} + +int printbuf_memappend(struct printbuf *p, const char *buf, int size) +{ + if (p->size <= p->bpos + size + 1) { + if (printbuf_extend(p, p->bpos + size + 1) < 0) + return -1; + } + memcpy(p->buf + p->bpos, buf, size); + p->bpos += size; + p->buf[p->bpos]= '\0'; + return size; +} + +int printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len) +{ + int size_needed; + + if (offset == -1) + offset = pb->bpos; + size_needed = offset + len; + if (pb->size < size_needed) + { + if (printbuf_extend(pb, size_needed) < 0) + return -1; + } + + memset(pb->buf + offset, charvalue, len); + if (pb->bpos < size_needed) + pb->bpos = size_needed; + + return 0; +} + +#if !defined(HAVE_VSNPRINTF) && defined(_MSC_VER) +# define vsnprintf _vsnprintf +#elif !defined(HAVE_VSNPRINTF) /* !HAVE_VSNPRINTF */ +# error Need vsnprintf! +#endif /* !HAVE_VSNPRINTF && defined(WIN32) */ + +#if !defined(HAVE_VASPRINTF) +/* CAW: compliant version of vasprintf */ +static int vasprintf(char **buf, const char *fmt, va_list ap) +{ +#ifndef WIN32 + static char _T_emptybuffer = '\0'; +#endif /* !defined(WIN32) */ + int chars; + char *b; + + if(!buf) { return -1; } + +#ifdef WIN32 + chars = _vscprintf(fmt, ap)+1; +#else /* !defined(WIN32) */ + /* CAW: RAWR! We have to hope to god here that vsnprintf doesn't overwrite + our buffer like on some 64bit sun systems.... but hey, its time to move on */ + chars = vsnprintf(&_T_emptybuffer, 0, fmt, ap)+1; + if(chars < 0) { chars *= -1; } /* CAW: old glibc versions have this problem */ +#endif /* defined(WIN32) */ + + b = (char*)malloc(sizeof(char)*chars); + if(!b) { return -1; } + + if((chars = vsprintf(b, fmt, ap)) < 0) + { + free(b); + } else { + *buf = b; + } + + return chars; +} +#endif /* !HAVE_VASPRINTF */ + +int sprintbuf(struct printbuf *p, const char *msg, ...) +{ + va_list ap; + char *t; + int size; + char buf[128]; + + /* user stack buffer first */ + va_start(ap, msg); + size = vsnprintf(buf, 128, msg, ap); + va_end(ap); + /* if string is greater than stack buffer, then use dynamic string + with vasprintf. Note: some implementation of vsnprintf return -1 + if output is truncated whereas some return the number of bytes that + would have been written - this code handles both cases. */ + if(size == -1 || size > 127) { + va_start(ap, msg); + if((size = vasprintf(&t, msg, ap)) < 0) { va_end(ap); return -1; } + va_end(ap); + printbuf_memappend(p, t, size); + free(t); + return size; + } else { + printbuf_memappend(p, buf, size); + return size; + } +} + +void printbuf_reset(struct printbuf *p) +{ + p->buf[0] = '\0'; + p->bpos = 0; +} + +void printbuf_free(struct printbuf *p) +{ + if(p) { + free(p->buf); + free(p); + } +} ADDED modules/json/c/printbuf.h Index: modules/json/c/printbuf.h ================================================================== --- /dev/null +++ modules/json/c/printbuf.h @@ -0,0 +1,77 @@ +/* + * $Id: printbuf.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + * + * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. + * The copyrights to the contents of this file are licensed under the MIT License + * (http://www.opensource.org/licenses/mit-license.php) + */ + +#ifndef _printbuf_h_ +#define _printbuf_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct printbuf { + char *buf; + int bpos; + int size; +}; + +extern struct printbuf* +printbuf_new(void); + +/* As an optimization, printbuf_memappend_fast is defined as a macro + * that handles copying data if the buffer is large enough; otherwise + * it invokes printbuf_memappend_real() which performs the heavy + * lifting of realloc()ing the buffer and copying data. + * Your code should not use printbuf_memappend directly--use + * printbuf_memappend_fast instead. + */ +extern int +printbuf_memappend(struct printbuf *p, const char *buf, int size); + +#define printbuf_memappend_fast(p, bufptr, bufsize) \ +do { \ + if ((p->size - p->bpos) > bufsize) { \ + memcpy(p->buf + p->bpos, (bufptr), bufsize); \ + p->bpos += bufsize; \ + p->buf[p->bpos]= '\0'; \ + } else { printbuf_memappend(p, (bufptr), bufsize); } \ +} while (0) + +#define printbuf_length(p) ((p)->bpos) + +/** + * Set len bytes of the buffer to charvalue, starting at offset offset. + * Similar to calling memset(x, charvalue, len); + * + * The memory allocated for the buffer is extended as necessary. + * + * If offset is -1, this starts at the end of the current data in the buffer. + */ +extern int +printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len); + +extern int +sprintbuf(struct printbuf *p, const char *msg, ...); + +extern void +printbuf_reset(struct printbuf *p); + +extern void +printbuf_free(struct printbuf *p); + +#ifdef __cplusplus +} +#endif + +#endif ADDED modules/json/json.bench Index: modules/json/json.bench ================================================================== --- /dev/null +++ modules/json/json.bench @@ -0,0 +1,167 @@ +# -*- tcl -*- +# Tcl Benchmark File +# +# This file contains a number of benchmarks for the 'json' parser +# to allow developers to monitor package performance. +# +# (c) 2013 Andreas Kupries + +# We need at least version 8.4 for the package and thus the +# benchmarks. + +if {![package vsatisfies [package present Tcl] 8.4]} { + bench_puts "Need Tcl 8.4+, found Tcl [package present Tcl]" + return +} + +# ### ### ### ######### ######### ######### ########################### +## Setting up the environment ... + +package require Tcl 8.4 + +package forget json + +set self [file join [pwd] [file dirname [info script]]] +set mod [file dirname $self] +set index [file join [file dirname $self] tcllibc pkgIndex.tcl] + +if 1 { + if {[file exists $index]} { + set ::dir [file dirname $index] + uplevel #0 [list source $index] + unset ::dir + package require tcllibc + } +} + +source [file join $self json.tcl] + +# ### ### ### ######### ######### ######### ########################### +## Helpers + +proc cat {f} { + set c [open $f] + set d [read $c] + close $c + return $d +} + +proc iota {n} { + set r {} + while {$n > 0} { + lappend r [json::string2json $n] + incr n -1 + } + return $r +} + +proc iota-dict {n} { + set r {} + while {$n > 0} { + lappend r f$n [json::string2json $n] + incr n -1 + } + return $r +} + +# ### ### ### ######### ######### ######### ########################### +## Get all the possible implementations + +json::SwitchTo {} +foreach e [json::KnownImplementations] { + ::json::LoadAccelerator $e +} + +# ### ### ### ######### ######### ######### ########################### +## Benchmarks. +## Just the parser, on the valid inputs for the testsuite. + +foreach impl [json::Implementations] { + json::SwitchTo $impl + + if {$impl eq "tcl"} { + set series {0 1 10 100 1000} + } else { + set series {0 1 10 100 1000} + } + + bench_puts "=== === === === === ===" + bench_puts "=== === === $impl ===" + bench_puts "=== === === === === ===" + + bench_puts {=== test-data =========} + + foreach f [glob -nocomplain -directory $self/test-data *.json] { + set in [cat $f] + + bench -desc "parse [file rootname [file tail $f]] ($impl)" -body { + json::json2dict $in + } + + bench -desc "validate [file rootname [file tail $f]] ($impl)" -body { + json::validate $in + } + } + + foreach f [glob -nocomplain -directory $self/test-data *.bench] { + set in [cat $f] + + bench -desc "parse [file rootname [file tail $f]] ($impl)" -body { + json::json2dict $in + } + + bench -desc "validate [file rootname [file tail $f]] ($impl)" -body { + json::validate $in + } + } + + bench_puts {=== synthetic array =========} + + foreach n $series { + set in [json::list2json [iota $n]] + + bench -desc "parse array-$n ($impl)" -body { + json::json2dict $in + } + + bench -desc "validate array-$n ($impl)" -body { + json::validate $in + } + } + + bench_puts {=== synthetic object =========} + + foreach n $series { + set in [json::dict2json [iota-dict $n]] + + bench -desc "parse object-$n ($impl)" -body { + json::json2dict $in + } + + bench -desc "validate object-$n ($impl)" -body { + json::validate $in + } + } + + bench_puts {=== synthetic string =========} + + foreach n $series { + set in [json::string2json [string repeat . $n]] + + bench -desc "parse string-$n ($impl)" -body { + json::json2dict $in + } + + bench -desc "validate string-$n ($impl)" -body { + json::validate $in + } + } +} + +# ### ### ### ######### ######### ######### ########################### +## Complete + +return + +# ### ### ### ######### ######### ######### ########################### +## Notes ... Index: modules/json/json.man ================================================================== --- modules/json/json.man +++ modules/json/json.man @@ -1,7 +1,8 @@ [comment {-*- tcl -*- doctools manpage}] -[manpage_begin json n 1.2] +[vset VERSION 1.3] +[manpage_begin json n [vset VERSION]] [keywords {data exchange}] [keywords {exchange format}] [keywords javascript] [keywords json] [copyright {2006 ActiveState Software Inc.}] @@ -8,11 +9,11 @@ [copyright {2009 Thomas Maeder, Glue Software Engineering AG}] [moddesc {JSON}] [titledesc {JSON parser}] [category {CGI programming}] [require Tcl 8.4] -[require json [opt 1.2]] +[require json [opt [vset VERSION]]] [description] [para] The [package json] package provides a simple Tcl-only library for parsing the JSON [uri http://www.json.org/] data exchange format as specified in RFC 4627 Index: modules/json/json.tcl ================================================================== --- modules/json/json.tcl +++ modules/json/json.tcl @@ -1,23 +1,190 @@ -# -# JSON parser for Tcl. -# -# See http://www.json.org/ && http://www.ietf.org/rfc/rfc4627.txt -# -# Total rework of the code published with version number 1.0 by -# Thomas Maeder, Glue Software Engineering AG -# -# $Id: json.tcl,v 1.7 2011/11/10 21:05:58 andreas_kupries Exp $ -# - -if {![package vsatisfies [package provide Tcl] 8.5]} { - package require dict -} - -package provide json 1.2 - -namespace eval json { +# json.tcl -- +# +# JSON parser for Tcl. Management code, Tcl/C detection and selection. +# +# Copyright (c) 2013 by Andreas Kupries + +# @mdgen EXCLUDE: jsonc.tcl + +package require Tcl 8.4 +namespace eval ::json {} + +# ### ### ### ######### ######### ######### +## Management of json implementations. + +# ::json::LoadAccelerator -- +# +# Loads a named implementation, if possible. +# +# Arguments: +# key Name of the implementation to load. +# +# Results: +# A boolean flag. True if the implementation +# was successfully loaded; and False otherwise. + +proc ::json::LoadAccelerator {key} { + variable accel + set r 0 + switch -exact -- $key { + critcl { + # Critcl implementation of json requires Tcl 8.4. + if {![package vsatisfies [package provide Tcl] 8.4]} {return 0} + if {[catch {package require tcllibc}]} {return 0} + set r [llength [info commands ::json::json2dict_critcl]] + } + tcl { + variable selfdir + source [file join $selfdir json_tcl.tcl] + set r 1 + } + default { + return -code error "invalid accelerator/impl. package $key:\ + must be one of [join [KnownImplementations] {, }]" + } + } + set accel($key) $r + return $r +} + +# ::json::SwitchTo -- +# +# Activates a loaded named implementation. +# +# Arguments: +# key Name of the implementation to activate. +# +# Results: +# None. + +proc ::json::SwitchTo {key} { + variable accel + variable loaded + variable apicmds + + if {[string equal $key $loaded]} { + # No change, nothing to do. + return + } elseif {![string equal $key ""]} { + # Validate the target implementation of the switch. + + if {![info exists accel($key)]} { + return -code error "Unable to activate unknown implementation \"$key\"" + } elseif {![info exists accel($key)] || !$accel($key)} { + return -code error "Unable to activate missing implementation \"$key\"" + } + } + + # Deactivate the previous implementation, if there was any. + + if {![string equal $loaded ""]} { + foreach c $apicmds { + rename ::json::${c} ::json::${c}_$loaded + } + } + + # Activate the new implementation, if there is any. + + if {![string equal $key ""]} { + foreach c $apicmds { + rename ::json::${c}_$key ::json::${c} + } + } + + # Remember the active implementation, for deactivation by future + # switches. + + set loaded $key + return +} + +# ::json::Implementations -- +# +# Determines which implementations are +# present, i.e. loaded. +# +# Arguments: +# None. +# +# Results: +# A list of implementation keys. + +proc ::json::Implementations {} { + variable accel + set res {} + foreach n [array names accel] { + if {!$accel($n)} continue + lappend res $n + } + return $res +} + +# ::json::KnownImplementations -- +# +# Determines which implementations are known +# as possible implementations. +# +# Arguments: +# None. +# +# Results: +# A list of implementation keys. In the order +# of preference, most prefered first. + +proc ::json::KnownImplementations {} { + return {critcl tcl} +} + +proc ::json::Names {} { + return { + critcl {tcllibc based} + tcl {pure Tcl} + } +} + +# ### ### ### ######### ######### ######### +## Initialization: Data structures. + +namespace eval ::json { + variable selfdir [file dirname [info script]] + variable accel + array set accel {tcl 0 critcl 0} + variable loaded {} + + variable apicmds { + json2dict + many-json2dict + } +} + +# ### ### ### ######### ######### ######### +## Initialization: Choose an implementation, +## most prefered first. Loads only one of the +## possible implementations. And activates it. + +namespace eval ::json { + variable e + foreach e [KnownImplementations] { + if {[LoadAccelerator $e]} { + SwitchTo $e + break + } + } + unset e +} + +# ### ### ### ######### ######### ######### +## Tcl implementation of validation, shared for Tcl and C implementation. +## +## The regexp based validation is consistently faster than json-c. +## Suspected reasons: Tcl REs are mainly in C as well, and json-c has +## overhead in constructing its own data structures. While irrelevant +## to validation json-c still builds them, it has no mode doing pure +## syntax checking. + +namespace eval ::json { # Regular expression for tokenizing a JSON text (cf. http://json.org/) # tokens consisting of a single character variable singleCharTokens { "{" "}" ":" "\\[" "\\]" "," } variable singleCharTokenRE "\[[join $singleCharTokens {}]\]" @@ -55,301 +222,47 @@ # Validate JSON text # @param jsonText JSON text # @return 1 iff $jsonText conforms to the JSON grammar # (@see http://json.org/) -proc json::validate {jsonText} { +proc ::json::validate {jsonText} { variable validJsonRE return [regexp -- $validJsonRE $jsonText] } -# Parse JSON text into a dict -# @param jsonText JSON text -# @return dict (or list) containing the object represented by $jsonText -proc json::json2dict {jsonText} { - variable tokenRE - - set tokens [regexp -all -inline -- $tokenRE $jsonText] - set nrTokens [llength $tokens] - set tokenCursor 0 - return [parseValue $tokens $nrTokens tokenCursor] -} - -# Parse multiple JSON entities in a string into a list of dictionaries -# @param jsonText JSON text to parse -# @param max Max number of entities to extract. -# @return list of (dict (or list) containing the objects) represented by $jsonText -proc json::many-json2dict {jsonText {max -1}} { - variable tokenRE - - if {$max == 0} { - return -code error -errorCode {JSON BAD-LIMIT ZERO} \ - "Bad limit 0 of json entities to extract." - } - - set tokens [regexp -all -inline -- $tokenRE $jsonText] - set nrTokens [llength $tokens] - set tokenCursor 0 - - set result {} - set found 0 - set n $max - while {$n != 0} { - if {$tokenCursor >= $nrTokens} break - lappend result [parseValue $tokens $nrTokens tokenCursor] - incr found - if {$n > 0} {incr n -1} - } - - if {$n > 0} { - return -code error -errorCode {JSON BAD-LIMIT TOO LARGE} \ - "Bad limit $max of json entities to extract, found only $found." - } - - return $result -} - -# Throw an exception signaling an unexpected token -proc json::unexpected {tokenCursor token expected} { - return -code error -errorcode [list JSON UNEXPECTED $tokenCursor $expected] \ - "unexpected token \"$token\" at position $tokenCursor; expecting $expected" -} - -# Get rid of the quotes surrounding a string token and substitute the -# real characters for escape sequences within it -# @param token -# @return unquoted unescaped value of the string contained in $token -proc json::unquoteUnescapeString {token} { - set unquoted [string range $token 1 end-1] - return [subst -nocommands -novariables $unquoted] -} - -# Parse an object member -# @param tokens list of tokens -# @param nrTokens length of $tokens -# @param tokenCursorName name (in caller's context) of variable -# holding current position in $tokens -# @param objectDictName name (in caller's context) of dict -# representing the JSON object of which to -# parse the next member -proc json::parseObjectMember {tokens nrTokens tokenCursorName objectDictName} { - upvar $tokenCursorName tokenCursor - upvar $objectDictName objectDict - - set token [lindex $tokens $tokenCursor] - incr tokenCursor - - set leadingChar [string index $token 0] - if {$leadingChar eq "\""} { - set memberName [unquoteUnescapeString $token] - - if {$tokenCursor == $nrTokens} { - unexpected $tokenCursor "END" "\":\"" - } else { - set token [lindex $tokens $tokenCursor] - incr tokenCursor - - if {$token eq ":"} { - set memberValue [parseValue $tokens $nrTokens tokenCursor] - dict set objectDict $memberName $memberValue - } else { - unexpected $tokenCursor $token "\":\"" - } - } - } else { - unexpected $tokenCursor $token "STRING" - } -} - -# Parse the members of an object -# @param tokens list of tokens -# @param nrTokens length of $tokens -# @param tokenCursorName name (in caller's context) of variable -# holding current position in $tokens -# @param objectDictName name (in caller's context) of dict -# representing the JSON object of which to -# parse the next member -proc json::parseObjectMembers {tokens nrTokens tokenCursorName objectDictName} { - upvar $tokenCursorName tokenCursor - upvar $objectDictName objectDict - - while true { - parseObjectMember $tokens $nrTokens tokenCursor objectDict - - set token [lindex $tokens $tokenCursor] - incr tokenCursor - - switch -exact $token { - "," { - # continue - } - "\}" { - break - } - default { - unexpected $tokenCursor $token "\",\"|\"\}\"" - } - } - } -} - -# Parse an object -# @param tokens list of tokens -# @param nrTokens length of $tokens -# @param tokenCursorName name (in caller's context) of variable -# holding current position in $tokens -# @return parsed object (Tcl dict) -proc json::parseObject {tokens nrTokens tokenCursorName} { - upvar $tokenCursorName tokenCursor - - if {$tokenCursor == $nrTokens} { - unexpected $tokenCursor "END" "OBJECT" - } else { - set result [dict create] - - set token [lindex $tokens $tokenCursor] - - if {$token eq "\}"} { - # empty object - incr tokenCursor - } else { - parseObjectMembers $tokens $nrTokens tokenCursor result - } - - return $result - } -} - -# Parse the elements of an array -# @param tokens list of tokens -# @param nrTokens length of $tokens -# @param tokenCursorName name (in caller's context) of variable -# holding current position in $tokens -# @param resultName name (in caller's context) of the list -# representing the JSON array -proc json::parseArrayElements {tokens nrTokens tokenCursorName resultName} { - upvar $tokenCursorName tokenCursor - upvar $resultName result - - while true { - lappend result [parseValue $tokens $nrTokens tokenCursor] - - if {$tokenCursor == $nrTokens} { - unexpected $tokenCursor "END" "\",\"|\"\]\"" - } else { - set token [lindex $tokens $tokenCursor] - incr tokenCursor - - switch -exact $token { - "," { - # continue - } - "\]" { - break - } - default { - unexpected $tokenCursor $token "\",\"|\"\]\"" - } - } - } - } -} - -# Parse an array -# @param tokens list of tokens -# @param nrTokens length of $tokens -# @param tokenCursorName name (in caller's context) of variable -# holding current position in $tokens -# @return parsed array (Tcl list) -proc json::parseArray {tokens nrTokens tokenCursorName} { - upvar $tokenCursorName tokenCursor - - if {$tokenCursor == $nrTokens} { - unexpected $tokenCursor "END" "ARRAY" - } else { - set result {} - - set token [lindex $tokens $tokenCursor] - - set leadingChar [string index $token 0] - if {$leadingChar eq "\]"} { - # empty array - incr tokenCursor - } else { - parseArrayElements $tokens $nrTokens tokenCursor result - } - - return $result - } -} - -# Parse a value -# @param tokens list of tokens -# @param nrTokens length of $tokens -# @param tokenCursorName name (in caller's context) of variable -# holding current position in $tokens -# @return parsed value (dict, list, string, number) -proc json::parseValue {tokens nrTokens tokenCursorName} { - upvar $tokenCursorName tokenCursor - - if {$tokenCursor == $nrTokens} { - unexpected $tokenCursor "END" "VALUE" - } else { - set token [lindex $tokens $tokenCursor] - incr tokenCursor - - set leadingChar [string index $token 0] - switch -exact -- $leadingChar { - "\{" { - return [parseObject $tokens $nrTokens tokenCursor] - } - "\[" { - return [parseArray $tokens $nrTokens tokenCursor] - } - "\"" { - # quoted string - return [unquoteUnescapeString $token] - } - "t" - - "f" - - "n" { - # bare word: true, false or null - return $token - } - default { - # number? - if {[string is double -strict $token]} { - return $token - } else { - unexpected $tokenCursor $token "VALUE" - } - } - } - } -} - -proc json::dict2json {dictVal} { +# ### ### ### ######### ######### ######### +## These three procedures shared between Tcl and Critcl implementations. +## See also package "json::write". + +proc ::json::dict2json {dictVal} { # XXX: Currently this API isn't symmetrical, as to create proper # XXX: JSON text requires type knowledge of the input data set json "" + set prefix "" - dict for {key val} $dictVal { + foreach {key val} $dictVal { # key must always be a string, val may be a number, string or # bare word (true|false|null) if {0 && ![string is double -strict $val] && ![regexp {^(?:true|false|null)$} $val]} { set val "\"$val\"" } - append json "\"$key\": $val," \n + append json "$prefix\"$key\": $val" \n + set prefix , } return "\{${json}\}" } -proc json::list2json {listVal} { +proc ::json::list2json {listVal} { return "\[[join $listVal ,]\]" } -proc json::string2json {str} { +proc ::json::string2json {str} { return "\"$str\"" } + +# ### ### ### ######### ######### ######### +## Ready + +package provide json 1.3 Index: modules/json/json.test ================================================================== --- modules/json/json.test +++ modules/json/json.test @@ -15,272 +15,73 @@ devtools testutilities.tcl] testsNeedTcl [expr {[catch {package require dict}] ? "8.5" : "8.4"}] testsNeedTcltest 2.0 -testing { - useLocal json.tcl json -} - -catch {unset JSON} -catch {unset TCL} -catch {unset DICTSORT} - -proc dictsort3 {spec data} { - while [llength $spec] { - set type [lindex $spec 0] - set spec [lrange $spec 1 end] - - switch -- $type { - dict { - lappend spec * string - - set json {} - foreach {key} [lsort [dict keys $data]] { - set val [dict get $data $key] - foreach {keymatch valtype} $spec { - if {[string match $keymatch $key]} { - lappend json $key [dictsort3 $valtype $val] - break - } - } - } - return $json - } - list { - lappend spec * string - set json {} - set idx 0 - foreach {val} $data { - foreach {keymatch valtype} $spec { - if {$idx == $keymatch || $keymatch eq "*"} { - lappend json [dictsort3 $valtype $val] - break - } - } - incr idx - } - return $json - } - string { - return $data - } - default {error "Invalid type"} - } - } -} - - -set JSON(array) {[ - { - "precision": "zip", - "Latitude": 37.7668, - "Longitude": -122.3959, - "Address": "", - "City": "SAN FRANCISCO", - "State": "CA", - "Zip": "94107", - "Country": "US" - }, - { - "precision": "zip", - "Latitude": 37.371991, - "Longitude": -122.026020, - "Address": "", - "City": "SUNNYVALE", - "State": "CA", - "Zip": "94085", - "Country": "US" - } - ]} -set TCL(array) {{precision zip Latitude 37.7668 Longitude -122.3959 Address {} City {SAN FRANCISCO} State CA Zip 94107 Country US} {precision zip Latitude 37.371991 Longitude -122.026020 Address {} City SUNNYVALE State CA Zip 94085 Country US}} - -set DICTSORT(array) {list dict} - -set JSON(glossary) {{ - "glossary": { - "title": "example glossary", - "mixlist": ["a \"\" str", -0.09, null, "", {"member":true}], - "GlossDiv": { - "title": "S", - "GlossList": [{ - "ID": "SGML", - "GlossTerm": "Standard \\\" Language", - "Acronym": "SGML\\", - "Abbrev": "ISO 8879:1986", - "GlossDef": - "A meta-markup language, used ...", - "GlossSeeAlso": ["GML", "XML", "markup"]}]}} -}} -set TCL(glossary) {glossary {title {example glossary} mixlist {{a "" str} -0.09 null {} {member true}} GlossDiv {title S GlossList {{ID SGML GlossTerm {Standard \" Language} Acronym SGML\\ Abbrev {ISO 8879:1986} GlossDef {A meta-markup language, used ...} GlossSeeAlso {GML XML markup}}}}}} -set DICTSORT(glossary) {dict * {dict GlossDiv {dict GlossList {list dict}}}} - -set JSON(menu) {{"menu": { - "id": "file", - "value": "File:", - "unival": "\u6021:", - "popup": { - "menuitem": [ - {"value": "Open", "onclick": "OpenDoc()"}, - {"value": "Close", "onclick": "CloseDoc()"} - ] - } -} -}} -set TCL(menu) [list menu [list id file value File: unival \u6021: popup {menuitem {{value Open onclick OpenDoc()} {value Close onclick CloseDoc()}}}]] -set DICTSORT(menu) {dict * {dict popup {dict * {list dict}}}} - -set JSON(widget) {{"widget": { - "debug": "on", - "window": { - "title":"Sample Widget", - "name": "main_window", - "width": 500, - "height": 500}, - "text": { - "data": "Click Here", - "size": 36, - "style": "bold", - "name": null, - "hOffset":250, - "vOffset": 100, - "alignment": "center", - "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" - } -} -}} -set TCL(widget) {widget {debug on window {title {Sample Widget} name main_window width 500 height 500} text {data {Click Here} size 36 style bold name null hOffset 250 vOffset 100 alignment center onMouseUp {sun1.opacity = (sun1.opacity / 100) * 90;}}}} -set DICTSORT(widget) {dict * {dict text dict window dict}} - -set JSON(menu2) {{"menu": { - "header": "Viewer", - "items": [ - {"id": "Open"}, - {"id": "OpenNew", "label": "Open New"}, - null, - {"id": "ZoomIn", "label": "Zoom In"}, - {"id": "ZoomOut", "label": "Zoom Out"}, - null, - {"id": "Help"}, - {"id": "About", "label": "About Viewer..."} - ] -} -}} -set TCL(menu2) {menu {header Viewer items {{id Open} {id OpenNew label {Open New}} null {id ZoomIn label {Zoom In}} {id ZoomOut label {Zoom Out}} null {id Help} {id About label {About Viewer...}}}}} -set DICTSORT(menu2) {dict * {dict items {list 0 dict 1 dict 3 dict 4 dict 6 dict 7 dict}}} - -set JSON(emptyList) {[]} -set TCL(emptyList) {} - -set JSON(emptyList2) {{"menu": []}} -set TCL(emptyList2) {menu {}} - -set JSON(emptyList3) {["menu", []]} -set TCL(emptyList3) {menu {}} - -set JSON(emptyList4) {[[]]} -set TCL(emptyList4) {{}} - -proc resultfor {name} { - global TCL - transform $TCL($name) $name -} - -proc transform {res name} { - global DICTSORT - if {[info exists DICTSORT($name)]} { - return [dictsort3 $DICTSORT($name) $res] - } else { - return $res - } -} - -proc transform* {res args} { - set t {} - foreach r $res n $args { - lappend t [transform $r $n] - } - return $t +support { + useLocalFile test_support.tcl +} + +testing { + useAccel [useTcllibC] json/json.tcl json + TestAccelInit json } # ------------------------------------------------------------------------- # Tests # ------------------------------------------------------------------------- -set i 0 -foreach name [array names JSON] { - test json-1.[incr i] "test JSON $name" -body { - set res [json::json2dict $JSON($name)] - if {[info exists DICTSORT($name)]} { - return [string equal [dictsort3 $DICTSORT($name) $res] [dictsort3 $DICTSORT($name) $TCL($name)]] - } else { - return [string equal $res $TCL($name)] - } - } -result 1 -} - -# ------------------------------------------------------------------------- -# More Tests -# ------------------------------------------------------------------------- - -test json-2.0 {list2json} -body { - json::list2json {{"a"} {"b"} {"c"}} -} -result {["a","b","c"]} - -test json-2.1 {string2json} -body { - json::string2json a -} -result {"a"} - -# ------------------------------------------------------------------------- - -test json-3.0 {many-json2dict, wrong args, not enough} -body { - json::many-json2dict -} -returnCodes error -result {wrong # args: should be "json::many-json2dict jsonText ?max?"} - -test json-3.1 {many-json2dict, wrong args, too many} -body { - json::many-json2dict J M X -} -returnCodes error -result {wrong # args: should be "json::many-json2dict jsonText ?max?"} - -test json-3.2 {many-json2dict, bad limit, zero} -body { - json::many-json2dict {[]} 0 -} -returnCodes error -result {Bad limit 0 of json entities to extract.} - -set i 2 -foreach first [array names JSON] { - foreach second [array names JSON] { - set input $JSON($first) - append input " " $JSON($second) - - set output {} - lappend output [resultfor $first] - lappend output [resultfor $second] - - test json-3.[incr i] "many-json2dict: $first/$second, all" -body { - transform* [json::many-json2dict $input] $first $second - } -result $output - } -} - -foreach first [array names JSON] { - foreach second [array names JSON] { - set input $JSON($first) - append input " " $JSON($second) - - set output {} - lappend output [resultfor $first] - - test json-3.[incr i] "many-json2dict: $first/$second, first only" -body { - transform* [json::many-json2dict $input 1] $first - } -result $output - } -} - -foreach first [array names JSON] { - set input $JSON($first) - test json-3.[incr i] "many-json2dict, bad limit, 3 over 1" -body { - json::many-json2dict $input 3 - } -returnCodes error -result {Bad limit 3 of json entities to extract, found only 1.} +TestAccelDo json impl { + switch -exact -- $impl { + critcl { + set MY myjson + + proc tmWrong {m loarg n {xarg {}}} { + return [tcltest::wrongNumArgs "myjson $m" $loarg $n] + } + + proc tmTooMany {m loarg {xarg {}}} { + return [tcltest::tooManyArgs "myjson $m" $loarg] + } + + proc tmTake {tcl c} { return $c } + } + tcl { + set MY ::myjson + + if {[package vsatisfies [package provide Tcl] 8.5]} { + proc tmWrong {m loarg n {xarg {}}} { + if {$xarg == {}} {set xarg $loarg} + if {$xarg != {}} {set xarg " $xarg"} + incr n + return [tcltest::wrongNumArgs "I $m" "name$xarg" $n] + } + + proc tmTooMany {m loarg {xarg {}}} { + if {$xarg == {}} {set xarg $loarg} + if {$xarg != {}} {set xarg " $xarg"} + return [tcltest::tooManyArgs "I $m" "name$xarg"] + } + } else { + proc tmWrong {m loarg n {xarg {}}} { + if {$xarg == {}} {set xarg $loarg} + if {$xarg != {}} {set xarg " $xarg"} + incr n + return [tcltest::wrongNumArgs "::struct::json::I::$m" "name$xarg" $n] + } + + proc tmTooMany {m loarg {xarg {}}} { + if {$xarg == {}} {set xarg $loarg} + if {$xarg != {}} {set xarg " $xarg"} + return [tcltest::tooManyArgs "::struct::json::I::$m" "name$xarg"] + } + } + + proc tmTake {tcl c} { return $tcl } + } + } + source [localPath json.testsuite] } # ------------------------------------------------------------------------- catch {unset JSON} catch {unset TCL} ADDED modules/json/json.testsuite Index: modules/json/json.testsuite ================================================================== --- /dev/null +++ modules/json/json.testsuite @@ -0,0 +1,102 @@ +# -*- tcl -*- +# ------------------------------------------------------------------------- +# Tests +# ------------------------------------------------------------------------- + +set i 0 +foreach name [lsort -dict [array names JSON]] { + test json-${impl}-1.[incr i] "test JSON $name" -body { + transform [json::json2dict $JSON($name)] $name + } -result [resultfor $name] +} + +set i 0 +foreach name [lsort -dict [array names JSON]] { + test json-${impl}-7.[incr i] "validate JSON $name" -body { + json::validate $JSON($name) + } -result 1 +} + +set i 0 +foreach name [lsort -dict [array names FAIL]] { + test json-${impl}-8.[incr i] "test FAIL $name" -body { + json::json2dict $FAIL($name) + } -returnCodes error -result $ERR(${name}-${impl}) +} + +set i 0 +foreach name [lsort -dict [array names FAIL]] { + test json-${impl}-9.[incr i] "validate FAIL $name" -body { + json::validate $FAIL($name) + } -result 0 +} + +# ------------------------------------------------------------------------- +# More Tests - list2json, string2json +# TODO: dict2json +# ------------------------------------------------------------------------- + +test json-${impl}-2.0 {list2json} -body { + json::list2json {{"a"} {"b"} {"c"}} +} -result {["a","b","c"]} + +test json-${impl}-2.1 {string2json} -body { + json::string2json a +} -result {"a"} + +# ------------------------------------------------------------------------- +# many-json2dict +# ------------------------------------------------------------------------- + +test json-${impl}-3.0 {many-json2dict, wrong args, not enough} -body { + json::many-json2dict +} -returnCodes error -result {wrong # args: should be "json::many-json2dict jsonText ?max?"} + +test json-${impl}-3.1 {many-json2dict, wrong args, too many} -body { + json::many-json2dict J M X +} -returnCodes error -result {wrong # args: should be "json::many-json2dict jsonText ?max?"} + +test json-${impl}-3.2 {many-json2dict, bad limit, zero} -body { + json::many-json2dict {[]} 0 +} -returnCodes error -result {Bad limit 0 of json entities to extract.} + +set i 0 +foreach first [lsort -dict [array names JSON]] { + foreach second [lsort -dict [array names JSON]] { + set input $JSON($first) + append input " " $JSON($second) + + set output {} + lappend output [resultfor $first] + lappend output [resultfor $second] + + test json-${impl}-4.[incr i] "many-json2dict: $first/$second, all" -body { + transform* [json::many-json2dict $input] $first $second + } -result $output + } +} + +set i 0 +foreach first [lsort -dict [array names JSON]] { + foreach second [lsort -dict [array names JSON]] { + set input $JSON($first) + append input " " $JSON($second) + + set output {} + lappend output [resultfor $first] + + test json-${impl}-5.[incr i] "many-json2dict: $first/$second, first only" -body { + transform* [json::many-json2dict $input 1] $first + } -result $output + } +} + +set i 0 +foreach first [lsort -dict [array names JSON]] { + set input $JSON($first) + test json-${impl}-6.[incr i] "many-json2dict, bad limit, 3 over 1" -body { + json::many-json2dict $input 3 + } -returnCodes error -result {Bad limit 3 of json entities to extract, found only 1.} +} + +# ------------------------------------------------------------------------- ADDED modules/json/json_tcl.tcl Index: modules/json/json_tcl.tcl ================================================================== --- /dev/null +++ modules/json/json_tcl.tcl @@ -0,0 +1,285 @@ +# +# JSON parser for Tcl. +# +# See http://www.json.org/ && http://www.ietf.org/rfc/rfc4627.txt +# +# Total rework of the code published with version number 1.0 by +# Thomas Maeder, Glue Software Engineering AG +# +# $Id: json.tcl,v 1.7 2011/11/10 21:05:58 andreas_kupries Exp $ +# + +if {![package vsatisfies [package provide Tcl] 8.5]} { + package require dict +} + +# Parse JSON text into a dict +# @param jsonText JSON text +# @return dict (or list) containing the object represented by $jsonText +proc ::json::json2dict_tcl {jsonText} { + variable tokenRE + + set tokens [regexp -all -inline -- $tokenRE $jsonText] + set nrTokens [llength $tokens] + set tokenCursor 0 + return [parseValue $tokens $nrTokens tokenCursor] +} + +# Parse multiple JSON entities in a string into a list of dictionaries +# @param jsonText JSON text to parse +# @param max Max number of entities to extract. +# @return list of (dict (or list) containing the objects) represented by $jsonText +proc ::json::many-json2dict_tcl {jsonText {max -1}} { + variable tokenRE + + if {$max == 0} { + return -code error -errorCode {JSON BAD-LIMIT ZERO} \ + "Bad limit 0 of json entities to extract." + } + + set tokens [regexp -all -inline -- $tokenRE $jsonText] + set nrTokens [llength $tokens] + set tokenCursor 0 + + set result {} + set found 0 + set n $max + while {$n != 0} { + if {$tokenCursor >= $nrTokens} break + lappend result [parseValue $tokens $nrTokens tokenCursor] + incr found + if {$n > 0} {incr n -1} + } + + if {$n > 0} { + return -code error -errorCode {JSON BAD-LIMIT TOO LARGE} \ + "Bad limit $max of json entities to extract, found only $found." + } + + return $result +} + +# Throw an exception signaling an unexpected token +proc ::json::unexpected {tokenCursor token expected} { + return -code error -errorcode [list JSON UNEXPECTED $tokenCursor $expected] \ + "unexpected token \"$token\" at position $tokenCursor; expecting $expected" +} + +# Get rid of the quotes surrounding a string token and substitute the +# real characters for escape sequences within it +# @param token +# @return unquoted unescaped value of the string contained in $token +proc ::json::unquoteUnescapeString {token} { + set unquoted [string range $token 1 end-1] + return [subst -nocommands -novariables $unquoted] +} + +# Parse an object member +# @param tokens list of tokens +# @param nrTokens length of $tokens +# @param tokenCursorName name (in caller's context) of variable +# holding current position in $tokens +# @param objectDictName name (in caller's context) of dict +# representing the JSON object of which to +# parse the next member +proc ::json::parseObjectMember {tokens nrTokens tokenCursorName objectDictName} { + upvar $tokenCursorName tokenCursor + upvar $objectDictName objectDict + + set token [lindex $tokens $tokenCursor] + incr tokenCursor + + set leadingChar [string index $token 0] + if {$leadingChar eq "\""} { + set memberName [unquoteUnescapeString $token] + + if {$tokenCursor == $nrTokens} { + unexpected $tokenCursor "END" "\":\"" + } else { + set token [lindex $tokens $tokenCursor] + incr tokenCursor + + if {$token eq ":"} { + set memberValue [parseValue $tokens $nrTokens tokenCursor] + dict set objectDict $memberName $memberValue + } else { + unexpected $tokenCursor $token "\":\"" + } + } + } else { + unexpected $tokenCursor $token "STRING" + } +} + +# Parse the members of an object +# @param tokens list of tokens +# @param nrTokens length of $tokens +# @param tokenCursorName name (in caller's context) of variable +# holding current position in $tokens +# @param objectDictName name (in caller's context) of dict +# representing the JSON object of which to +# parse the next member +proc ::json::parseObjectMembers {tokens nrTokens tokenCursorName objectDictName} { + upvar $tokenCursorName tokenCursor + upvar $objectDictName objectDict + + while true { + parseObjectMember $tokens $nrTokens tokenCursor objectDict + + set token [lindex $tokens $tokenCursor] + incr tokenCursor + + switch -exact $token { + "," { + # continue + } + "\}" { + break + } + default { + unexpected $tokenCursor $token "\",\"|\"\}\"" + } + } + } +} + +# Parse an object +# @param tokens list of tokens +# @param nrTokens length of $tokens +# @param tokenCursorName name (in caller's context) of variable +# holding current position in $tokens +# @return parsed object (Tcl dict) +proc ::json::parseObject {tokens nrTokens tokenCursorName} { + upvar $tokenCursorName tokenCursor + + if {$tokenCursor == $nrTokens} { + unexpected $tokenCursor "END" "OBJECT" + } else { + set result [dict create] + + set token [lindex $tokens $tokenCursor] + + if {$token eq "\}"} { + # empty object + incr tokenCursor + } else { + parseObjectMembers $tokens $nrTokens tokenCursor result + } + + return $result + } +} + +# Parse the elements of an array +# @param tokens list of tokens +# @param nrTokens length of $tokens +# @param tokenCursorName name (in caller's context) of variable +# holding current position in $tokens +# @param resultName name (in caller's context) of the list +# representing the JSON array +proc ::json::parseArrayElements {tokens nrTokens tokenCursorName resultName} { + upvar $tokenCursorName tokenCursor + upvar $resultName result + + while true { + lappend result [parseValue $tokens $nrTokens tokenCursor] + + if {$tokenCursor == $nrTokens} { + unexpected $tokenCursor "END" "\",\"|\"\]\"" + } else { + set token [lindex $tokens $tokenCursor] + incr tokenCursor + + switch -exact $token { + "," { + # continue + } + "\]" { + break + } + default { + unexpected $tokenCursor $token "\",\"|\"\]\"" + } + } + } + } +} + +# Parse an array +# @param tokens list of tokens +# @param nrTokens length of $tokens +# @param tokenCursorName name (in caller's context) of variable +# holding current position in $tokens +# @return parsed array (Tcl list) +proc ::json::parseArray {tokens nrTokens tokenCursorName} { + upvar $tokenCursorName tokenCursor + + if {$tokenCursor == $nrTokens} { + unexpected $tokenCursor "END" "ARRAY" + } else { + set result {} + + set token [lindex $tokens $tokenCursor] + + set leadingChar [string index $token 0] + if {$leadingChar eq "\]"} { + # empty array + incr tokenCursor + } else { + parseArrayElements $tokens $nrTokens tokenCursor result + } + + return $result + } +} + +# Parse a value +# @param tokens list of tokens +# @param nrTokens length of $tokens +# @param tokenCursorName name (in caller's context) of variable +# holding current position in $tokens +# @return parsed value (dict, list, string, number) +proc ::json::parseValue {tokens nrTokens tokenCursorName} { + upvar $tokenCursorName tokenCursor + + if {$tokenCursor == $nrTokens} { + unexpected $tokenCursor "END" "VALUE" + } else { + set token [lindex $tokens $tokenCursor] + incr tokenCursor + + set leadingChar [string index $token 0] + switch -exact -- $leadingChar { + "\{" { + return [parseObject $tokens $nrTokens tokenCursor] + } + "\[" { + return [parseArray $tokens $nrTokens tokenCursor] + } + "\"" { + # quoted string + return [unquoteUnescapeString $token] + } + "t" { + # bare word: true (mapped to numeric boolean) + return 1 + } + "f" { + # bare word: false (mapped to numeric boolean) + return 0 + } + "n" { + # bare word: null (return as is) + return $token + } + default { + # number? + if {[string is double -strict $token]} { + return $token + } else { + unexpected $tokenCursor $token "VALUE" + } + } + } + } +} ADDED modules/json/jsonc.tcl Index: modules/json/jsonc.tcl ================================================================== --- /dev/null +++ modules/json/jsonc.tcl @@ -0,0 +1,373 @@ +# jsonc.tcl -- +# +# Implementation of a JSON parser in C, binding to the json-c library. +# See c/README-tcllib for notes on origin. +# +# Copyright (c) 2013 - critcl wrapper - Andreas Kupries +# Copyright (c) 2013 - C binding - mi+tcl.tk-2013@aldan.algebra.com + +package require critcl +# @sak notprovided jsonc +package provide jsonc 1 +package require Tcl 8.4 + +critcl::cheaders -Ic c/*.h +critcl::csources c/*.c + +# # ## ### Import base declarations, forwards ### ## # # + +critcl::ccode { + #include + #include + + #define assert(x) if (!(x)) { Tcl_Panic ("failed jsonc assert (" #x ")"); } + + static Tcl_Obj *value2obj (Tcl_Interp *, struct json_object *); + static Tcl_Obj *lazy_value2obj (Tcl_Interp *, struct json_object *); + + static Tcl_FreeInternalRepProc jsoncfree; + static Tcl_DupInternalRepProc jsoncdup; + static Tcl_UpdateStringProc jsonc2string; +} + +# # ## ### Tcl_ObjType 'json-c' ### ## # # + +# Note: Without a set-from-any function the only way of creating +# Tcl_Obj* with type 'json-c' is through function 'jsonNewObj'. It is +# not possible to generate it from an arbitrary string. It has to be +# done through the 'json2dict' command. + +critcl::ccode { + static Tcl_ObjType json_t = { + "json-c", /* name */ + jsoncfree, /* freeIntRepProc */ + jsoncdup, /* dupIntRepProc */ + jsonc2string, /* updateStringProc */ + NULL /* setFromAnyProc */ + }; + + #define tcl2jso(O) (O->internalRep.twoPtrValue.ptr1) + + static void + jsoncfree(Tcl_Obj *O) + { + assert(O->typePtr == &json_t); + json_object_put(tcl2jso(O)); + } + + static void + jsoncdup(Tcl_Obj *src, Tcl_Obj *dst) + { + assert(src->typePtr == &json_t); + dst->bytes = src->bytes; + dst->length = src->length; + dst->typePtr = &json_t; + Tcl_IncrRefCount(src); + } + + static void + jsonc2string(Tcl_Obj *O) + { + json_object *jso = tcl2jso(O); + Tcl_Obj *copy; + + assert(O->typePtr == &json_t); + + /* The string representation is generated by going through + * a helper generated from the json with an appropriate + * standard type (string, int, bool, dict, list) and taking + * its contents. Note that not only the helper's string rep + * is taken, but its internal rep as well, shimmering this + * object to the type. + * + * Q (AK): Why shimmer this way? + */ + + copy = value2obj(NULL, jso); + + O->typePtr = copy->typePtr; + O->bytes = Tcl_GetStringFromObj(copy, &O->length); + memcpy(&O->internalRep, ©->internalRep, sizeof(O->internalRep)); + + json_object_put(jso); + + copy->bytes = NULL; + copy->length = 0; + copy->typePtr = NULL; + Tcl_DecrRefCount(copy); + } + + static Tcl_Obj * + jsoncNewObj(struct json_object *jso) + { + Tcl_Obj *result = Tcl_NewObj(); + + result->bytes = NULL; + result->typePtr = &json_t; + tcl2jso(result) = json_object_get(jso); + + return result; + } +} + +# # ## ### Main Conversion ### ## # # + +critcl::ccode { + static Tcl_Obj * + lazy_value2obj(Tcl_Interp *I, struct json_object *jso) + { + switch (json_object_get_type(jso)) { + case json_type_object: + case json_type_array: + /* Defer further construction, encapsulate the value into a 'json-c' + * representation. + */ + return jsoncNewObj(jso); + default: + return value2obj(I, jso); + } + } + + static Tcl_Obj * + value2obj(Tcl_Interp *I, struct json_object *jso) + { + struct array_list* ar; + int len; + Tcl_Obj* key; + Tcl_Obj* result; + Tcl_Obj* value; + + switch (json_object_get_type(jso)) { + case json_type_object: { + /* Using List API here for compat with Tcl 8.4. */ + result = Tcl_NewListObj(0, NULL); + + json_object_object_foreach(jso, k, v) { + key = Tcl_NewStringObj(k, -1); + value = lazy_value2obj(I, v); + + Tcl_ListObjAppendElement(I, result, key); + Tcl_ListObjAppendElement(I, result, value); + json_object_object_del(jso, k); + } + return result; + } + + case json_type_null: { + return Tcl_NewStringObj("null",-1); + } + + case json_type_boolean: { + return Tcl_NewBooleanObj(json_object_get_boolean(jso)); + } + + case json_type_double: { + return Tcl_NewDoubleObj(json_object_get_double(jso)); + } + + case json_type_int: { + return Tcl_NewLongObj(json_object_get_int64(jso)); + } + + case json_type_array: { + ar = json_object_get_array(jso); + result = Tcl_NewListObj(0, NULL); + + for (len = 0; len < ar->length; len++) { + value = lazy_value2obj(I, ar->array[len]); + Tcl_ListObjAppendElement(I, result, value); + } + return result; + } + + case json_type_string: { + len = json_object_get_string_len(jso); + return Tcl_NewStringObj(json_object_get_string(jso), len); + } + } + return NULL; /* unreachable */ + } +} + +namespace eval ::json { + critcl::ccommand json2dict_critcl {dummy I objc objv} { + struct json_tokener *tok; + enum json_tokener_error jerr; + struct json_object *parsed; + const char *text; + int len; + + if (objc != 2) { + Tcl_WrongNumArgs(I, 1, objv, "json"); + return TCL_ERROR; + } + + text = Tcl_GetStringFromObj(objv[1], &len); + tok = json_tokener_new(); + parsed = json_tokener_parse_ex(tok, text, len); + + if (parsed == NULL) { + jerr = json_tokener_get_error(tok); + Tcl_SetResult(I, + (void *)json_tokener_error_desc(jerr), + TCL_STATIC); + json_tokener_free(tok); + return TCL_ERROR; + } + json_tokener_free(tok); + + Tcl_SetObjResult(I, value2obj(I, parsed)); + + json_object_put(parsed); + return TCL_OK; + } + + critcl::ccommand many-json2dict_critcl {dummy I objc objv} { + struct json_tokener *tok; + enum json_tokener_error jerr; + struct json_object *parsed; + const char *text; + int len; + int max; + int found; + + Tcl_Obj* result = Tcl_NewListObj(0, NULL); + + if ((objc < 2) || (objc > 3)) { + Tcl_WrongNumArgs(I, 1, objv, "jsonText ?max?"); + return TCL_ERROR; + } + + if (objc == 3) { + if (Tcl_GetIntFromObj(I, objv[2], &max) != TCL_OK) { + return TCL_ERROR; + } + if (max <= 0) { + Tcl_AppendResult (I, "Bad limit ", + Tcl_GetString (objv[2]), + " of json entities to extract.", + NULL); + Tcl_SetErrorCode (I, "JSON", "BAD-LIMIT", NULL); + return TCL_ERROR; + } + + } else { + max = -1; + } + + text = Tcl_GetStringFromObj(objv[1], &len); + tok = json_tokener_new(); + found = 0; + + /* Iterate over the input until + * - we have gotten all requested values. + * - we have run out of input + * - we have run into an error + */ + + while ((max < 0) || max) { + parsed = json_tokener_parse_ex(tok, text, len); + + /* parse error, abort */ + if (parsed == NULL) { + jerr = json_tokener_get_error(tok); + Tcl_SetResult(I, + json_tokener_error_desc(jerr), + TCL_STATIC); + + json_tokener_free(tok); + Tcl_DecrRefCount (result); + return TCL_ERROR; + } + + /* Proper value extracted, extend result */ + found ++; + Tcl_ListObjAppendElement(I, result, + value2obj(I, parsed)); + json_object_put(parsed); + + /* Count down on the number of still missing + * values, if not asking for all (-1) + */ + if (max > 0) max --; + + /* Abort if we have consumed all input */ + if (tok->char_offset >= len) break; + + /* Skip over consumed text for next parse */ + text += tok->char_offset; + len -= tok->char_offset; + } + + json_tokener_free(tok); + + /* While all parse were ok we reached end of + * input without getting all requested values, + * this is an error + */ + if (max > 0) { + char buf [30]; + sprintf (buf, "%d", found); + Tcl_AppendResult (I, "Bad limit ", + Tcl_GetString (objv[2]), + " of json entities to extract, found only ", + buf, + ".", + NULL); + Tcl_SetErrorCode (I, "JSON", "BAD-LIMIT", "TOO", "LARGE", NULL); + Tcl_DecrRefCount (result); + return TCL_ERROR; + } + + /* We are good and done */ + Tcl_SetObjResult(I, result); + return TCL_OK; + } + + critcl::ccommand validate_critcl {dummy I objc objv} { + struct json_tokener *tok; + enum json_tokener_error jerr; + struct json_object *parsed; + const char *text; + int len; + int max; + int found; + + if (objc != 2) { + Tcl_WrongNumArgs(I, 1, objv, "jsonText"); + return TCL_ERROR; + } + + text = Tcl_GetStringFromObj(objv[1], &len); + tok = json_tokener_new(); + + /* Iterate over the input until we have run + * out of text, or encountered an error + */ + + while (1) { + parsed = json_tokener_parse_ex(tok, text, len); + + /* parse error, abort, not valid */ + if (parsed == NULL) { + json_tokener_free(tok); + Tcl_SetObjResult(I, Tcl_NewBooleanObj (0)); + return TCL_OK; + } + json_object_put(parsed); + + /* Abort if we have consumed all input */ + if (tok->char_offset >= len) break; + + /* Skip over consumed text for next parse */ + text += tok->char_offset; + len -= tok->char_offset; + } + + json_tokener_free(tok); + + /* We are good and done */ + Tcl_SetObjResult(I, Tcl_NewBooleanObj (1)); + return TCL_OK; + } +} Index: modules/json/pkgIndex.tcl ================================================================== --- modules/json/pkgIndex.tcl +++ modules/json/pkgIndex.tcl @@ -1,7 +1,7 @@ # Tcl package index file, version 1.1 if {![package vsatisfies [package provide Tcl] 8.4]} {return} -package ifneeded json 1.2 [list source [file join $dir json.tcl]] +package ifneeded json 1.3 [list source [file join $dir json.tcl]] if {![package vsatisfies [package provide Tcl] 8.5]} {return} package ifneeded json::write 1.0.2 [list source [file join $dir json_write.tcl]] ADDED modules/json/test-data/array.json Index: modules/json/test-data/array.json ================================================================== --- /dev/null +++ modules/json/test-data/array.json @@ -0,0 +1,22 @@ +[ + { + "precision": "zip", + "Latitude": 37.7668, + "Longitude": -122.3959, + "Address": "", + "City": "SAN FRANCISCO", + "State": "CA", + "Zip": "94107", + "Country": "US" + }, + { + "precision": "zip", + "Latitude": 37.371991, + "Longitude": -122.026020, + "Address": "", + "City": "SUNNYVALE", + "State": "CA", + "Zip": "94085", + "Country": "US" + } + ] ADDED modules/json/test-data/array.result Index: modules/json/test-data/array.result ================================================================== --- /dev/null +++ modules/json/test-data/array.result @@ -0,0 +1,1 @@ +{precision zip Latitude 37.7668 Longitude -122.3959 Address {} City {SAN FRANCISCO} State CA Zip 94107 Country US} {precision zip Latitude 37.371991 Longitude -122.026020 Address {} City SUNNYVALE State CA Zip 94085 Country US} ADDED modules/json/test-data/array.sort Index: modules/json/test-data/array.sort ================================================================== --- /dev/null +++ modules/json/test-data/array.sort @@ -0,0 +1,1 @@ +list dict ADDED modules/json/test-data/glossary.json Index: modules/json/test-data/glossary.json ================================================================== --- /dev/null +++ modules/json/test-data/glossary.json @@ -0,0 +1,15 @@ +{ + "glossary": { + "title": "example glossary", + "mixlist": ["a \"\" str", -0.09, null, "", {"member":true}], + "GlossDiv": { + "title": "S", + "GlossList": [{ + "ID": "SGML", + "GlossTerm": "Standard \\\" Language", + "Acronym": "SGML\\", + "Abbrev": "ISO 8879:1986", + "GlossDef": + "A meta-markup language, used ...", + "GlossSeeAlso": ["GML", "XML", "markup"]}]}} +} ADDED modules/json/test-data/glossary.result Index: modules/json/test-data/glossary.result ================================================================== --- /dev/null +++ modules/json/test-data/glossary.result @@ -0,0 +1,1 @@ +glossary {title {example glossary} mixlist {{a "" str} -0.09 null {} {member 1}} GlossDiv {title S GlossList {{ID SGML GlossTerm {Standard \" Language} Acronym SGML\\ Abbrev {ISO 8879:1986} GlossDef {A meta-markup language, used ...} GlossSeeAlso {GML XML markup}}}}} ADDED modules/json/test-data/glossary.sort Index: modules/json/test-data/glossary.sort ================================================================== --- /dev/null +++ modules/json/test-data/glossary.sort @@ -0,0 +1,1 @@ +dict * {dict GlossDiv {dict GlossList {list dict}}} ADDED modules/json/test-data/menu.json Index: modules/json/test-data/menu.json ================================================================== --- /dev/null +++ modules/json/test-data/menu.json @@ -0,0 +1,12 @@ +{"menu": { + "id": "file", + "value": "File:", + "unival": "\u6021:", + "popup": { + "menuitem": [ + {"value": "Open", "onclick": "OpenDoc()"}, + {"value": "Close", "onclick": "CloseDoc()"} + ] + } +} +} ADDED modules/json/test-data/menu.result Index: modules/json/test-data/menu.result ================================================================== --- /dev/null +++ modules/json/test-data/menu.result @@ -0,0 +1,1 @@ +menu {id file value File: unival @@@: popup {menuitem {{value Open onclick OpenDoc()} {value Close onclick CloseDoc()}}}} ADDED modules/json/test-data/menu.sort Index: modules/json/test-data/menu.sort ================================================================== --- /dev/null +++ modules/json/test-data/menu.sort @@ -0,0 +1,1 @@ +dict * {dict popup {dict * {list dict}}} ADDED modules/json/test-data/menu2.json Index: modules/json/test-data/menu2.json ================================================================== --- /dev/null +++ modules/json/test-data/menu2.json @@ -0,0 +1,14 @@ +{"menu": { + "header": "Viewer", + "items": [ + {"id": "Open"}, + {"id": "OpenNew", "label": "Open New"}, + null, + {"id": "ZoomIn", "label": "Zoom In"}, + {"id": "ZoomOut", "label": "Zoom Out"}, + null, + {"id": "Help"}, + {"id": "About", "label": "About Viewer..."} + ] +} +} ADDED modules/json/test-data/menu2.result Index: modules/json/test-data/menu2.result ================================================================== --- /dev/null +++ modules/json/test-data/menu2.result @@ -0,0 +1,1 @@ +menu {header Viewer items {{id Open} {id OpenNew label {Open New}} null {id ZoomIn label {Zoom In}} {id ZoomOut label {Zoom Out}} null {id Help} {id About label {About Viewer...}}}} ADDED modules/json/test-data/menu2.sort Index: modules/json/test-data/menu2.sort ================================================================== --- /dev/null +++ modules/json/test-data/menu2.sort @@ -0,0 +1,1 @@ +dict * {dict items {list 0 dict 1 dict 3 dict 4 dict 6 dict 7 dict}} ADDED modules/json/test-data/widget.json Index: modules/json/test-data/widget.json ================================================================== --- /dev/null +++ modules/json/test-data/widget.json @@ -0,0 +1,19 @@ +{"widget": { + "debug": "on", + "window": { + "title":"Sample Widget", + "name": "main_window", + "width": 500, + "height": 500}, + "text": { + "data": "Click Here", + "size": 36, + "style": "bold", + "name": null, + "hOffset":250, + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } +} +} ADDED modules/json/test-data/widget.result Index: modules/json/test-data/widget.result ================================================================== --- /dev/null +++ modules/json/test-data/widget.result @@ -0,0 +1,1 @@ +widget {debug on window {title {Sample Widget} name main_window width 500 height 500} text {data {Click Here} size 36 style bold name null hOffset 250 vOffset 100 alignment center onMouseUp {sun1.opacity = (sun1.opacity / 100) * 90;}}} ADDED modules/json/test-data/widget.sort Index: modules/json/test-data/widget.sort ================================================================== --- /dev/null +++ modules/json/test-data/widget.sort @@ -0,0 +1,1 @@ +dict * {dict text dict window dict} ADDED modules/json/test_support.tcl Index: modules/json/test_support.tcl ================================================================== --- /dev/null +++ modules/json/test_support.tcl @@ -0,0 +1,129 @@ + +#use fileutil/fileutil.tcl fileutil + +catch {unset JSON} +catch {unset TCL} +catch {unset DICTSORT} + +proc dictsort3 {spec data} { + while [llength $spec] { + set type [lindex $spec 0] + set spec [lrange $spec 1 end] + + switch -- $type { + dict { + lappend spec * string + + set json {} + foreach {key} [lsort [dict keys $data]] { + set val [dict get $data $key] + foreach {keymatch valtype} $spec { + if {[string match $keymatch $key]} { + lappend json $key [dictsort3 $valtype $val] + break + } + } + } + return $json + } + list { + lappend spec * string + set json {} + set idx 0 + foreach {val} $data { + foreach {keymatch valtype} $spec { + if {$idx == $keymatch || $keymatch eq "*"} { + lappend json [dictsort3 $valtype $val] + break + } + } + incr idx + } + return $json + } + string { + return $data + } + default { + error "Invalid type" + } + } + } +} + +foreach f [TestFilesGlob test-data/*.json] { + set name [file rootname [file tail $f]] + set JSON($name) [tcltest::viewFile $f] +} + +foreach f [TestFilesGlob test-data/*.result] { + set name [file rootname [file tail $f]] + set TCL($name) [tcltest::viewFile $f] +} + +foreach f [TestFilesGlob test-data/*.sort] { + set name [file rootname [file tail $f]] + set DICTSORT($name) [tcltest::viewFile $f] +} + +# Postprocessing result of one test case, insert proper expected unicodepoint +set TCL(menu) [string map [list @@@ \u6021] $TCL(menu)] + +set JSON(emptyList) {[]} +set TCL(emptyList) {} + +set JSON(emptyList2) {{"menu": []}} +set TCL(emptyList2) {menu {}} + +set JSON(emptyList3) {["menu", []]} +set TCL(emptyList3) {menu {}} + +set JSON(emptyList4) {[[]]} +set TCL(emptyList4) {{}} + +set JSON(escapes) {"\t\r\n\f\b\/\\\""} +set TCL(escapes) "\t\r\n\f\b/\\\"" + + + +foreach f [TestFilesGlob test-data/*.fail] { + set name [file rootname [file tail $f]] + set FAIL($name) [tcltest::viewFile $f] +} + +foreach f [TestFilesGlob test-data/*.err] { + set name [file rootname [file tail $f]] + set ERR($name) [tcltest::viewFile $f] +} + +set FAIL(escape1) {"\%"} +set ERR(escape1-tcl) {unexpected token "END" at position 0; expecting VALUE} +set ERR(escape1-critcl) {invalid string sequence} + +set FAIL(escape2) {"\."} +set ERR(escape2-tcl) {unexpected token "END" at position 0; expecting VALUE} +set ERR(escape2-critcl) {invalid string sequence} + + + +proc resultfor {name} { + global TCL + transform $TCL($name) $name +} + +proc transform {res name} { + global DICTSORT + if {[info exists DICTSORT($name)]} { + return [dictsort3 $DICTSORT($name) $res] + } else { + return $res + } +} + +proc transform* {res args} { + set t {} + foreach r $res n $args { + lappend t [transform $r $n] + } + return $t +} Index: modules/tcllibc.tcl ================================================================== --- modules/tcllibc.tcl +++ modules/tcllibc.tcl @@ -1,10 +1,10 @@ # Umbrella, i.e. Bundle, to put all of the critcl modules which are # found in Tcllib in one shared library. package require critcl -package provide tcllibc 0.3.10 +package provide tcllibc 0.3.11 namespace eval ::tcllib { variable tcllibc_rcsid {$Id: tcllibc.tcl,v 1.13 2010/05/25 19:26:17 andreas_kupries Exp $} critcl::ccode { /* no code required in this file */ Index: support/installation/version.tcl ================================================================== --- support/installation/version.tcl +++ support/installation/version.tcl @@ -8,10 +8,11 @@ dist_exclude modules/fileinput critcl_main tcllibc tcllibc.tcl critcl base64c {base64/base64c.tcl base64/uuencode.tcl base64/yencode.tcl} critcl crcc {crc/crcc.tcl crc/sum.tcl crc/crc32.tcl} +critcl jsonc json/jsonc.tcl critcl md4c md4/md4c.tcl critcl md5c md5/md5c.tcl critcl md5cryptc md5crypt/md5cryptc.tcl critcl rc4c rc4/rc4c.tcl critcl sha1c sha1/sha1c.tcl