├── .cvsignore ├── .gitignore ├── .travis.yml ├── CREDITS ├── EXPERIMENTAL ├── README.md ├── TODO ├── config.m4 ├── config.w32 ├── jsonreader.c ├── jsonreader.php ├── libvktor ├── vktor.c ├── vktor.h ├── vktor_unicode.c └── vktor_unicode.h ├── php_jsonreader.h └── tests ├── 001.phpt ├── 002.phpt ├── 003.phpt ├── 004.phpt ├── 005.phpt ├── 006.phpt ├── 007.phpt ├── 008.phpt ├── 009.phpt ├── 010.phpt ├── 011.phpt ├── 012.phpt ├── 013.phpt └── 014.phpt /.cvsignore: -------------------------------------------------------------------------------- 1 | .deps 2 | *.lo 3 | *.la 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | .deps 3 | .libs/ 4 | Makefile 5 | Makefile.fragments 6 | Makefile.global 7 | Makefile.objects 8 | acinclude.m4 9 | aclocal.m4 10 | autom4te.cache/ 11 | build/ 12 | config.guess 13 | config.h 14 | config.h.in 15 | config.log 16 | config.nice 17 | config.status 18 | config.sub 19 | configure 20 | configure.in 21 | install-sh 22 | jsonreader.la 23 | jsonreader.lo 24 | libtool 25 | libvktor/.libs/ 26 | libvktor/vktor.lo 27 | libvktor/vktor_unicode.lo 28 | ltmain.sh 29 | missing 30 | mkinstalldirs 31 | modules/ 32 | run-tests.php 33 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # 2 | # build and test ext-jsonreader on Travis CI - https://travis-ci.org/ 3 | # 4 | 5 | language: php 6 | sudo: false 7 | php: 8 | - 5.3 9 | - 5.4 10 | - 5.5 11 | - 5.6 12 | - 'nightly' 13 | 14 | env: 15 | - NO_INTERACTION=1 REPORT_EXIT_STATUS=1 16 | 17 | script: 18 | - phpize 19 | - ./configure --enable-shared 20 | - make 21 | - php run-tests.php -q -d extension=`pwd`/.libs/jsonreader.so -p `which php` --show-diff 22 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | JSONReader 2 | Shahar Evron 3 | -------------------------------------------------------------------------------- /EXPERIMENTAL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shevron/ext-jsonreader/f04eb48adbe552bda6f73d45cab804dd41e48676/EXPERIMENTAL -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JSONReader - a JSON stream pull parser for PHP 2 | =============================================================================== 3 | 4 | Introduction 5 | ------------ 6 | The jsonreader extension provides the JSONReader class - a forward-only, memory 7 | efficient pull parser for JSON streams. The API is modeled after the XMLReader 8 | extension API (despite the very unrelated backend technologies) and the 9 | extension is based on the libvktor library which is shipped with the extension. 10 | 11 | jsonreader currently works with PHP 5.x only. 12 | 13 | 14 | Why another JSON extension? 15 | --------------------------- 16 | The existing ext/json which is shipped with PHP is very convenient and simple 17 | to use - but it is inefficient when working with large ammounts of JSON data, 18 | as it requires reading the entire JSON data into memory (e.g. using 19 | file_get_contents()) and then converting it into a PHP variable at once - for 20 | large data sets, this takes up a lot of memory. 21 | 22 | JSONReader is designed for memory efficiency - it works on streams and can 23 | read JSON data from any PHP stream without loading the entire data into memory. 24 | It also allows the developer to extract specific values from a JSON stream 25 | without decoding and loading all data into memory. 26 | 27 | In short, JSONReader should be preferred over ext/json in situations where 28 | memory usage is a concern or when the size of the JSON-encoded data read from 29 | a file or a stream might be significant. It might also be preferred if one 30 | needs to extract a small part of some JSON-encoded data without loading an 31 | entire data set into memory. 32 | 33 | In situations where the size of the JSON data is limited and memory is not a 34 | concern, it is often more convenient to use ext/json. 35 | 36 | 37 | Installation 38 | ------------ 39 | jsonreader is installed like any other PHP extension. To compile on most 40 | Unix-like systems, do the following (assuming you have the nescesary build 41 | toolchain available): 42 | 43 | 1. cd into the source directory of jsonreader 44 | 2. run the following commands (assuming your PHP binaries are in $PATH): 45 | 46 | ```sh 47 | $ phpize 48 | $ ./configure --enable-shared 49 | $ make 50 | $ sudo make install 51 | ``` 52 | 53 | (you can use `su` instead of `sudo` in order to install as root) 54 | 55 | Then, add the following line to your php.ini: 56 | 57 | ```sh 58 | extension=jsonreader.so 59 | ``` 60 | 61 | After restarting your web server JSONReader should be available. 62 | 63 | 64 | Basic Usage 65 | ----------- 66 | The following code demonstrates parsing a simple JSON array of strings from 67 | an HTTP stream: 68 | 69 | ```php 70 | open('http://www.example.com/news.json'); 74 | 75 | while ($reader->read()) { 76 | switch($reader->tokenType) { 77 | case JSONReader::ARRAY_START: 78 | echo "Array start:\n"; 79 | break; 80 | 81 | case JSONReader::ARRAY_END: 82 | echo "Array end.\n"; 83 | break; 84 | 85 | case JSONReader::VALUE: 86 | echo " - " . $reader->value . "\n"; 87 | break; 88 | } 89 | } 90 | 91 | $reader->close(); 92 | 93 | ?> 94 | ``` 95 | 96 | The JSONReader class provides the following methods: 97 | 98 | 99 | ```php 100 | bool JSONReader::open(mixed $URI) 101 | ``` 102 | 103 | Open a stream to read from, or set an open stream as the stream to read from. 104 | 105 | `$URI` can be either a string representing any valid PHP stream URI (such 106 | as 'json.txt', 'http://example.com/path/json' or 'php://stdin') or an 107 | already-open stream resource - this is useful for example if you need to 108 | read the HTTP headers from an open TCP stream before passing the HTTP 109 | body to the parser, or if you need to set up some stream context or 110 | filters before parsing it. 111 | 112 | Returns TRUE on success, FALSE otherwise. 113 | 114 | ```php 115 | bool JSONReader::close(); 116 | ``` 117 | 118 | Close the open stream attached to the parser. Note that if you do not call this method, the stream will be automatically closed when the object is destroyed. 119 | 120 | ```php 121 | bool JSONReader::read(); 122 | ``` 123 | 124 | Read the next JSON token. Returns TRUE as long as there is something to read, or FALSE when reading is done or when an error occured. 125 | 126 | After calling JSONReader::read() you can check the token type, value or other 127 | properties using one of the object properties desctibed below. 128 | 129 | ```php 130 | int JSONReader::tokenType 131 | ``` 132 | 133 | The type of the current token. NULL if there is no current token, or one of the token constants listed below. 134 | 135 | ```php 136 | mixed JSONReader::value 137 | ``` 138 | 139 | The value of the current token, if it has one (not all tokens have a value).
140 | Will be NULL if there is no current token or if the token has no value.
141 | Otherwise, may contain NULL, a boolean, a string, an integer or a floating-point number. 142 | 143 | ```php 144 | int JSONReader::currentStruct 145 | ``` 146 | 147 | The type of the current JSON structure we are in. This might be one of `JSONReader::ARRAY` or `JSONReader::OBJECT` - or NULL if we are in neither. 148 | 149 | ```php 150 | int JSONReader::currentDepth 151 | ``` 152 | 153 | The current nesting level inside the JSON data. 0 means root level. 154 | 155 | The following constants represent the different JSON token types: 156 | 157 | ```php 158 | JSONReader::NULL - a JSON 'null' (this does not equal a PHP NULL) 159 | JSONReader::FALSE - Boolean false 160 | JSONReader::TRUE - Boolean true 161 | JSONReader::INT - An integer value 162 | JSONReader::FLOAT - A floating-point value 163 | JSONReader::STRING - A string 164 | 165 | JSONReader::ARRAY_START - the beginning of an array 166 | JSONReader::ARRAY_END - the end of an array 167 | JSONReader::OBJECT_START - the beginning of an object 168 | JSONReader::OBJECT_KEY - an object key (::value will contain the key) 169 | JSONReader::OBJECT_END - the end of an object 170 | ``` 171 | 172 | Additionally, you may test the value of JSONReader->tokenType against the 173 | following constants that represent classes of token types: 174 | 175 | ```php 176 | JSONReader::BOOLEAN - Either TRUE or FALSE 177 | JSONReader::NUMBER - A numeric type - either an INT or a FLOAT 178 | JSONReader::VALUE - Any value token (including null, booleans, 179 | integers, floats and strings) 180 | ``` 181 | 182 | Note that you shoud use "bitwise and" (&) to compare against these constants - 183 | the value of JSONReader->tokenType will never be equal (==) to any of them: 184 | 185 | ```php 186 | tokenType & JSONReader::NUMBER) { 190 | // do something... 191 | } 192 | 193 | // Check if the current token is boolean 194 | if ($reader->tokenTyppe & JSONReader::BOOLEAN) { 195 | // do something... 196 | } 197 | 198 | // Check if the current token is a value token but not a string 199 | if ($reader->tokenType & (JSONReader::VALUE & ~JSONReader::STRING)) { 200 | // do something ... 201 | } 202 | 203 | // NOTE: This will never be true: 204 | if ($reader->tokenType == JSONReader::VALUE) { 205 | // will never happen! 206 | } 207 | 208 | ?> 209 | ``` 210 | 211 | Constructor Attributes 212 | ---------------------- 213 | The JSONReader constructor optionally accepts an array of attributes: 214 | 215 | ```php 216 | void JSONReader::__construct([array $attributes]) 217 | ``` 218 | 219 | `$attributes` is an associative (attribute => value) array, with the following 220 | constants representing different attributes: 221 | 222 | ```php 223 | JSONReader::ATTR_ERRMODE (NOTE:: Not implemented yet!) 224 | ``` 225 | 226 | Set the reader's error handling method. This might be one of: 227 | 228 | ```php 229 | JSONReader::ERRMODE_PHPERR - report a PHP error / warning (default) 230 | JSONReader::ERRMODE_EXCEPT - throw a JSONReaderException 231 | JSONReader::ERRMODE_INTERN - do not report errors, but keep them internally and allow the programmer to manually check for any errors 232 | ``` 233 | 234 | ```php 235 | JSONReader::ATTR_MAX_DEPTH 236 | ``` 237 | 238 | Set the maximal nesting level of the JSON parser. If the maximal nesting 239 | level is reached, the parser will return with an error. If not specified, 240 | the value of the jsonreader.max_depth INI setting is used, and the default 241 | value is 64. 242 | 243 | There is usually no reason to modify this, unless you know in advance the 244 | JSON structure you are about it read might contain very deep nesting 245 | arrays or objects. 246 | 247 | ```php 248 | JSONReader::ATTR_READ_BUFF 249 | ``` 250 | 251 | Set the stream read buffer size. This sets the largest chunk of data which 252 | is read from the stream per iteration and passed on to the parser. Smaller 253 | values may reduce memory usage but will require more work. If not 254 | specified, the value of the jsonreader.read_buffer INI setting is used, 255 | and the default value of that is 4096. 256 | 257 | There is usually no need to change this. 258 | 259 | The following example demonstrates passing attributes when creating the 260 | object: 261 | 262 | ```php 263 | 128, 269 | JSONReader::ATTR_ERRMODE => JSONReader::ERRMODE_EXCEPT 270 | )); 271 | 272 | ?> 273 | ``` 274 | 275 | 276 | INI Settings 277 | ------------ 278 | The following php.ini directives are available: 279 | 280 | * `jsonreader.max_depth` - The default maximal depth that the reader can handle. The default value is 64. This can be overriden using the `ATTR_MAX_DEPTH` attribute. 281 | 282 | * `jsonreader.read_buffer` - The default read buffer size in bytes. The default value is 4096. This can be overriden using the `ATTR_READ_BUFF` attribute. 283 | 284 | 285 | Caveats / Known Issues 286 | ---------------------- 287 | - Please note that the extension expects input in UTF-8 encoding and decodes any 288 | JSON-encoded special characters into UTF-8. In the future other encodings 289 | might be supported but for now you are adviced to apply an iconv stream filter 290 | if you are reading from a stream which is not UTF-8 encoded. 291 | 292 | - This extension is experimental, the API is likely to change and break in 293 | future versions. 294 | 295 | - See the TODO file for planned functionality which is currently not implemented. 296 | 297 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | TODO for the JSONReader PHP extension 2 | ------------------------------------- 3 | 4 | - Allow reading of overflowing integer or float values as strings 5 | 6 | - Implement ability to parse a string buffer and not only a stream 7 | 8 | - Implement internal error handler 9 | 10 | 11 | Additions that will require improvements to libvktor: 12 | ----------------------------------------------------- 13 | 14 | - Add some abilities to skip ahead 15 | - Over the current value token without reading it 16 | - To a specific key in an object 17 | - A specific number of elements in an array or an object 18 | - Out of the current nesting level ("step out") 19 | 20 | - Add support for additional Unicode encodings (input or output) 21 | 22 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | dnl config.m4 for extension jsonreader 3 | 4 | PHP_ARG_ENABLE(jsonreader, whether to enable jsonreader support, 5 | [ --enable-jsonreader Enable jsonreader support]) 6 | 7 | 8 | if test "$PHP_JSONREADER" != "no"; then 9 | if test "$ZEND_DEBUG" = "yes"; then 10 | AC_DEFINE(ENABLE_DEBUG, 1, [Enable debugging information]) 11 | fi 12 | 13 | PHP_NEW_EXTENSION(jsonreader, jsonreader.c libvktor/vktor_unicode.c libvktor/vktor.c, $ext_shared) 14 | fi 15 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // $Id$ 2 | // vim:ft=javascript 3 | 4 | // If your extension references something external, use ARG_WITH 5 | ARG_WITH("jsonreader", "for jsonreader support", "no"); 6 | 7 | if (PHP_JSONREADER != "no") { 8 | EXTENSION("jsonreader", "jsonreader.c libvktor/vktor.c libvktor/vktor_unicode.c"); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /jsonreader.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2008 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Shahar Evron, shahar@prematureoptimization.org | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id: header,v 1.16.2.1.2.1.2.1 2008/02/07 19:39:50 iliaa Exp $ */ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include "config.h" 23 | #endif 24 | 25 | #include "php.h" 26 | #include "php_ini.h" 27 | #include "ext/standard/info.h" 28 | #include "php_jsonreader.h" 29 | #include "zend_exceptions.h" 30 | 31 | #include "libvktor/vktor.h" 32 | 33 | ZEND_DECLARE_MODULE_GLOBALS(jsonreader) 34 | 35 | static zend_object_handlers jsonreader_obj_handlers; 36 | static zend_class_entry *jsonreader_ce; 37 | static zend_class_entry *jsonreader_exception_ce; 38 | 39 | const HashTable jsonreader_prop_handlers; 40 | 41 | typedef struct _jsonreader_object { 42 | zend_object std; 43 | php_stream *stream; 44 | vktor_parser *parser; 45 | zend_bool close_stream; 46 | long max_depth; 47 | long read_buffer; 48 | int errmode; 49 | } jsonreader_object; 50 | 51 | #define JSONREADER_REG_CLASS_CONST_L(name, value) \ 52 | zend_declare_class_constant_long(jsonreader_ce, name, sizeof(name) - 1, \ 53 | (long) value TSRMLS_CC) 54 | 55 | #define JSONREADER_VALUE_TOKEN VKTOR_T_NULL | \ 56 | VKTOR_T_TRUE | \ 57 | VKTOR_T_FALSE | \ 58 | VKTOR_T_INT | \ 59 | VKTOR_T_FLOAT | \ 60 | VKTOR_T_STRING 61 | 62 | /* {{{ attribute keys and possible values */ 63 | enum { 64 | ATTR_MAX_DEPTH = 1, 65 | ATTR_READ_BUFF, 66 | ATTR_ERRMODE, 67 | 68 | ERRMODE_PHPERR, 69 | ERRMODE_EXCEPT, 70 | ERRMODE_INTERN 71 | }; 72 | /* }}} */ 73 | 74 | /* }}} */ 75 | 76 | /* {{{ Memory management functions wrapping emalloc etc. */ 77 | 78 | /* {{{ jsr_malloc 79 | Wrapper for PHP's emalloc, passed to libvktor as malloc alternative */ 80 | static void *jsr_malloc(size_t size) 81 | { 82 | return emalloc(size); 83 | } 84 | /* }}} */ 85 | 86 | /* {{{ jsr_realloc 87 | Wrapper for PHP's erealloc, passed to libvktor as realloc alternative */ 88 | static void *jsr_realloc(void *ptr, size_t size) 89 | { 90 | return erealloc(ptr, size); 91 | } 92 | /* }}} */ 93 | 94 | /* {{{ jsr_free 95 | Wrapper for PHP's efree, passed to libvktor as free alternative */ 96 | static void jsr_free(void *ptr) 97 | { 98 | efree(ptr); 99 | } 100 | /* }}} */ 101 | 102 | /* }}} */ 103 | 104 | /* {{{ jsonreader_handle_error 105 | Handle a parser error - for now generate an E_WARNING, in the future this might 106 | also do things like throw an exception or use an internal error handler */ 107 | static void jsonreader_handle_error(vktor_error *err, jsonreader_object *obj TSRMLS_DC) 108 | { 109 | switch(obj->errmode) { 110 | case ERRMODE_PHPERR: 111 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "parser error [#%d]: %s", 112 | err->code, err->message); 113 | break; 114 | 115 | case ERRMODE_EXCEPT: 116 | zend_throw_exception_ex(jsonreader_exception_ce, err->code TSRMLS_CC, 117 | err->message); 118 | break; 119 | 120 | default: // For now emit a PHP WARNING 121 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "parser error [#%d]: %s", 122 | err->code, err->message); 123 | break; 124 | } 125 | 126 | vktor_error_free(err); 127 | } 128 | /* }}} */ 129 | 130 | /* {{{ Property access related functions and type definitions */ 131 | 132 | typedef int (*jsonreader_read_t) (jsonreader_object *obj, zval **retval TSRMLS_DC); 133 | typedef int (*jsonreader_write_t) (jsonreader_object *obj, zval *newval TSRMLS_DC); 134 | 135 | typedef struct _jsonreader_prop_handler { 136 | jsonreader_read_t read_func; 137 | jsonreader_write_t write_func; 138 | } jsonreader_prop_handler; 139 | 140 | /* {{{ jsonreader_read_na 141 | Called when a user tries to read a write-only property of a JSONReader object */ 142 | static int jsonreader_read_na(jsonreader_object *obj, zval **retval TSRMLS_DC) 143 | { 144 | *retval = NULL; 145 | php_error_docref(NULL TSRMLS_CC, E_ERROR, "trying to read a write-only property"); 146 | return FAILURE; 147 | } 148 | /* }}} */ 149 | 150 | /* {{{ jsonreader_write_na 151 | Called when a user tries to write to a read-only property of a JSONReader object */ 152 | static int jsonreader_write_na(jsonreader_object *obj, zval *newval TSRMLS_DC) 153 | { 154 | php_error_docref(NULL TSRMLS_CC, E_ERROR, "trying to modify a read-only property"); 155 | return FAILURE; 156 | } 157 | /* }}} */ 158 | 159 | /* {{{ jsonreader_register_prop_handler 160 | Register a read/write handler for a specific property of JSONReader objects */ 161 | static void jsonreader_register_prop_handler(char *name, jsonreader_read_t read_func, jsonreader_write_t write_func TSRMLS_DC) 162 | { 163 | jsonreader_prop_handler jph; 164 | 165 | jph.read_func = read_func ? read_func : jsonreader_read_na; 166 | jph.write_func = write_func ? write_func : jsonreader_write_na; 167 | 168 | zend_hash_add((HashTable *) &jsonreader_prop_handlers, name, strlen(name) + 1, &jph, 169 | sizeof(jsonreader_prop_handler), NULL); 170 | } 171 | /* }}} */ 172 | 173 | /* {{{ jsonreader_read_property 174 | Property read handler */ 175 | zval* jsonreader_read_property(zval *object, zval *member, int type TSRMLS_DC) 176 | { 177 | jsonreader_object *intern; 178 | zval tmp_member; 179 | zval *retval; 180 | jsonreader_prop_handler *jph; 181 | zend_object_handlers *std_hnd; 182 | int ret; 183 | 184 | if (Z_TYPE_P(member) != IS_STRING) { 185 | tmp_member = *member; 186 | zval_copy_ctor(&tmp_member); 187 | convert_to_string(&tmp_member); 188 | member = &tmp_member; 189 | } 190 | 191 | ret = FAILURE; 192 | intern = (jsonreader_object *) zend_objects_get_address(object TSRMLS_CC); 193 | 194 | ret = zend_hash_find(&jsonreader_prop_handlers, Z_STRVAL_P(member), 195 | Z_STRLEN_P(member) + 1, (void **) &jph); 196 | 197 | if (ret == SUCCESS) { 198 | ret = jph->read_func(intern, &retval TSRMLS_CC); 199 | if (ret == SUCCESS) { 200 | Z_SET_REFCOUNT_P(retval, 0); 201 | } else { 202 | retval = EG(uninitialized_zval_ptr); 203 | } 204 | } else { 205 | std_hnd = zend_get_std_object_handlers(); 206 | #if PHP_VERSION_ID < 50399 207 | retval = std_hnd->read_property(object, member, type TSRMLS_CC); 208 | #else 209 | retval = std_hnd->read_property(object, member, type TSRMLS_CC, NULL); 210 | #endif 211 | 212 | } 213 | 214 | if (member == &tmp_member) { 215 | zval_dtor(member); 216 | } 217 | 218 | return retval; 219 | } 220 | /* }}} */ 221 | 222 | /* {{{ jsonreader_write_property */ 223 | void jsonreader_write_property(zval *object, zval *member, zval *value TSRMLS_DC) 224 | { 225 | jsonreader_object *intern; 226 | zval tmp_member; 227 | jsonreader_prop_handler *jph; 228 | zend_object_handlers *std_hnd; 229 | int ret; 230 | 231 | if (Z_TYPE_P(member) != IS_STRING) { 232 | tmp_member = *member; 233 | zval_copy_ctor(&tmp_member); 234 | convert_to_string(&tmp_member); 235 | member = &tmp_member; 236 | } 237 | 238 | ret = FAILURE; 239 | intern = (jsonreader_object *) zend_objects_get_address(object TSRMLS_CC); 240 | 241 | ret = zend_hash_find(&jsonreader_prop_handlers, Z_STRVAL_P(member), 242 | Z_STRLEN_P(member) + 1, (void **) &jph); 243 | 244 | if (ret == SUCCESS) { 245 | jph->write_func(intern, value TSRMLS_CC); 246 | } else { 247 | std_hnd = zend_get_std_object_handlers(); 248 | #if PHP_VERSION_ID < 50399 249 | std_hnd->write_property(object, member, value TSRMLS_CC); 250 | #else 251 | std_hnd->write_property(object, member, value TSRMLS_CC, NULL); 252 | #endif 253 | } 254 | 255 | if (member == &tmp_member) { 256 | zval_dtor(member); 257 | } 258 | } 259 | /* }}} */ 260 | 261 | /* {{{ jsonreader_get_token_type 262 | Get the type of the current token */ 263 | static int jsonreader_get_token_type(jsonreader_object *obj, zval **retval TSRMLS_DC) 264 | { 265 | vktor_token token; 266 | 267 | ALLOC_ZVAL(*retval); 268 | 269 | if (! obj->parser) { 270 | ZVAL_NULL(*retval); 271 | 272 | } else { 273 | token = vktor_get_token_type(obj->parser); 274 | if (token == VKTOR_T_NONE) { 275 | ZVAL_NULL(*retval); 276 | } else { 277 | ZVAL_LONG(*retval, token); 278 | } 279 | } 280 | 281 | return SUCCESS; 282 | } 283 | /* }}} */ 284 | 285 | /* {{{ jsonreader_get_token_value 286 | Get the value of the current token */ 287 | static int jsonreader_get_token_value(jsonreader_object *obj, zval **retval TSRMLS_DC) 288 | { 289 | vktor_token t_type; 290 | vktor_error *err = NULL; 291 | 292 | ALLOC_ZVAL(*retval); 293 | 294 | if (! obj->parser) { 295 | ZVAL_NULL(*retval); 296 | 297 | } else { 298 | t_type = vktor_get_token_type(obj->parser); 299 | switch(t_type) { 300 | case VKTOR_T_NONE: 301 | case VKTOR_T_NULL: 302 | case VKTOR_T_ARRAY_START: 303 | case VKTOR_T_ARRAY_END: 304 | case VKTOR_T_OBJECT_START: 305 | case VKTOR_T_OBJECT_END: 306 | ZVAL_NULL(*retval); 307 | break; 308 | 309 | case VKTOR_T_FALSE: 310 | case VKTOR_T_TRUE: 311 | ZVAL_BOOL(*retval, (t_type == VKTOR_T_TRUE)); 312 | break; 313 | 314 | case VKTOR_T_OBJECT_KEY: 315 | case VKTOR_T_STRING: { 316 | char *strval; 317 | int strlen; 318 | 319 | strlen = vktor_get_value_str(obj->parser, &strval, &err); 320 | if (err != NULL) { 321 | ZVAL_NULL(*retval); 322 | jsonreader_handle_error(err, obj TSRMLS_CC); 323 | return FAILURE; 324 | } 325 | 326 | ZVAL_STRINGL(*retval, strval, strlen, 1); 327 | break; 328 | } 329 | 330 | case VKTOR_T_INT: 331 | ZVAL_LONG(*retval, vktor_get_value_long(obj->parser, &err)); 332 | if (err != NULL) { 333 | ZVAL_NULL(*retval); 334 | jsonreader_handle_error(err, obj TSRMLS_CC); 335 | return FAILURE; 336 | } 337 | break; 338 | 339 | case VKTOR_T_FLOAT: 340 | ZVAL_DOUBLE(*retval, vktor_get_value_double(obj->parser, &err)); 341 | if (err != NULL) { 342 | ZVAL_NULL(*retval); 343 | jsonreader_handle_error(err, obj TSRMLS_CC); 344 | return FAILURE; 345 | } 346 | break; 347 | 348 | default: /* should not happen */ 349 | php_error_docref(NULL TSRMLS_CC, E_ERROR, 350 | "internal error: unkown token type %d", t_type); 351 | return FAILURE; 352 | break; 353 | } 354 | } 355 | 356 | return SUCCESS; 357 | } 358 | /* }}} */ 359 | 360 | /* {{{ jsonreader_get_current_struct 361 | Get the type of the current JSON struct we are in (object, array or none) */ 362 | static int jsonreader_get_current_struct(jsonreader_object *obj, zval **retval TSRMLS_DC) 363 | { 364 | vktor_struct cs; 365 | 366 | ALLOC_ZVAL(*retval); 367 | 368 | if (! obj->parser) { 369 | ZVAL_NULL(*retval); 370 | 371 | } else { 372 | cs = vktor_get_current_struct(obj->parser); 373 | if (cs == VKTOR_STRUCT_NONE) { 374 | ZVAL_NULL(*retval); 375 | } else { 376 | ZVAL_LONG(*retval, cs); 377 | } 378 | } 379 | 380 | return SUCCESS; 381 | } 382 | /* }}} */ 383 | 384 | /* {{{ jsonreader_get_current_depth 385 | Get the current nesting level */ 386 | static int jsonreader_get_current_depth(jsonreader_object *obj, zval **retval TSRMLS_DC) 387 | { 388 | int depth; 389 | 390 | ALLOC_ZVAL(*retval); 391 | 392 | if (! obj->parser) { 393 | ZVAL_NULL(*retval); 394 | 395 | } else { 396 | depth = vktor_get_depth(obj->parser); 397 | ZVAL_LONG(*retval, depth); 398 | } 399 | 400 | return SUCCESS; 401 | } 402 | /* }}} */ 403 | 404 | /* }}} */ 405 | 406 | /* {{{ jsonreader_object_free_storage 407 | C-level object destructor for JSONReader objects */ 408 | static void jsonreader_object_free_storage(void *object TSRMLS_DC) 409 | { 410 | jsonreader_object *intern = (jsonreader_object *) object; 411 | 412 | zend_object_std_dtor(&intern->std TSRMLS_CC); 413 | 414 | if (intern->parser) { 415 | vktor_parser_free(intern->parser); 416 | } 417 | 418 | if (intern->stream && intern->close_stream) { 419 | php_stream_close(intern->stream); 420 | } 421 | 422 | efree(object); 423 | } 424 | /* }}} */ 425 | 426 | /* {{{ jsonreader_object_new 427 | C-level constructor of JSONReader objects. Does not initialize the vktor 428 | parser - this will be initialized when needed, by calling jsonreader_init() */ 429 | static zend_object_value jsonreader_object_new(zend_class_entry *ce TSRMLS_DC) 430 | { 431 | zend_object_value retval; 432 | jsonreader_object *intern; 433 | 434 | intern = ecalloc(1, sizeof(jsonreader_object)); 435 | intern->max_depth = JSONREADER_G(max_depth); 436 | intern->read_buffer = JSONREADER_G(read_buffer); 437 | intern->errmode = ERRMODE_PHPERR; 438 | 439 | zend_object_std_init(&(intern->std), ce TSRMLS_CC); 440 | 441 | #if PHP_VERSION_ID < 50399 442 | zend_hash_copy(intern->std.properties, &ce->default_properties, 443 | (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); 444 | #else 445 | object_properties_init(&(intern->std), ce); 446 | #endif 447 | 448 | retval.handle = zend_objects_store_put(intern, 449 | (zend_objects_store_dtor_t) zend_objects_destroy_object, 450 | (zend_objects_free_object_storage_t) jsonreader_object_free_storage, 451 | NULL TSRMLS_CC); 452 | 453 | retval.handlers = &jsonreader_obj_handlers; 454 | 455 | return retval; 456 | } 457 | /* }}} */ 458 | 459 | /* {{{ jsonreader_init 460 | Initialize or reset an internal jsonreader object struct. Will close & free 461 | any stream opened by the reader, and initialize the associated vktor parser 462 | (and free the old parser, if exists) */ 463 | static void jsonreader_init(jsonreader_object *obj TSRMLS_DC) 464 | { 465 | if (obj->parser) { 466 | vktor_parser_free(obj->parser); 467 | } 468 | obj->parser = vktor_parser_init(obj->max_depth); 469 | 470 | if (obj->stream) { 471 | php_stream_close(obj->stream); 472 | } 473 | } 474 | /* }}} */ 475 | 476 | /* {{{ jsonreader_read_more_data 477 | Read more data from the stream and pass it to the parser */ 478 | static int jsonreader_read_more_data(jsonreader_object *obj TSRMLS_DC) 479 | { 480 | char *buffer; 481 | int read; 482 | vktor_status status; 483 | vktor_error *err; 484 | 485 | buffer = emalloc(sizeof(char) * obj->read_buffer); 486 | 487 | read = php_stream_read(obj->stream, buffer, obj->read_buffer); 488 | if (read <= 0) { 489 | /* done reading or error */ 490 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "JSON stream ended while expecting more data"); 491 | return FAILURE; 492 | } 493 | 494 | status = vktor_feed(obj->parser, buffer, read, 1, &err); 495 | if (status == VKTOR_ERROR) { 496 | jsonreader_handle_error(err, obj TSRMLS_CC); 497 | return FAILURE; 498 | } 499 | 500 | return SUCCESS; 501 | } 502 | /* }}} */ 503 | 504 | /* {{{ jsonreader_read 505 | Read the next token from the JSON stream */ 506 | static int jsonreader_read(jsonreader_object *obj TSRMLS_DC) 507 | { 508 | vktor_status status = VKTOR_OK; 509 | vktor_error *err; 510 | int retval; 511 | 512 | do { 513 | status = vktor_parse(obj->parser, &err); 514 | 515 | switch(status) { 516 | case VKTOR_OK: 517 | retval = SUCCESS; 518 | break; 519 | 520 | case VKTOR_COMPLETE: 521 | retval = FAILURE; /* done, not really a failure */ 522 | break; 523 | 524 | case VKTOR_ERROR: 525 | jsonreader_handle_error(err, obj TSRMLS_CC); 526 | retval = FAILURE; 527 | break; 528 | 529 | case VKTOR_MORE_DATA: 530 | if (jsonreader_read_more_data(obj TSRMLS_CC) == FAILURE) { 531 | retval = FAILURE; 532 | status = VKTOR_ERROR; 533 | } 534 | break; 535 | 536 | default: 537 | /* should not happen! */ 538 | php_error_docref(NULL TSRMLS_CC, E_ERROR, "invalid status from internal JSON parser"); 539 | retval = FAILURE; 540 | break; 541 | } 542 | 543 | } while (status == VKTOR_MORE_DATA); 544 | 545 | return retval; 546 | } 547 | /* }}} */ 548 | 549 | /* {{{ jsonreader_set_attribute 550 | set an attribute of the JSONReader object */ 551 | static void jsonreader_set_attribute(jsonreader_object *obj, ulong attr_key, zval *attr_value TSRMLS_DC) 552 | { 553 | long lval = Z_LVAL_P(attr_value); 554 | 555 | switch(attr_key) { 556 | case ATTR_MAX_DEPTH: 557 | if (lval < 1) { 558 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "maximal nesting level must be more than 0, %ld given", lval); 559 | } else { 560 | obj->max_depth = lval; 561 | } 562 | break; 563 | 564 | case ATTR_READ_BUFF: 565 | if (lval < 1) { 566 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "read buffer size must be more than 0, %ld given", lval); 567 | } else { 568 | obj->read_buffer = lval; 569 | } 570 | break; 571 | 572 | case ATTR_ERRMODE: 573 | switch(lval) { 574 | case ERRMODE_PHPERR: 575 | case ERRMODE_EXCEPT: 576 | case ERRMODE_INTERN: 577 | obj->errmode = (int) lval; 578 | break; 579 | 580 | default: 581 | php_error_docref(NULL TSRMLS_CC, E_WARNING, 582 | "invalid error handler attribute value: %ld", lval); 583 | break; 584 | } 585 | break; 586 | } 587 | } 588 | 589 | /* {{{ proto void JSONReader::__construct([array options]) 590 | Create a new JSONReader object, potentially setting some local attributes */ 591 | PHP_METHOD(jsonreader, __construct) 592 | { 593 | zval *object = getThis(); 594 | zval *options = NULL; 595 | 596 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a", &options) == FAILURE) { 597 | ZVAL_NULL(object); 598 | return; 599 | } 600 | 601 | /* got attributes - set them */ 602 | if (options != NULL) { 603 | jsonreader_object *intern; 604 | zval **attr_value; 605 | char *str_key; 606 | ulong long_key; 607 | 608 | intern = (jsonreader_object *) zend_object_store_get_object(object TSRMLS_CC); 609 | zend_hash_internal_pointer_reset(Z_ARRVAL_P(options)); 610 | while (zend_hash_get_current_data(Z_ARRVAL_P(options), (void **) &attr_value) == SUCCESS && 611 | zend_hash_get_current_key(Z_ARRVAL_P(options), &str_key, &long_key, 0) == HASH_KEY_IS_LONG) { 612 | 613 | jsonreader_set_attribute(intern, long_key, *attr_value TSRMLS_CC); 614 | zend_hash_move_forward(Z_ARRVAL_P(options)); 615 | } 616 | } 617 | } 618 | /* }}} */ 619 | 620 | /* {{{ proto boolean JSONReader::open(mixed URI) 621 | Opens the URI (any valid PHP stream URI) that JSONReader will read 622 | from. Can accept either a URI as a string, or an already-open stream 623 | resource. Returns TRUE on success or FALSE on failure. */ 624 | PHP_METHOD(jsonreader, open) 625 | { 626 | zval *object, *arg; 627 | jsonreader_object *intern; 628 | php_stream *tmp_stream; 629 | int options = ENFORCE_SAFE_MODE | REPORT_ERRORS; 630 | 631 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) { 632 | return; 633 | } 634 | 635 | object = getThis(); 636 | intern = (jsonreader_object *) zend_object_store_get_object(object TSRMLS_CC); 637 | 638 | switch(Z_TYPE_P(arg)) { 639 | case IS_STRING: 640 | tmp_stream = php_stream_open_wrapper(Z_STRVAL_P(arg), "r", options, NULL); 641 | intern->close_stream = 1; 642 | break; 643 | 644 | case IS_RESOURCE: 645 | php_stream_from_zval(tmp_stream, &arg); 646 | intern->close_stream = 0; 647 | break; 648 | 649 | default: 650 | php_error_docref(NULL TSRMLS_CC, E_ERROR, 651 | "argument is expected to be a resource of type stream or a string, %s given", 652 | zend_zval_type_name(arg)); 653 | RETURN_FALSE; 654 | break; 655 | 656 | } 657 | 658 | jsonreader_init(intern TSRMLS_CC); 659 | intern->stream = tmp_stream; 660 | 661 | RETURN_TRUE; 662 | } 663 | /* }}} */ 664 | 665 | /* {{{ proto boolean JSONReader::close() 666 | Close the currently open JSON stream and free related resources */ 667 | PHP_METHOD(jsonreader, close) 668 | { 669 | zval *object; 670 | jsonreader_object *intern; 671 | 672 | object = getThis(); 673 | intern = (jsonreader_object *) zend_object_store_get_object(object TSRMLS_CC); 674 | 675 | /* Close stream, if open */ 676 | if (intern->stream) { 677 | php_stream_close(intern->stream); 678 | intern->stream = NULL; 679 | } 680 | 681 | /* Free parser, if created */ 682 | if (intern->parser) { 683 | vktor_parser_free(intern->parser); 684 | intern->parser = NULL; 685 | } 686 | 687 | RETURN_TRUE; 688 | } 689 | /* }}} */ 690 | 691 | /* {{{ proto boolean JSONReader::read() 692 | Read the next token from the JSON stream. Retuns TRUE as long as something is 693 | read, or FALSE when there is nothing left to read, or when an error occured. */ 694 | PHP_METHOD(jsonreader, read) 695 | { 696 | zval *object; 697 | jsonreader_object *intern; 698 | 699 | RETVAL_TRUE; 700 | 701 | object = getThis(); 702 | intern = (jsonreader_object *) zend_object_store_get_object(object TSRMLS_CC); 703 | 704 | if (! intern->stream) { 705 | php_error_docref(NULL TSRMLS_CC, E_WARNING, 706 | "trying to read but no stream was opened"); 707 | RETURN_FALSE; 708 | } 709 | 710 | /* TODO: replace assertion with an if(!) and init parser (?) */ 711 | assert(intern->parser != NULL); 712 | 713 | if (jsonreader_read(intern TSRMLS_CC) != SUCCESS) { 714 | RETVAL_FALSE; 715 | } 716 | } 717 | /* }}} */ 718 | 719 | /* {{{ ARG_INFO */ 720 | ZEND_BEGIN_ARG_INFO(arginfo_jsonreader___construct, 0) 721 | ZEND_ARG_INFO(0, attributes) 722 | ZEND_END_ARG_INFO() 723 | 724 | ZEND_BEGIN_ARG_INFO(arginfo_jsonreader_open, 0) 725 | ZEND_ARG_INFO(0, URI) 726 | ZEND_END_ARG_INFO() 727 | 728 | ZEND_BEGIN_ARG_INFO(arginfo_jsonreader_close, 0) 729 | ZEND_END_ARG_INFO() 730 | 731 | ZEND_BEGIN_ARG_INFO(arginfo_jsonreader_read, 0) 732 | ZEND_END_ARG_INFO() 733 | /* }}} */ 734 | 735 | /* {{{ zend_function_entry jsonreader_class_methods */ 736 | static const zend_function_entry jsonreader_class_methods[] = { 737 | PHP_ME(jsonreader, __construct, arginfo_jsonreader___construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) 738 | PHP_ME(jsonreader, open, arginfo_jsonreader_open, ZEND_ACC_PUBLIC) 739 | PHP_ME(jsonreader, close, arginfo_jsonreader_close, ZEND_ACC_PUBLIC) 740 | PHP_ME(jsonreader, read, arginfo_jsonreader_read, ZEND_ACC_PUBLIC) 741 | {NULL, NULL, NULL} 742 | }; 743 | /* }}} */ 744 | 745 | #ifdef COMPILE_DL_JSONREADER 746 | ZEND_GET_MODULE(jsonreader) 747 | #endif 748 | 749 | /* {{{ PHP_INI */ 750 | PHP_INI_BEGIN() 751 | STD_PHP_INI_ENTRY("jsonreader.max_depth", "64", PHP_INI_ALL, OnUpdateLong, max_depth, zend_jsonreader_globals, jsonreader_globals) 752 | STD_PHP_INI_ENTRY("jsonreader.read_buffer", "4096", PHP_INI_ALL, OnUpdateLong, read_buffer, zend_jsonreader_globals, jsonreader_globals) 753 | PHP_INI_END() 754 | /* }}} */ 755 | 756 | /* {{{ PHP_GINIT_FUNCTION */ 757 | PHP_GINIT_FUNCTION(jsonreader) 758 | { 759 | jsonreader_globals->max_depth = 64; 760 | jsonreader_globals->read_buffer = 4096; 761 | } 762 | /* }}} */ 763 | 764 | /* {{{ PHP_MINIT_FUNCTION */ 765 | PHP_MINIT_FUNCTION(jsonreader) 766 | { 767 | zend_class_entry ce; 768 | 769 | REGISTER_INI_ENTRIES(); 770 | 771 | /** 772 | * Declare the JSONReader class 773 | */ 774 | 775 | /* Set object handlers */ 776 | zend_hash_init((HashTable *) &jsonreader_prop_handlers, 0, NULL, NULL, 1); 777 | memcpy(&jsonreader_obj_handlers, zend_get_std_object_handlers(), 778 | sizeof(zend_object_handlers)); 779 | jsonreader_obj_handlers.read_property = jsonreader_read_property; 780 | jsonreader_obj_handlers.write_property = jsonreader_write_property; 781 | 782 | /* Initalize the class entry */ 783 | INIT_CLASS_ENTRY(ce, "JSONReader", jsonreader_class_methods); 784 | ce.create_object = jsonreader_object_new; 785 | 786 | jsonreader_ce = zend_register_internal_class(&ce TSRMLS_CC); 787 | 788 | /* Register class constants */ 789 | JSONREADER_REG_CLASS_CONST_L("ATTR_MAX_DEPTH", ATTR_MAX_DEPTH); 790 | JSONREADER_REG_CLASS_CONST_L("ATTR_READ_BUFF", ATTR_READ_BUFF); 791 | JSONREADER_REG_CLASS_CONST_L("ATTR_ERRMODE", ATTR_ERRMODE); 792 | JSONREADER_REG_CLASS_CONST_L("ERRMODE_PHPERR", ERRMODE_PHPERR); 793 | JSONREADER_REG_CLASS_CONST_L("ERRMODE_EXCEPT", ERRMODE_EXCEPT); 794 | JSONREADER_REG_CLASS_CONST_L("ERRMODE_INTERN", ERRMODE_INTERN); 795 | 796 | JSONREADER_REG_CLASS_CONST_L("NULL", VKTOR_T_NULL); 797 | JSONREADER_REG_CLASS_CONST_L("FALSE", VKTOR_T_FALSE); 798 | JSONREADER_REG_CLASS_CONST_L("TRUE", VKTOR_T_TRUE); 799 | JSONREADER_REG_CLASS_CONST_L("BOOLEAN", VKTOR_T_FALSE | VKTOR_T_TRUE); 800 | JSONREADER_REG_CLASS_CONST_L("INT", VKTOR_T_INT); 801 | JSONREADER_REG_CLASS_CONST_L("FLOAT", VKTOR_T_FLOAT); 802 | JSONREADER_REG_CLASS_CONST_L("NUMBER", VKTOR_T_INT | VKTOR_T_FLOAT); 803 | JSONREADER_REG_CLASS_CONST_L("STRING", VKTOR_T_STRING); 804 | JSONREADER_REG_CLASS_CONST_L("VALUE", JSONREADER_VALUE_TOKEN); 805 | JSONREADER_REG_CLASS_CONST_L("ARRAY_START", VKTOR_T_ARRAY_START); 806 | JSONREADER_REG_CLASS_CONST_L("ARRAY_END", VKTOR_T_ARRAY_END); 807 | JSONREADER_REG_CLASS_CONST_L("OBJECT_START", VKTOR_T_OBJECT_START); 808 | JSONREADER_REG_CLASS_CONST_L("OBJECT_KEY", VKTOR_T_OBJECT_KEY); 809 | JSONREADER_REG_CLASS_CONST_L("OBJECT_END", VKTOR_T_OBJECT_END); 810 | 811 | JSONREADER_REG_CLASS_CONST_L("ARRAY", VKTOR_STRUCT_ARRAY); 812 | JSONREADER_REG_CLASS_CONST_L("OBJECT", VKTOR_STRUCT_OBJECT); 813 | 814 | /* Register property handlers */ 815 | jsonreader_register_prop_handler("tokenType", jsonreader_get_token_type, NULL TSRMLS_CC); 816 | jsonreader_register_prop_handler("value", jsonreader_get_token_value, NULL TSRMLS_CC); 817 | jsonreader_register_prop_handler("currentStruct", jsonreader_get_current_struct, NULL TSRMLS_CC); 818 | jsonreader_register_prop_handler("currentDepth", jsonreader_get_current_depth, NULL TSRMLS_CC); 819 | 820 | /** 821 | * Declare the JSONReaderException class 822 | */ 823 | INIT_CLASS_ENTRY(ce, "JSONReaderException", NULL); 824 | jsonreader_exception_ce = zend_register_internal_class_ex(&ce, 825 | zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC); 826 | 827 | /** 828 | * Set libvktor to use PHP memory allocation functions 829 | */ 830 | vktor_set_memory_handlers(jsr_malloc, jsr_realloc, jsr_free); 831 | 832 | return SUCCESS; 833 | } 834 | /* }}} */ 835 | 836 | /* {{{ PHP_MSHUTDOWN_FUNCTION */ 837 | PHP_MSHUTDOWN_FUNCTION(jsonreader) 838 | { 839 | UNREGISTER_INI_ENTRIES(); 840 | zend_hash_destroy((HashTable *) &jsonreader_prop_handlers); 841 | return SUCCESS; 842 | } 843 | /* }}} */ 844 | 845 | /* {{{ PHP_MINFO_FUNCTION */ 846 | PHP_MINFO_FUNCTION(jsonreader) 847 | { 848 | php_info_print_table_start(); 849 | php_info_print_table_header(2, "jsonreader support", "enabled"); 850 | php_info_print_table_end(); 851 | 852 | DISPLAY_INI_ENTRIES(); 853 | } 854 | /* }}} */ 855 | 856 | /* {{{ jsonreader_module_entry */ 857 | zend_module_entry jsonreader_module_entry = { 858 | STANDARD_MODULE_HEADER, 859 | "JSONReader", 860 | NULL, 861 | PHP_MINIT(jsonreader), 862 | PHP_MSHUTDOWN(jsonreader), 863 | NULL, 864 | NULL, 865 | PHP_MINFO(jsonreader), 866 | "0.1", 867 | PHP_MODULE_GLOBALS(jsonreader), 868 | PHP_GINIT(jsonreader), 869 | NULL, 870 | NULL, 871 | STANDARD_MODULE_PROPERTIES_EX 872 | }; 873 | /* }}} */ 874 | 875 | /* 876 | * Local variables: 877 | * tab-width: 4 878 | * c-basic-offset: 4 879 | * End: 880 | * vim600: noet sw=4 ts=4 fdm=marker 881 | * vim<600: noet sw=4 ts=4 882 | */ 883 | -------------------------------------------------------------------------------- /jsonreader.php: -------------------------------------------------------------------------------- 1 | "; 3 | 4 | if(!extension_loaded('jsonreader')) { 5 | dl('jsonreader.' . PHP_SHLIB_SUFFIX); 6 | } 7 | $module = 'jsonreader'; 8 | $functions = get_extension_funcs($module); 9 | echo "Functions available in the test extension:$br\n"; 10 | foreach($functions as $func) { 11 | echo $func."$br\n"; 12 | } 13 | echo "$br\n"; 14 | $function = 'confirm_' . $module . '_compiled'; 15 | if (extension_loaded($module)) { 16 | $str = $function($module); 17 | } else { 18 | $str = "Module $module is not compiled into PHP"; 19 | } 20 | echo "$str\n"; 21 | ?> 22 | -------------------------------------------------------------------------------- /libvktor/vktor.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vktor JSON pull-parser library 3 | * 4 | * Copyright (c) 2009 Shahar Evron 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, 10 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the 12 | * Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | /** 29 | * @file vktor.c 30 | * 31 | * Main vktor library file. Defines the external API of vktor as well as some 32 | * internal static functions, data types and macros. 33 | */ 34 | 35 | /** 36 | * @defgroup internal Internal API 37 | * @internal 38 | * @{ 39 | */ 40 | 41 | #ifdef HAVE_CONFIG_H 42 | #include "config.h" 43 | #endif 44 | 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | 52 | #include "vktor.h" 53 | #include "vktor_unicode.h" 54 | 55 | /** 56 | * Maximal error string length (mostly for internal use). 57 | */ 58 | #define VKTOR_MAX_E_LEN 256 59 | 60 | /** 61 | * Memory allocation chunk size used when reading strings. Make sure it is 62 | * never below 4 which is the largent possible size of a Unicode character. 63 | */ 64 | #ifndef VKTOR_STR_MEMCHUNK 65 | #define VKTOR_STR_MEMCHUNK 128 66 | #endif 67 | 68 | /** 69 | * Memory allocation chunk size used when reading numbers 70 | */ 71 | #ifndef VKTOR_NUM_MEMCHUNK 72 | #define VKTOR_NUM_MEMCHUNK 32 73 | #endif 74 | 75 | /** 76 | * Convenience macro to check if we are at the end of a buffer 77 | */ 78 | #define eobuffer(b) (b->ptr >= b->size) 79 | 80 | /** 81 | * Set some macros depending on whether byte counting is enabled or not 82 | */ 83 | #ifdef BYTECOUNTER 84 | 85 | #define INCREMENT_BUFFER_PTR(p) \ 86 | p->buffer->ptr++; \ 87 | p->bytecounter++; 88 | 89 | #define BYTECOUNT_TPL " at %d bytes" 90 | #define BYTECOUNT_VAL , parser->bytecounter 91 | 92 | #else 93 | 94 | #define INCREMENT_BUFFER_PTR(p) p->buffer->ptr++; 95 | #define BYTECOUNT_TPL 96 | #define BYTECOUNT_VAL 97 | 98 | #endif 99 | 100 | 101 | /** 102 | * When debugging is enabled, file and line info can be added to error macros 103 | */ 104 | #ifdef ENABLE_DEBUG 105 | #define _VK_QUOTEME(x) #x 106 | #define VK_QUOTEME(x) _VK_QUOTEME(x) 107 | #define LINEINFO "[" __FILE__ ":" VK_QUOTEME(__LINE__) "] " 108 | #else 109 | #define LINEINFO 110 | #endif 111 | 112 | /** 113 | * Convenience macro to set an 'unexpected character' error 114 | */ 115 | #define set_error_unexpected_c(e, c) \ 116 | set_error(e, VKTOR_ERR_UNEXPECTED_INPUT, \ 117 | LINEINFO "Unexpected character in input: '%c' (0x%02hhx)" \ 118 | BYTECOUNT_TPL, c, c BYTECOUNT_VAL) 119 | 120 | /** 121 | * A bitmask representing any 'value' token 122 | */ 123 | #define VKTOR_VALUE_TOKEN VKTOR_T_NULL | \ 124 | VKTOR_T_FALSE | \ 125 | VKTOR_T_TRUE | \ 126 | VKTOR_T_INT | \ 127 | VKTOR_T_FLOAT | \ 128 | VKTOR_T_STRING | \ 129 | VKTOR_T_ARRAY_START | \ 130 | VKTOR_T_OBJECT_START 131 | 132 | /** 133 | * Convenience macro to check if we are in a specific type of JSON struct 134 | */ 135 | #define nest_stack_in(p, c) (p->nest_stack[p->nest_ptr] == c) 136 | 137 | /** 138 | * Convenience macro to easily set the expected next token map after a value 139 | * token, taking current struct struct (if any) into account. 140 | */ 141 | #define expect_next_value_token(p) \ 142 | switch(p->nest_stack[p->nest_ptr]) { \ 143 | case VKTOR_STRUCT_OBJECT: \ 144 | p->expected = VKTOR_C_COMMA | \ 145 | VKTOR_T_OBJECT_END; \ 146 | break; \ 147 | \ 148 | case VKTOR_STRUCT_ARRAY: \ 149 | p->expected = VKTOR_C_COMMA | \ 150 | VKTOR_T_ARRAY_END; \ 151 | break; \ 152 | \ 153 | default: \ 154 | p->expected = VKTOR_T_NONE; \ 155 | break; \ 156 | } 157 | 158 | /** 159 | * Convenience macro to check, and reallocate if needed, the memory size for 160 | * reading a token 161 | */ 162 | #define check_reallocate_token_memory(cs) \ 163 | if ((ptr + 5) >= maxlen) { \ 164 | maxlen = maxlen + cs; \ 165 | if ((token = vrealloc(token, maxlen * sizeof(char))) == NULL) { \ 166 | set_error(error, VKTOR_ERR_OUT_OF_MEMORY, \ 167 | "unable to allocate %d more bytes for string parsing" \ 168 | LINEINFO, cs); \ 169 | return VKTOR_ERROR; \ 170 | } \ 171 | } 172 | 173 | /** 174 | * Buffer struct, containing some text to parse along with an internal pointer 175 | * and a link to the next buffer. 176 | * 177 | * vktor internally holds text to be parsed as a linked list of buffers pushed 178 | * by the user, so no memory reallocations are required. Whenever a buffer is 179 | * completely parsed, the parser will advance to the next buffer pointed by 180 | * #next_buff and will free the previous buffer 181 | * 182 | * This is done internally by the parser 183 | */ 184 | typedef struct _vktor_buffer_struct { 185 | char *text; /**< buffer text */ 186 | long size; /**< buffer size */ 187 | long ptr; /**< internal buffer position */ 188 | char free; /**< free the bffer when done */ 189 | struct _vktor_buffer_struct *next_buff; /**< pointer to the next buffer */ 190 | } vktor_buffer; 191 | 192 | /** 193 | * Parser struct - this is the main object used by the user to parse a JSON 194 | * stream. 195 | */ 196 | struct _vktor_parser_struct { 197 | vktor_buffer *buffer; /**< the current buffer being parsed */ 198 | vktor_buffer *last_buffer; /**< a pointer to the last buffer */ 199 | vktor_token token_type; /**< current token type */ 200 | void *token_value; /**< current token value, if any */ 201 | int token_size; /**< current token value length, if any */ 202 | char token_resume; /**< current token is only half read */ 203 | long expected; /**< bitmask of possible expected tokens */ 204 | vktor_struct *nest_stack; /**< array holding current nesting stack */ 205 | int nest_ptr; /**< pointer to the current nesting level */ 206 | int max_nest; /**< maximal nesting level */ 207 | unsigned long unicode_c; /**< temp container for unicode characters */ 208 | #ifdef BYTECOUNTER 209 | /** Total bytes parsed counter, only enabled if BYTECOUNTER is defined **/ 210 | unsigned long bytecounter; 211 | #endif 212 | }; 213 | 214 | /** 215 | * @enum vktor_specialchar 216 | * 217 | * Special JSON characters - these are not returned to the user as tokens 218 | * but still have a meaning when parsing JSON. 219 | */ 220 | typedef enum { 221 | VKTOR_C_COMMA = 1 << 16, /**< ",", used as struct separator */ 222 | VKTOR_C_COLON = 1 << 17, /**< ":", used to separate object key:value */ 223 | VKTOR_C_DOT = 1 << 18, /**< ".", used in floating-point numbers */ 224 | VKTOR_C_SIGNUM = 1 << 19, /**< "+" or "-" used in numbers */ 225 | VKTOR_C_EXP = 1 << 20, /**< "e" or "E" used for number exponent */ 226 | VKTOR_C_ESCAPED = 1 << 21, /**< An escaped character */ 227 | VKTOR_C_UNIC1 = 1 << 22, /**< Unicode encoded character (1st byte) */ 228 | VKTOR_C_UNIC2 = 1 << 23, /**< Unicode encoded character (2nd byte) */ 229 | VKTOR_C_UNIC3 = 1 << 24, /**< Unicode encoded character (3rd byte) */ 230 | VKTOR_C_UNIC4 = 1 << 25, /**< Unicode encoded character (4th byte) */ 231 | VKTOR_C_UNIC_LS = 1 << 26, /**< Unicode low surrogate */ 232 | } vktor_specialchar; 233 | 234 | static vktor_malloc vmalloc = malloc; 235 | static vktor_free vfree = free; 236 | static vktor_realloc vrealloc = realloc; 237 | 238 | /** 239 | * @brief Free a vktor_buffer struct 240 | * 241 | * Free a vktor_buffer struct without following any next buffers in the chain. 242 | * Call buffer_free_all() to free an entire chain of buffers. 243 | * 244 | * @param[in,out] buffer the buffer to free 245 | */ 246 | static void 247 | buffer_free(vktor_buffer *buffer) 248 | { 249 | assert(buffer != NULL); 250 | assert(buffer->text != NULL); 251 | 252 | vfree(buffer->text); 253 | vfree(buffer); 254 | } 255 | 256 | /** 257 | * @brief Free an entire linked list of vktor buffers 258 | * 259 | * Free an entire linked list of vktor buffers. Will usually be called by 260 | * vktor_parser_free() to free all buffers attached to a parser. 261 | * 262 | * @param[in,out] buffer the first buffer in the list to free 263 | */ 264 | static void 265 | buffer_free_all(vktor_buffer *buffer) 266 | { 267 | vktor_buffer *next; 268 | 269 | while (buffer != NULL) { 270 | next = buffer->next_buff; 271 | if (buffer->free) { 272 | buffer_free(buffer); 273 | } 274 | buffer = next; 275 | } 276 | } 277 | 278 | /** 279 | * @brief Initialize and populate new error struct 280 | * 281 | * If eptr is NULL, will do nothing. Otherwise, will initialize a new error 282 | * struct with an error message and code, and set eptr to point to it. 283 | * 284 | * The error message is passed as an sprintf-style format and an arbitrary set 285 | * of parameters, just like sprintf() would be used. 286 | * 287 | * Used internally to pass error messages back to the user. 288 | * 289 | * @param [in,out] eptr error struct pointer-pointer to populate or NULL 290 | * @param [in] code error code 291 | * @param [in] msg error message (sprintf-style format) 292 | */ 293 | static void 294 | set_error(vktor_error **eptr, vktor_errcode code, const char *msg, ...) 295 | { 296 | vktor_error *err; 297 | 298 | if (eptr == NULL) { 299 | return; 300 | } 301 | 302 | if ((err = vmalloc(sizeof(vktor_error))) == NULL) { 303 | return; 304 | } 305 | 306 | err->code = code; 307 | err->message = vmalloc(VKTOR_MAX_E_LEN * sizeof(char)); 308 | if (err->message != NULL) { 309 | va_list ap; 310 | 311 | va_start(ap, msg); 312 | vsnprintf(err->message, VKTOR_MAX_E_LEN, msg, ap); 313 | va_end(ap); 314 | } 315 | 316 | *eptr = err; 317 | } 318 | 319 | /** 320 | * @brief Initialize a vktor buffer struct 321 | * 322 | * Initialize a vktor buffer struct and set it's associated text and other 323 | * properties 324 | * 325 | * @param [in] text buffer contents 326 | * @param [in] text_len the length of the buffer 327 | * @param [in] free whether to free the buffer when done or not 328 | * @return A newly-allocated buffer struct 329 | */ 330 | static vktor_buffer* 331 | buffer_init(char *text, long text_len, char free) 332 | { 333 | vktor_buffer *buffer; 334 | 335 | if ((buffer = vmalloc(sizeof(vktor_buffer))) == NULL) { 336 | return NULL; 337 | } 338 | 339 | buffer->text = text; 340 | buffer->size = text_len; 341 | buffer->ptr = 0; 342 | buffer->free = free; 343 | buffer->next_buff = NULL; 344 | 345 | return buffer; 346 | } 347 | 348 | /** 349 | * @brief Advance the parser to the next buffer 350 | * 351 | * Advance the parser to the next buffer in the parser's buffer list. Called 352 | * when the end of the current buffer is reached, and more data is required. 353 | * 354 | * If no further buffers are available, will set vktor_parser->buffer and 355 | * vktor_parser->last_buffer to NULL. 356 | * 357 | * @param [in,out] parser The parser we are working with 358 | */ 359 | static void 360 | parser_advance_buffer(vktor_parser *parser) 361 | { 362 | vktor_buffer *next; 363 | 364 | assert(parser->buffer != NULL); 365 | assert(eobuffer(parser->buffer)); 366 | 367 | next = parser->buffer->next_buff; 368 | if (parser->buffer->free) { 369 | buffer_free(parser->buffer); 370 | } 371 | parser->buffer = next; 372 | 373 | if (parser->buffer == NULL) { 374 | parser->last_buffer = NULL; 375 | } 376 | } 377 | 378 | /** 379 | * @brief Set the current token just read by the parser 380 | * 381 | * Set the current token just read by the parser. Called when a token is 382 | * encountered, before returning from vktor_parse(). The user can then access 383 | * the token information. Will also take care of freeing any previous token 384 | * held by the parser. 385 | * 386 | * @param [in,out] parser Parser object 387 | * @param [in] token New token type 388 | * @param [in] value New token value or NULL if no value 389 | */ 390 | static void 391 | parser_set_token(vktor_parser *parser, vktor_token token, void *value) 392 | { 393 | parser->token_type = token; 394 | if (parser->token_value != NULL) { 395 | vfree(parser->token_value); 396 | } 397 | parser->token_value = value; 398 | } 399 | 400 | /** 401 | * @brief add a nesting level to the nesting stack 402 | * 403 | * Add a nesting level to the nesting stack when a new array or object is 404 | * encountered. Will make sure that the maximal nesting level is not 405 | * overflowed. 406 | * 407 | * @param [in,out] parser Parser object 408 | * @param [in] nest_type nesting type - array or object 409 | * @param [out] error an error struct pointer pointer or NULL 410 | * 411 | * @return Status code - VKTOR_OK or VKTOR_ERROR 412 | */ 413 | static vktor_status 414 | nest_stack_add(vktor_parser *parser, vktor_struct nest_type, 415 | vktor_error **error) 416 | { 417 | assert(parser != NULL); 418 | 419 | parser->nest_ptr++; 420 | if (parser->nest_ptr >= parser->max_nest) { 421 | set_error(error, VKTOR_ERR_MAX_NEST, 422 | "maximal nesting level of %d reached", parser->max_nest); 423 | return VKTOR_ERROR; 424 | } 425 | 426 | parser->nest_stack[parser->nest_ptr] = nest_type; 427 | 428 | return VKTOR_OK; 429 | } 430 | 431 | /** 432 | * @brief pop a nesting level out of the nesting stack 433 | * 434 | * Pop a nesting level out of the nesting stack when the end of an array or an 435 | * object is encountered. Will ensure there are no stack underflows. 436 | * 437 | * @param [in,out] parser Parser object 438 | * @param [out] error struct pointer pointer or NULL 439 | * 440 | * @return Status code: VKTOR_OK or VKTOR_ERROR 441 | */ 442 | static vktor_status 443 | nest_stack_pop(vktor_parser *parser, vktor_error **error) 444 | { 445 | assert(parser != NULL); 446 | assert(parser->nest_stack[parser->nest_ptr]); 447 | 448 | parser->nest_ptr--; 449 | if (parser->nest_ptr < 0) { 450 | set_error(error, VKTOR_ERR_INTERNAL_ERR, 451 | "internal parser error: nesting stack pointer underflow"); 452 | return VKTOR_ERROR; 453 | } 454 | 455 | return VKTOR_OK; 456 | } 457 | 458 | /** 459 | * @brief Read a string token 460 | * 461 | * Read a string token until the ending double-quote. Will decode any special 462 | * escaped characters found along the way, and will gracefully handle buffer 463 | * replacement. 464 | * 465 | * Used by parser_read_string_token() and parser_read_objkey_token() 466 | * 467 | * @param [in,out] parser Parser object 468 | * @param [out] error Error object pointer pointer or NULL 469 | * 470 | * @return Status code 471 | * 472 | * @todo Handle control characters and unicode 473 | * @todo Handle quoted special characters 474 | */ 475 | static vktor_status 476 | parser_read_string(vktor_parser *parser, vktor_error **error) 477 | { 478 | char c; 479 | char *token; 480 | int ptr, maxlen; 481 | int done = 0; 482 | 483 | assert(parser != NULL); 484 | 485 | // Allocate memory for reading the string 486 | 487 | if (parser->token_resume) { 488 | ptr = parser->token_size; 489 | if (ptr < VKTOR_STR_MEMCHUNK) { 490 | maxlen = VKTOR_STR_MEMCHUNK; 491 | token = (void *) parser->token_value; 492 | assert(token != NULL); 493 | } else { 494 | maxlen = ptr + VKTOR_STR_MEMCHUNK; 495 | token = vrealloc(parser->token_value, sizeof(char) * maxlen); 496 | } 497 | 498 | } else { 499 | token = vmalloc(VKTOR_STR_MEMCHUNK * sizeof(char)); 500 | maxlen = VKTOR_STR_MEMCHUNK; 501 | ptr = 0; 502 | } 503 | 504 | if (token == NULL) { 505 | set_error(error, VKTOR_ERR_OUT_OF_MEMORY, 506 | "unable to allocate %d bytes for string parsing", 507 | VKTOR_STR_MEMCHUNK); 508 | return VKTOR_ERROR; 509 | } 510 | 511 | // Read string from buffer 512 | 513 | while (parser->buffer != NULL) { 514 | while (! eobuffer(parser->buffer)) { 515 | c = parser->buffer->text[parser->buffer->ptr]; 516 | 517 | // Read an escaped character (previous char was '/') 518 | if (parser->expected == VKTOR_C_ESCAPED) { 519 | switch (c) { 520 | case '"': 521 | case '\\': 522 | case '/': 523 | token[ptr++] = c; 524 | check_reallocate_token_memory(VKTOR_STR_MEMCHUNK); 525 | parser->expected = VKTOR_T_STRING; 526 | break; 527 | 528 | case 'b': 529 | token[ptr++] = '\b'; 530 | check_reallocate_token_memory(VKTOR_STR_MEMCHUNK); 531 | parser->expected = VKTOR_T_STRING; 532 | break; 533 | 534 | case 'f': 535 | token[ptr++] = '\f'; 536 | check_reallocate_token_memory(VKTOR_STR_MEMCHUNK); 537 | parser->expected = VKTOR_T_STRING; 538 | break; 539 | 540 | case 'n': 541 | token[ptr++] = '\n'; 542 | check_reallocate_token_memory(VKTOR_STR_MEMCHUNK); 543 | parser->expected = VKTOR_T_STRING; 544 | break; 545 | 546 | case 'r': 547 | token[ptr++] = '\r'; 548 | check_reallocate_token_memory(VKTOR_STR_MEMCHUNK); 549 | parser->expected = VKTOR_T_STRING; 550 | break; 551 | 552 | case 't': 553 | token[ptr++] = '\t'; 554 | check_reallocate_token_memory(VKTOR_STR_MEMCHUNK); 555 | parser->expected = VKTOR_T_STRING; 556 | break; 557 | 558 | case 'u': 559 | // Read an escaped unicode character 560 | parser->expected = VKTOR_C_UNIC1; 561 | break; 562 | 563 | default: 564 | // what is this? 565 | // throw an error or deal as a regular character? 566 | set_error_unexpected_c(error, c); 567 | return VKTOR_ERROR; 568 | break; 569 | } 570 | 571 | } else if (parser->expected & (VKTOR_C_UNIC1 | 572 | VKTOR_C_UNIC2 | 573 | VKTOR_C_UNIC3 | 574 | VKTOR_C_UNIC4)) { 575 | 576 | // Read an escaped unicode sequence 577 | c = vktor_unicode_hex_to_int((unsigned char) c); 578 | switch(parser->expected) { 579 | 580 | case VKTOR_C_UNIC1: 581 | parser->unicode_c = parser->unicode_c | (c << 12); 582 | parser->expected = VKTOR_C_UNIC2; 583 | break; 584 | 585 | case VKTOR_C_UNIC2: 586 | parser->unicode_c = parser->unicode_c | (c << 8); 587 | parser->expected = VKTOR_C_UNIC3; 588 | break; 589 | 590 | case VKTOR_C_UNIC3: 591 | parser->unicode_c = parser->unicode_c | (c << 4); 592 | parser->expected = VKTOR_C_UNIC4; 593 | break; 594 | 595 | case VKTOR_C_UNIC4: 596 | parser->unicode_c = parser->unicode_c | c; 597 | parser->expected = parser->expected = VKTOR_T_STRING; 598 | 599 | if (VKTOR_UNICODE_HIGH_SURROGATE(parser->unicode_c)) { 600 | // Expecting a low surrogate 601 | parser->unicode_c <<= 16; 602 | parser->expected = VKTOR_C_UNIC_LS; 603 | 604 | } else if(parser->unicode_c > 0xffff) { 605 | // Found the low surrogate pair? 606 | if (! VKTOR_UNICODE_LOW_SURROGATE((parser->unicode_c & 0x0000ffff))) { 607 | // invalid low surrogate 608 | set_error_unexpected_c(error, c); 609 | return VKTOR_ERROR; 610 | 611 | } 612 | 613 | // Convert surrogate pair to UTF-8 614 | unsigned char utf8[5]; 615 | short l, i; 616 | 617 | l = vktor_unicode_sp_to_utf8( 618 | (unsigned short) (parser->unicode_c >> 16), 619 | (unsigned short) parser->unicode_c, 620 | utf8); 621 | if (l == 0) { 622 | // invalid surrogate pair 623 | set_error_unexpected_c(error, c); 624 | return VKTOR_ERROR; 625 | } 626 | 627 | for (i = 0; i < l; i++) { 628 | token[ptr++] = utf8[i]; 629 | } 630 | check_reallocate_token_memory(VKTOR_STR_MEMCHUNK); 631 | parser->unicode_c = 0; 632 | parser->expected = VKTOR_T_STRING; 633 | 634 | } else { 635 | unsigned char utf8[4]; 636 | short l, i; 637 | 638 | // Get the character as UTF8 and add it to the string 639 | l = vktor_unicode_cp_to_utf8((unsigned short) parser->unicode_c, utf8); 640 | if (l == 0) { 641 | // invalid Unicode character 642 | set_error_unexpected_c(error, c); 643 | return VKTOR_ERROR; 644 | } 645 | 646 | for (i = 0; i < l; i++) { 647 | token[ptr++] = utf8[i]; 648 | } 649 | check_reallocate_token_memory(VKTOR_STR_MEMCHUNK); 650 | parser->unicode_c = 0; 651 | parser->expected = VKTOR_T_STRING; 652 | } 653 | 654 | break; 655 | 656 | default: // should not happen 657 | set_error(error, VKTOR_ERR_INTERNAL_ERR, 658 | "internal parser error: expecing a Unicode sequence character"); 659 | return VKTOR_ERROR; 660 | break; 661 | } 662 | 663 | } else if (parser->expected == VKTOR_C_UNIC_LS) { 664 | // Expecting another unicode character 665 | switch(c) { 666 | case '\\': 667 | break; 668 | 669 | case 'u': 670 | parser->expected = VKTOR_C_UNIC1; 671 | break; 672 | 673 | default: 674 | set_error_unexpected_c(error, c); 675 | return VKTOR_ERROR; 676 | break; 677 | } 678 | 679 | } else { 680 | switch (c) { 681 | case '"': 682 | // end of string; 683 | if (! (parser->expected & VKTOR_T_STRING)) { 684 | // string should not end yet! 685 | set_error_unexpected_c(error, c); 686 | return VKTOR_ERROR; 687 | } 688 | done = 1; 689 | break; 690 | 691 | case '\\': 692 | // Some escaped character 693 | parser->expected = VKTOR_C_ESCAPED; 694 | break; 695 | 696 | default: 697 | // Are we expecting a regular char? 698 | if (! (parser->expected & VKTOR_T_STRING)) { 699 | set_error_unexpected_c(error, c); 700 | return VKTOR_ERROR; 701 | } 702 | 703 | // Unicode control characters must be escaped 704 | if (c >= 0 && c <= 0x1f) { 705 | set_error_unexpected_c(error, c); 706 | return VKTOR_ERROR; 707 | } 708 | 709 | token[ptr++] = c; 710 | check_reallocate_token_memory(VKTOR_STR_MEMCHUNK); 711 | break; 712 | } 713 | } 714 | 715 | INCREMENT_BUFFER_PTR(parser); 716 | if (done) break; 717 | } 718 | 719 | if (done) break; 720 | parser_advance_buffer(parser); 721 | } 722 | 723 | parser->token_value = (void *) token; 724 | parser->token_size = ptr; 725 | 726 | // Check if we need more data 727 | if (! done) { 728 | parser->token_resume = 1; 729 | return VKTOR_MORE_DATA; 730 | } else { 731 | token[ptr] = '\0'; 732 | parser->token_resume = 0; 733 | return VKTOR_OK; 734 | } 735 | } 736 | 737 | /** 738 | * @brief Read a string token 739 | * 740 | * Read a string token using parser_read_string() and set the next expected 741 | * token map accordingly 742 | * 743 | * @param [in,out] parser Parser object 744 | * @param [out] error Error object pointer pointer or null 745 | * 746 | * @return Status code 747 | */ 748 | static vktor_status 749 | parser_read_string_token(vktor_parser *parser, vktor_error **error) 750 | { 751 | vktor_status status; 752 | 753 | if (! parser->token_resume) { 754 | parser_set_token(parser, VKTOR_T_STRING, NULL); 755 | } 756 | 757 | // Read string 758 | status = parser_read_string(parser, error); 759 | 760 | // Set next expected token 761 | if (status == VKTOR_OK) { 762 | expect_next_value_token(parser); 763 | } 764 | 765 | return status; 766 | } 767 | 768 | /** 769 | * @brief Read an object key token 770 | * 771 | * Read an object key using parser_read_string() and set the next expected 772 | * token map accordingly 773 | * 774 | * @param [in,out] parser Parser object 775 | * @param [out] error Error object pointer pointer or null 776 | * 777 | * @return Status code 778 | */ 779 | static vktor_status 780 | parser_read_objkey_token(vktor_parser *parser, vktor_error **error) 781 | { 782 | vktor_status status; 783 | 784 | assert(nest_stack_in(parser, VKTOR_STRUCT_OBJECT)); 785 | 786 | // Expecting a string 787 | parser->expected = VKTOR_T_STRING; 788 | 789 | if (! parser->token_resume) { 790 | parser_set_token(parser, VKTOR_T_OBJECT_KEY, NULL); 791 | } 792 | 793 | // Read string 794 | status = parser_read_string(parser, error); 795 | 796 | // Set next expected token 797 | if (status == VKTOR_OK) { 798 | parser->expected = VKTOR_C_COLON; 799 | } 800 | 801 | return status; 802 | } 803 | 804 | /** 805 | * @brief Read an "expected" token 806 | * 807 | * Read an "expected" token - used when we guess in advance what the next token 808 | * should be and want to try reading it. 809 | * 810 | * Used internally to read true, false, and null tokens. 811 | * 812 | * If during the parsing the input does not match the expected token, an error 813 | * is returned. 814 | * 815 | * @param [in,out] parser Parser object 816 | * @param [in] expect Expected token as string 817 | * @param [in] explen Expected token length 818 | * @param [out] error Error object pointer pointer or NULL 819 | * 820 | * @return Status code 821 | */ 822 | static vktor_status 823 | parser_read_expectedstr(vktor_parser *parser, const char *expect, int explen, 824 | vktor_error **error) 825 | { 826 | char c; 827 | 828 | assert(parser != NULL); 829 | assert(expect != NULL); 830 | 831 | // Expected string should be "null", "true" or "false" 832 | assert(explen > 3 && explen < 6); 833 | 834 | if (! parser->token_resume) { 835 | parser->token_size = 0; 836 | } 837 | 838 | for (; parser->token_size < explen; parser->token_size++) { 839 | if (parser->buffer == NULL) { 840 | parser->token_resume = 1; 841 | return VKTOR_MORE_DATA; 842 | } 843 | 844 | if (eobuffer(parser->buffer)) { 845 | parser_advance_buffer(parser); 846 | if (parser->buffer == NULL) { 847 | parser->token_resume = 1; 848 | return VKTOR_MORE_DATA; 849 | } 850 | } 851 | 852 | c = parser->buffer->text[parser->buffer->ptr]; 853 | if (expect[parser->token_size] != c) { 854 | set_error_unexpected_c(error, c); 855 | return VKTOR_ERROR; 856 | } 857 | 858 | INCREMENT_BUFFER_PTR(parser); 859 | } 860 | 861 | // if we made it here, it means we are good! 862 | parser->token_size = 0; 863 | parser->token_resume = 0; 864 | return VKTOR_OK; 865 | } 866 | 867 | /** 868 | * @brief Read an expected null token 869 | * 870 | * Read an expected null token using parser_read_expectedstr(). Will also set 871 | * the next expected token map. 872 | * 873 | * @param [in,out] parser Parser object 874 | * @param [out] error Error object pointer pointer or NULL 875 | * 876 | * @return Status code 877 | */ 878 | static vktor_status 879 | parser_read_null(vktor_parser *parser, vktor_error **error) 880 | { 881 | vktor_status st = parser_read_expectedstr(parser, "null", 4, error); 882 | 883 | if (st != VKTOR_ERROR) { 884 | parser_set_token(parser, VKTOR_T_NULL, NULL); 885 | if (st == VKTOR_OK) { 886 | // Set the next expected token 887 | expect_next_value_token(parser); 888 | } 889 | } 890 | 891 | return st; 892 | } 893 | 894 | /** 895 | * @brief Read an expected true token 896 | * 897 | * Read an expected true token using parser_read_expectedstr(). Will also set 898 | * the next expected token map. 899 | * 900 | * @param [in,out] parser Parser object 901 | * @param [out] error Error object pointer pointer or NULL 902 | * 903 | * @return Status code 904 | */ 905 | static vktor_status 906 | parser_read_true(vktor_parser *parser, vktor_error **error) 907 | { 908 | vktor_status st = parser_read_expectedstr(parser, "true", 4, error); 909 | 910 | if (st != VKTOR_ERROR) { 911 | parser_set_token(parser, VKTOR_T_TRUE, NULL); 912 | if (st == VKTOR_OK) { 913 | // Set the next expected token 914 | expect_next_value_token(parser); 915 | } 916 | } 917 | 918 | return st; 919 | } 920 | 921 | /** 922 | * @brief Read an expected false token 923 | * 924 | * Read an expected false token using parser_read_expectedstr(). Will also set 925 | * the next expected token map. 926 | * 927 | * @param [in,out] parser Parser object 928 | * @param [out] error Error object pointer pointer or NULL 929 | * 930 | * @return Status code 931 | */ 932 | static vktor_status 933 | parser_read_false(vktor_parser *parser, vktor_error **error) 934 | { 935 | vktor_status st = parser_read_expectedstr(parser, "false", 5, error); 936 | 937 | if (st != VKTOR_ERROR) { 938 | parser_set_token(parser, VKTOR_T_FALSE, NULL); 939 | if (st == VKTOR_OK) { 940 | // Set the next expected token 941 | expect_next_value_token(parser); 942 | } 943 | } 944 | 945 | return st; 946 | } 947 | 948 | /** 949 | * @brief Read a number token 950 | * 951 | * Read a number token - this might be an integer or a floating point number. 952 | * Will set the token_type accordingly. 953 | * 954 | * @param [in,out] parser Parser object 955 | * @param [out] error Error object pointer pointer 956 | * 957 | * @return Status code 958 | */ 959 | static vktor_status 960 | parser_read_number_token(vktor_parser *parser, vktor_error **error) 961 | { 962 | char c; 963 | char *token; 964 | int ptr, maxlen; 965 | int done = 0; 966 | 967 | assert(parser != NULL); 968 | 969 | if (parser->token_resume) { 970 | ptr = parser->token_size; 971 | if (ptr < VKTOR_NUM_MEMCHUNK) { 972 | maxlen = VKTOR_NUM_MEMCHUNK; 973 | token = (void *) parser->token_value; 974 | assert(token != NULL); 975 | } else { 976 | maxlen = ptr + VKTOR_NUM_MEMCHUNK; 977 | token = vrealloc(parser->token_value, sizeof(char) * maxlen); 978 | } 979 | 980 | } else { 981 | token = vmalloc(VKTOR_NUM_MEMCHUNK * sizeof(char)); 982 | maxlen = VKTOR_NUM_MEMCHUNK; 983 | ptr = 0; 984 | 985 | // Reading a new token - set possible expected characters 986 | parser->expected = VKTOR_T_INT | 987 | VKTOR_T_FLOAT | 988 | VKTOR_C_DOT | 989 | VKTOR_C_EXP | 990 | VKTOR_C_SIGNUM; 991 | 992 | // Free previous token and set token type to INT until proven otherwise 993 | parser_set_token(parser, VKTOR_T_INT, NULL); 994 | } 995 | 996 | if (token == NULL) { 997 | set_error(error, VKTOR_ERR_OUT_OF_MEMORY, 998 | "unable to allocate %d bytes for string parsing", 999 | VKTOR_NUM_MEMCHUNK); 1000 | return VKTOR_ERROR; 1001 | } 1002 | 1003 | while (parser->buffer != NULL) { 1004 | while (! eobuffer(parser->buffer)) { 1005 | c = parser->buffer->text[parser->buffer->ptr]; 1006 | 1007 | switch (c) { 1008 | case '0': 1009 | case '1': 1010 | case '2': 1011 | case '3': 1012 | case '4': 1013 | case '5': 1014 | case '6': 1015 | case '7': 1016 | case '8': 1017 | case '9': 1018 | // Digits are always allowed 1019 | token[ptr++] = c; 1020 | 1021 | // Signum cannot come after a digit 1022 | parser->expected = (parser->expected & ~VKTOR_C_SIGNUM); 1023 | break; 1024 | 1025 | case '.': 1026 | if (! (parser->expected & VKTOR_C_DOT && ptr > 0)) { 1027 | set_error_unexpected_c(error, c); 1028 | return VKTOR_ERROR; 1029 | } 1030 | 1031 | token[ptr++] = c; 1032 | 1033 | // Dots are no longer allowed 1034 | parser->expected = parser->expected & ~VKTOR_C_DOT; 1035 | 1036 | // This is a floating point number 1037 | parser->token_type = VKTOR_T_FLOAT; 1038 | break; 1039 | 1040 | case '-': 1041 | case '+': 1042 | if (! (parser->expected & VKTOR_C_SIGNUM)) { 1043 | set_error_unexpected_c(error, c); 1044 | return VKTOR_ERROR; 1045 | } 1046 | 1047 | token[ptr++] = c; 1048 | 1049 | // Signum is no longer allowed 1050 | parser->expected = parser->expected & ~VKTOR_C_SIGNUM; 1051 | break; 1052 | 1053 | case 'e': 1054 | case 'E': 1055 | if (! (parser->expected & VKTOR_C_EXP && ptr > 0)) { 1056 | set_error_unexpected_c(error, c); 1057 | return VKTOR_ERROR; 1058 | } 1059 | 1060 | // Make sure the previous sign is a number 1061 | switch(token[ptr - 1]) { 1062 | case '.': 1063 | case '+': 1064 | case '-': 1065 | set_error_unexpected_c(error, c); 1066 | return VKTOR_ERROR; 1067 | break; 1068 | } 1069 | 1070 | // Exponent is no longer allowed 1071 | parser->expected = parser->expected & ~VKTOR_C_EXP; 1072 | 1073 | // Dot is no longer allowed 1074 | parser->expected = parser->expected & ~VKTOR_C_DOT; 1075 | 1076 | // Signum is now allowed again 1077 | parser->expected = parser->expected | VKTOR_C_SIGNUM; 1078 | 1079 | // This is a floating point number 1080 | parser->token_type = VKTOR_T_FLOAT; 1081 | 1082 | token[ptr++] = 'e'; 1083 | break; 1084 | 1085 | default: 1086 | // Check that we are not expecting more digits 1087 | assert(ptr > 0); 1088 | switch(token[ptr - 1]) { 1089 | case 'e': 1090 | case 'E': 1091 | case '.': 1092 | case '+': 1093 | case '-': 1094 | set_error_unexpected_c(error, c); 1095 | return VKTOR_ERROR; 1096 | break; 1097 | } 1098 | 1099 | done = 1; 1100 | break; 1101 | } 1102 | 1103 | if (done) break; 1104 | INCREMENT_BUFFER_PTR(parser); 1105 | check_reallocate_token_memory(VKTOR_NUM_MEMCHUNK); 1106 | } 1107 | 1108 | if (done) break; 1109 | parser_advance_buffer(parser); 1110 | } 1111 | 1112 | parser->token_value = (void *) token; 1113 | parser->token_size = ptr; 1114 | 1115 | // Check if we need more data 1116 | if (! done) { 1117 | parser->token_resume = 1; 1118 | return VKTOR_MORE_DATA; 1119 | } else { 1120 | token[ptr] = '\0'; 1121 | parser->token_resume = 0; 1122 | expect_next_value_token(parser); 1123 | return VKTOR_OK; 1124 | } 1125 | } 1126 | 1127 | /** @} */ // end of internal PAI 1128 | 1129 | /** 1130 | * External API 1131 | * 1132 | * @defgroup external External API 1133 | * @{ 1134 | */ 1135 | 1136 | /** 1137 | * @brief Set memory handling function implementation 1138 | * 1139 | * Allows one to set alternative implementations of malloc, realloc and free. If 1140 | * set, the alternative implementations will be used by vktor globally to manage 1141 | * memory. Since this has global effect it is recommended to set this once before 1142 | * doing anything with vktor, and not to change this. 1143 | * 1144 | * You can pass NULL as any of the functions, in which case the standard malloc, 1145 | * realloc or free will be used. 1146 | * 1147 | * @param [in] vmalloc malloc implementation 1148 | * @param [in] vrealloc realloc implementation 1149 | * @param [in] free free implementation 1150 | */ 1151 | void 1152 | vktor_set_memory_handlers(vktor_malloc vmallocf, vktor_realloc vreallocf, 1153 | vktor_free vfreef) 1154 | { 1155 | vmalloc = (vmallocf == NULL ? malloc : vmallocf); 1156 | vrealloc = (vreallocf == NULL ? realloc : vreallocf); 1157 | vfree = (vfreef == NULL ? free : vfreef); 1158 | } 1159 | 1160 | /** 1161 | * @brief Initialize a new parser 1162 | * 1163 | * Initialize and return a new parser struct. Will return NULL if memory can't 1164 | * be allocated. 1165 | * 1166 | * @param [in] max_nest maximal nesting level 1167 | * 1168 | * @return a newly allocated parser 1169 | */ 1170 | vktor_parser* 1171 | vktor_parser_init(int max_nest) 1172 | { 1173 | vktor_parser *parser; 1174 | 1175 | if ((parser = vmalloc(sizeof(vktor_parser))) == NULL) { 1176 | return NULL; 1177 | } 1178 | 1179 | parser->buffer = NULL; 1180 | parser->last_buffer = NULL; 1181 | parser->token_type = VKTOR_T_NONE; 1182 | parser->token_value = NULL; 1183 | parser->token_resume = 0; 1184 | parser->unicode_c = 0; 1185 | 1186 | // set expectated tokens 1187 | parser->expected = VKTOR_VALUE_TOKEN; 1188 | 1189 | // set up nesting stack 1190 | parser->nest_stack = vmalloc(sizeof(vktor_struct) * max_nest); 1191 | parser->nest_ptr = 0; 1192 | parser->max_nest = max_nest; 1193 | 1194 | #ifdef BYTECOUNTER 1195 | parser->bytecounter = 0; 1196 | #endif 1197 | 1198 | return parser; 1199 | } 1200 | 1201 | /** 1202 | * @brief Feed the parser's internal buffer with more JSON data 1203 | * 1204 | * Feed the parser's internal buffer with more JSON data, to be used later when 1205 | * parsing. This function should be called before starting to parse at least 1206 | * once, and again whenever new data is available and the VKTOR_MORE_DATA 1207 | * status is returned from vktor_parse(). 1208 | * 1209 | * @param [in] parser parser object 1210 | * @param [in] text text to add to buffer 1211 | * @param [in] text_len length of text to add to buffer 1212 | * @param [in] char whether to free the buffer when done (1) or not (0) 1213 | * @param [in,out] err pointer to an unallocated error struct to return any 1214 | * errors, or NULL if there is no need for error handling 1215 | * 1216 | * @return vktor status code 1217 | * - VKTOR_OK on success 1218 | * - VKTOR_ERROR otherwise 1219 | */ 1220 | vktor_status 1221 | vktor_feed(vktor_parser *parser, char *text, long text_len, 1222 | char free, vktor_error **err) 1223 | { 1224 | vktor_buffer *buffer; 1225 | 1226 | // Create buffer 1227 | if ((buffer = buffer_init(text, text_len, free)) == NULL) { 1228 | set_error(err, VKTOR_ERR_OUT_OF_MEMORY, 1229 | "Unable to allocate memory buffer for %ld bytes", text_len); 1230 | return VKTOR_ERROR; 1231 | } 1232 | 1233 | // Link buffer to end of parser buffer chain 1234 | if (parser->last_buffer == NULL) { 1235 | assert(parser->buffer == NULL); 1236 | parser->buffer = buffer; 1237 | parser->last_buffer = buffer; 1238 | } else { 1239 | parser->last_buffer->next_buff = buffer; 1240 | parser->last_buffer = buffer; 1241 | } 1242 | 1243 | return VKTOR_OK; 1244 | } 1245 | 1246 | /** 1247 | * @brief Parse some JSON text and return on the next token 1248 | * 1249 | * Parse the text buffer until the next JSON token is encountered 1250 | * 1251 | * In case of error, if error is not NULL, it will be populated with error 1252 | * information, and VKTOR_ERROR will be returned 1253 | * 1254 | * @param [in,out] parser The parser object to work with 1255 | * @param [out] error A vktor_error pointer pointer, or NULL 1256 | * 1257 | * @return status code: 1258 | * - VKTOR_OK if a token was encountered 1259 | * - VKTOR_ERROR if an error has occured 1260 | * - VKTOR_MORE_DATA if we need more data in order to continue parsing 1261 | * - VKTOR_COMPLETE if parsing is complete and no further data is expected 1262 | */ 1263 | vktor_status 1264 | vktor_parse(vktor_parser *parser, vktor_error **error) 1265 | { 1266 | char c; 1267 | int done; 1268 | 1269 | assert(parser != NULL); 1270 | 1271 | // Do we have a buffer to work with? 1272 | while (parser->buffer != NULL) { 1273 | done = 0; 1274 | 1275 | // Do we need to continue reading the previous token? 1276 | if (parser->token_resume) { 1277 | 1278 | switch (parser->token_type) { 1279 | case VKTOR_T_OBJECT_KEY: 1280 | return parser_read_objkey_token(parser, error); 1281 | break; 1282 | 1283 | case VKTOR_T_STRING: 1284 | return parser_read_string_token(parser, error); 1285 | break; 1286 | 1287 | case VKTOR_T_NULL: 1288 | return parser_read_null(parser, error); 1289 | break; 1290 | 1291 | case VKTOR_T_TRUE: 1292 | return parser_read_true(parser, error); 1293 | break; 1294 | 1295 | case VKTOR_T_FALSE: 1296 | return parser_read_false(parser, error); 1297 | break; 1298 | 1299 | case VKTOR_T_INT: 1300 | case VKTOR_T_FLOAT: 1301 | return parser_read_number_token(parser, error); 1302 | break; 1303 | 1304 | default: 1305 | set_error(error, VKTOR_ERR_INTERNAL_ERR, 1306 | "token resume flag is set but token type %d is unexpected", 1307 | parser->token_type); 1308 | return VKTOR_ERROR; 1309 | break; 1310 | } 1311 | } 1312 | 1313 | while (! eobuffer(parser->buffer)) { 1314 | c = parser->buffer->text[parser->buffer->ptr]; 1315 | 1316 | switch (c) { 1317 | case '{': 1318 | if (! parser->expected & VKTOR_T_OBJECT_START) { 1319 | set_error_unexpected_c(error, c); 1320 | return VKTOR_ERROR; 1321 | } 1322 | 1323 | if (nest_stack_add(parser, VKTOR_STRUCT_OBJECT, error) == VKTOR_ERROR) { 1324 | return VKTOR_ERROR; 1325 | } 1326 | 1327 | parser_set_token(parser, VKTOR_T_OBJECT_START, NULL); 1328 | 1329 | // Expecting: object key or object end 1330 | parser->expected = VKTOR_T_OBJECT_KEY | 1331 | VKTOR_T_OBJECT_END; 1332 | 1333 | done = 1; 1334 | break; 1335 | 1336 | case '[': 1337 | if (! parser->expected & VKTOR_T_ARRAY_START) { 1338 | set_error_unexpected_c(error, c); 1339 | return VKTOR_ERROR; 1340 | } 1341 | 1342 | if (nest_stack_add(parser, VKTOR_STRUCT_ARRAY, error) == VKTOR_ERROR) { 1343 | return VKTOR_ERROR; 1344 | } 1345 | 1346 | parser_set_token(parser, VKTOR_T_ARRAY_START, NULL); 1347 | 1348 | // Expecting: any value or array end 1349 | parser->expected = VKTOR_VALUE_TOKEN | 1350 | VKTOR_T_ARRAY_END; 1351 | 1352 | done = 1; 1353 | break; 1354 | 1355 | case '"': 1356 | if (! (parser->expected & (VKTOR_T_STRING | 1357 | VKTOR_T_OBJECT_KEY))) { 1358 | set_error_unexpected_c(error, c); 1359 | return VKTOR_ERROR; 1360 | } 1361 | 1362 | INCREMENT_BUFFER_PTR(parser); 1363 | 1364 | if (parser->expected & VKTOR_T_OBJECT_KEY) { 1365 | return parser_read_objkey_token(parser, error); 1366 | } else { 1367 | return parser_read_string_token(parser, error); 1368 | } 1369 | 1370 | break; 1371 | 1372 | case ',': 1373 | if (! (parser->expected & VKTOR_C_COMMA)) { 1374 | set_error_unexpected_c(error, c); 1375 | return VKTOR_ERROR; 1376 | } 1377 | 1378 | switch(parser->nest_stack[parser->nest_ptr]) { 1379 | case VKTOR_STRUCT_OBJECT: 1380 | parser->expected = VKTOR_T_OBJECT_KEY; 1381 | break; 1382 | 1383 | case VKTOR_STRUCT_ARRAY: 1384 | parser->expected = VKTOR_VALUE_TOKEN; 1385 | break; 1386 | 1387 | default: 1388 | set_error(error, VKTOR_ERR_INTERNAL_ERR, 1389 | "internal parser error: unexpected nesting stack member"); 1390 | return VKTOR_ERROR; 1391 | break; 1392 | } 1393 | break; 1394 | 1395 | case ':': 1396 | if (! (parser->expected & VKTOR_C_COLON)) { 1397 | set_error_unexpected_c(error, c); 1398 | return VKTOR_ERROR; 1399 | } 1400 | 1401 | // Colon is only expected inside objects 1402 | assert(nest_stack_in(parser, VKTOR_STRUCT_OBJECT)); 1403 | 1404 | // Next we expected a value 1405 | parser->expected = VKTOR_VALUE_TOKEN; 1406 | break; 1407 | 1408 | case '}': 1409 | if (! (parser->expected & VKTOR_T_OBJECT_END && 1410 | nest_stack_in(parser, VKTOR_STRUCT_OBJECT))) { 1411 | 1412 | set_error_unexpected_c(error, c); 1413 | return VKTOR_ERROR; 1414 | } 1415 | 1416 | parser_set_token(parser, VKTOR_T_OBJECT_END, NULL); 1417 | 1418 | if (nest_stack_pop(parser, error) == VKTOR_ERROR) { 1419 | return VKTOR_ERROR; 1420 | } 1421 | 1422 | if (parser->nest_ptr > 0) { 1423 | // Next can be either a comma, or end of array / object 1424 | parser->expected = VKTOR_C_COMMA | 1425 | VKTOR_T_OBJECT_END | 1426 | VKTOR_T_ARRAY_END; 1427 | } else { 1428 | // Next can be nothing 1429 | parser->expected = VKTOR_T_NONE; 1430 | } 1431 | 1432 | done = 1; 1433 | break; 1434 | 1435 | case ']': 1436 | if (! (parser->expected & VKTOR_T_ARRAY_END && 1437 | nest_stack_in(parser, VKTOR_STRUCT_ARRAY))) { 1438 | 1439 | set_error_unexpected_c(error, c); 1440 | return VKTOR_ERROR; 1441 | } 1442 | parser_set_token(parser, VKTOR_T_ARRAY_END, NULL); 1443 | 1444 | if (nest_stack_pop(parser, error) == VKTOR_ERROR) { 1445 | return VKTOR_ERROR; 1446 | } 1447 | 1448 | if (parser->nest_ptr > 0) { 1449 | // Next can be either a comma, or end of array / object 1450 | parser->expected = VKTOR_C_COMMA | 1451 | VKTOR_T_OBJECT_END | 1452 | VKTOR_T_ARRAY_END; 1453 | } else { 1454 | // Next can be nothing 1455 | parser->expected = VKTOR_T_NONE; 1456 | } 1457 | 1458 | done = 1; 1459 | break; 1460 | 1461 | case ' ': 1462 | case '\n': 1463 | case '\r': 1464 | case '\t': 1465 | case '\f': 1466 | case '\v': 1467 | // Whitespace - do nothing! 1468 | /** 1469 | * @todo consinder: read all whitespace without looping? 1470 | */ 1471 | break; 1472 | 1473 | case 't': 1474 | // true? 1475 | if (! (parser->expected & VKTOR_T_TRUE)) { 1476 | set_error_unexpected_c(error, c); 1477 | return VKTOR_ERROR; 1478 | } 1479 | 1480 | return parser_read_true(parser, error); 1481 | break; 1482 | 1483 | case 'f': 1484 | // false? 1485 | if (! (parser->expected & VKTOR_T_FALSE)) { 1486 | set_error_unexpected_c(error, c); 1487 | return VKTOR_ERROR; 1488 | } 1489 | 1490 | return parser_read_false(parser, error); 1491 | break; 1492 | 1493 | case 'n': 1494 | // null? 1495 | if (! (parser->expected & VKTOR_T_NULL)) { 1496 | set_error_unexpected_c(error, c); 1497 | return VKTOR_ERROR; 1498 | } 1499 | 1500 | return parser_read_null(parser, error); 1501 | break; 1502 | 1503 | case '0': 1504 | case '1': 1505 | case '2': 1506 | case '3': 1507 | case '4': 1508 | case '5': 1509 | case '6': 1510 | case '7': 1511 | case '8': 1512 | case '9': 1513 | case '-': 1514 | case '+': 1515 | // Read a number 1516 | if (! (parser->expected & (VKTOR_T_INT | 1517 | VKTOR_T_FLOAT))) { 1518 | set_error_unexpected_c(error, c); 1519 | return VKTOR_ERROR; 1520 | } 1521 | 1522 | return parser_read_number_token(parser, error); 1523 | break; 1524 | 1525 | default: 1526 | // Unexpected character 1527 | set_error_unexpected_c(error, c); 1528 | return VKTOR_ERROR; 1529 | break; 1530 | } 1531 | 1532 | INCREMENT_BUFFER_PTR(parser); 1533 | if (done) break; 1534 | } 1535 | 1536 | if (done) break; 1537 | parser_advance_buffer(parser); 1538 | } 1539 | 1540 | assert(parser->nest_ptr >= 0); 1541 | 1542 | if (parser->buffer != NULL) { 1543 | return VKTOR_OK; 1544 | } else { 1545 | if (parser->nest_ptr == 0 && parser->token_type != VKTOR_T_NONE) { 1546 | return VKTOR_COMPLETE; 1547 | } else { 1548 | return VKTOR_MORE_DATA; 1549 | } 1550 | } 1551 | } 1552 | 1553 | /** 1554 | * @brief Get the current nesting depth 1555 | * 1556 | * Get the current array/object nesting depth of the current token the parser 1557 | * is pointing to 1558 | * 1559 | * @param [in] parser Parser object 1560 | * 1561 | * @return nesting level - 0 means top level 1562 | */ 1563 | int 1564 | vktor_get_depth(vktor_parser *parser) 1565 | { 1566 | return parser->nest_ptr; 1567 | } 1568 | 1569 | /** 1570 | * @brief Get the current struct type 1571 | * 1572 | * Get the struct type (object, array or none) containing the current token 1573 | * pointed to by the parser 1574 | * 1575 | * @param [in] parser Parser object 1576 | * 1577 | * @return A vktor_struct value or VKTOR_STRUCT_NONE if we are in the top 1578 | * level 1579 | */ 1580 | vktor_struct 1581 | vktor_get_current_struct(vktor_parser *parser) 1582 | { 1583 | assert(parser != NULL); 1584 | return parser->nest_stack[parser->nest_ptr]; 1585 | } 1586 | 1587 | /** 1588 | * @brief Get the current token type 1589 | * 1590 | * Get the type of the current token pointed to by the parser 1591 | * 1592 | * @param [in] parser Parser object 1593 | * 1594 | * @return Token type (one of the VKTOR_T_* tokens) 1595 | */ 1596 | vktor_token 1597 | vktor_get_token_type(vktor_parser *parser) 1598 | { 1599 | assert(parser != NULL); 1600 | return parser->token_type; 1601 | } 1602 | 1603 | /** 1604 | * @brief Get the token value as a long integer 1605 | * 1606 | * Get the value of the current token as a long integer. Suitable for reading 1607 | * the value of VKTOR_T_INT tokens, but can also be used to get the integer 1608 | * value of VKTOR_T_FLOAT tokens and even any numeric prefix of a VKTOR_T_STRING 1609 | * token. 1610 | * 1611 | * If the value of a number token is larger than the system's maximal long, 1612 | * 0 is returned and error will indicate overflow. In such cases, 1613 | * vktor_get_value_string() should be used to get the value as a string. 1614 | * 1615 | * @param [in] parser Parser object 1616 | * @param [out] error Error object pointer pointer or null 1617 | * 1618 | * @return The numeric value of the current token as a long int, 1619 | * @retval 0 in case of error (although 0 might also be normal, so check the 1620 | * value of error) 1621 | */ 1622 | long 1623 | vktor_get_value_long(vktor_parser *parser, vktor_error **error) 1624 | { 1625 | long val; 1626 | 1627 | assert(parser != NULL); 1628 | 1629 | if (parser->token_value == NULL) { 1630 | set_error(error, VKTOR_ERR_NO_VALUE, "token value is unknown"); 1631 | return 0; 1632 | } 1633 | 1634 | errno = 0; 1635 | val = strtol((char *) parser->token_value, NULL, 10); 1636 | if (errno == ERANGE) { 1637 | set_error(error, VKTOR_ERR_OUT_OF_RANGE, 1638 | "integer value overflows maximal long value"); 1639 | return 0; 1640 | } 1641 | 1642 | return val; 1643 | } 1644 | 1645 | /** 1646 | * @brief Get the token value as a double 1647 | * 1648 | * Get the value of the current token as a double precision floating point 1649 | * number. Suitable for reading the value of VKTOR_T_FLOAT tokens. 1650 | * 1651 | * If the value of a number token is larger than the system's HUGE_VAL 0 is 1652 | * returned and error will indicate overflow. In such cases, 1653 | * vktor_get_value_string() should be used to get the value as a string. 1654 | * 1655 | * @param [in] parser Parser object 1656 | * @param [out] error Error object pointer pointer or null 1657 | * 1658 | * @return The numeric value of the current token as a double 1659 | * @retval 0 in case of error (although 0 might also be normal, so check the 1660 | * value of error) 1661 | */ 1662 | double 1663 | vktor_get_value_double(vktor_parser *parser, vktor_error **error) 1664 | { 1665 | double val; 1666 | 1667 | assert(parser != NULL); 1668 | 1669 | if (parser->token_value == NULL) { 1670 | set_error(error, VKTOR_ERR_NO_VALUE, "token value is unknown"); 1671 | return 0; 1672 | } 1673 | 1674 | errno = 0; 1675 | val = strtod((char *) parser->token_value, NULL); 1676 | if (errno == ERANGE) { 1677 | set_error(error, VKTOR_ERR_OUT_OF_RANGE, 1678 | "number value overflows maximal double value"); 1679 | return 0; 1680 | } 1681 | 1682 | return val; 1683 | } 1684 | 1685 | /** 1686 | * @brief Get the value of the token as a string 1687 | * 1688 | * Get the value of the current token as a string, as well as the length of the 1689 | * token. Suitable for getting the value of a VKTOR_T_STRING token, but also 1690 | * for reading numeric values as a string. 1691 | * 1692 | * Note that the string pointer populated into val is owned by the parser and 1693 | * should not be freed by the user. 1694 | * 1695 | * @param [in] parser Parser object 1696 | * @param [out] val Pointer-pointer to be populated with the value 1697 | * @param [out] error Error object pointer pointer or NULL 1698 | * 1699 | * @return The length of the string 1700 | * @retval 0 in case of error (although 0 might also be normal, so check the 1701 | * value of error) 1702 | */ 1703 | int 1704 | vktor_get_value_str(vktor_parser *parser, char **val, vktor_error **error) 1705 | { 1706 | assert(parser != NULL); 1707 | 1708 | if (parser->token_value == NULL) { 1709 | set_error(error, VKTOR_ERR_NO_VALUE, "token value is unknown"); 1710 | return -1; 1711 | } 1712 | 1713 | *val = (char *) parser->token_value; 1714 | return parser->token_size; 1715 | } 1716 | 1717 | /** 1718 | * @brief Get the value of the token as a string 1719 | * 1720 | * Similar to vktor_get_value_str(), only this function will provide a copy of 1721 | * the string, which will need to be freed by the user when it is no longer 1722 | * used. 1723 | * 1724 | * @param [in] parser Parser object 1725 | * @param [out] val Pointer-pointer to be populated with the value 1726 | * @param [out] error Error object pointer pointer or NULL 1727 | * 1728 | * @return The length of the string 1729 | * @retval 0 in case of error (although 0 might also be normal, so check the 1730 | * value of error) 1731 | */ 1732 | int 1733 | vktor_get_value_str_copy(vktor_parser *parser, char **val, vktor_error **error) 1734 | { 1735 | char *str; 1736 | 1737 | assert(parser != NULL); 1738 | 1739 | if (parser->token_value == NULL) { 1740 | set_error(error, VKTOR_ERR_NO_VALUE, "token value is unknown"); 1741 | return 0; 1742 | } 1743 | 1744 | str = vmalloc(sizeof(char) * (parser->token_size + 1)); 1745 | str = memcpy(str, parser->token_value, parser->token_size); 1746 | str[parser->token_size] = '\0'; 1747 | 1748 | *val = str; 1749 | return parser->token_size; 1750 | } 1751 | 1752 | /** 1753 | * @brief Free a parser and any associated memory 1754 | * 1755 | * Free a parser and any associated memory structures, including any linked 1756 | * buffers 1757 | * 1758 | * @param [in,out] parser parser struct to free 1759 | */ 1760 | void 1761 | vktor_parser_free(vktor_parser *parser) 1762 | { 1763 | assert(parser != NULL); 1764 | 1765 | if (parser->buffer != NULL) { 1766 | buffer_free_all(parser->buffer); 1767 | } 1768 | 1769 | if (parser->token_value != NULL) { 1770 | vfree(parser->token_value); 1771 | } 1772 | 1773 | vfree(parser->nest_stack); 1774 | 1775 | vfree(parser); 1776 | } 1777 | 1778 | /** 1779 | * @brief Free an error struct 1780 | * 1781 | * Free an error struct 1782 | * 1783 | * @param [in,out] err Error struct to free 1784 | */ 1785 | void 1786 | vktor_error_free(vktor_error *err) 1787 | { 1788 | if (err->message != NULL) { 1789 | vfree(err->message); 1790 | } 1791 | 1792 | vfree(err); 1793 | } 1794 | 1795 | /** @} */ // end of external API 1796 | -------------------------------------------------------------------------------- /libvktor/vktor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vktor JSON pull-parser library 3 | * 4 | * Copyright (c) 2009 Shahar Evron 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, 10 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the 12 | * Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | /** 29 | * @file vktor.h 30 | * 31 | * vktor header file - defines the external API and types available to vktor 32 | * users. 33 | */ 34 | 35 | #ifndef _VKTOR_H 36 | 37 | /** 38 | * Parser struct - this is the main object used by the user to parse a JSON 39 | * stream. This opaque structure is defined internally in vktor.c. 40 | */ 41 | typedef struct _vktor_parser_struct vktor_parser; 42 | 43 | /* type definitions */ 44 | 45 | /** 46 | * @defgroup external External API 47 | * @{ 48 | */ 49 | 50 | /** 51 | * @enum vktor_token 52 | * 53 | * JSON token types 54 | * 55 | * Whenever a token is encountered during the parsing process, calling 56 | * vktor_get_token_type() will return one of these values to indicate the token 57 | * type. 58 | */ 59 | typedef enum { 60 | VKTOR_T_NONE = 0, /**< No token */ 61 | VKTOR_T_NULL = 1, /**< null */ 62 | VKTOR_T_FALSE = 1 << 1, /**< boolean false */ 63 | VKTOR_T_TRUE = 1 << 2, /**< boolean true */ 64 | VKTOR_T_INT = 1 << 3, /**< an integer value */ 65 | VKTOR_T_FLOAT = 1 << 4, /**< a floating-point value */ 66 | VKTOR_T_STRING = 1 << 5, /**< a string value */ 67 | VKTOR_T_ARRAY_START = 1 << 6, /**< array beginning */ 68 | VKTOR_T_ARRAY_END = 1 << 7, /**< array end */ 69 | VKTOR_T_OBJECT_START = 1 << 8, /**< object beginning */ 70 | VKTOR_T_OBJECT_KEY = 1 << 9, /**< an object pair key */ 71 | VKTOR_T_OBJECT_END = 1 << 10, /**< object end */ 72 | } vktor_token; 73 | 74 | /** 75 | * @enum vktor_struct 76 | * 77 | * Possible JSON struct types (array or object) 78 | */ 79 | typedef enum { 80 | VKTOR_STRUCT_NONE, /**< No struct */ 81 | VKTOR_STRUCT_ARRAY, /**< Array */ 82 | VKTOR_STRUCT_OBJECT /**< Object (AKA map, associative array) */ 83 | } vktor_struct; 84 | 85 | /** 86 | * @enum vktor_status 87 | * 88 | * Possible vktor parser status codes 89 | */ 90 | typedef enum { 91 | VKTOR_ERROR, /**< An error has occured */ 92 | VKTOR_OK, /**< Everything is OK */ 93 | VKTOR_MORE_DATA, /**< More data is required in order to continue parsing */ 94 | VKTOR_COMPLETE /**< Parsing is complete, no further data is expected */ 95 | } vktor_status; 96 | 97 | /** 98 | * @enum vktor_errcode 99 | * 100 | * Possible error codes used in vktor_error->code 101 | */ 102 | typedef enum { 103 | VKTOR_ERR_NONE, /**< no error */ 104 | VKTOR_ERR_OUT_OF_MEMORY, /**< can't allocate memory */ 105 | VKTOR_ERR_UNEXPECTED_INPUT, /**< unexpected characters in input buffer */ 106 | VKTOR_ERR_INCOMPLETE_DATA, /**< can't finish parsing without more data */ 107 | VKTOR_ERR_NO_VALUE, /**< trying to read non-existing value */ 108 | VKTOR_ERR_OUT_OF_RANGE, /**< long or double value is out of range */ 109 | VKTOR_ERR_MAX_NEST, /**< maximal nesting level reached */ 110 | VKTOR_ERR_INTERNAL_ERR /**< internal parser error */ 111 | } vktor_errcode; 112 | 113 | /** 114 | * Memory allocation and management function pointers 115 | */ 116 | 117 | /** 118 | * malloc implementation pointer 119 | */ 120 | typedef void *(*vktor_malloc) (size_t size); 121 | 122 | /** 123 | * realloc implementation pointer 124 | */ 125 | typedef void *(*vktor_realloc) (void *pointer, size_t size); 126 | 127 | /** 128 | * free implementation pointer 129 | */ 130 | typedef void (*vktor_free) (void *pointer); 131 | 132 | /** 133 | * Error structure, signifying the error code and error message 134 | * 135 | * Error structs must be freed using vktor_error_free() 136 | */ 137 | typedef struct _vktor_error_struct { 138 | vktor_errcode code; /**< error code */ 139 | char *message; /**< error message */ 140 | } vktor_error; 141 | 142 | /* function prototypes */ 143 | 144 | /** 145 | * @brief Initialize a new parser 146 | * 147 | * Initialize and return a new parser struct. Will return NULL if memory can't 148 | * be allocated. 149 | * 150 | * @param [in] max_nest maximal nesting level 151 | * 152 | * @return a newly allocated parser 153 | */ 154 | vktor_parser* vktor_parser_init(int max_nest); 155 | 156 | /** 157 | * @brief Free a parser and any associated memory 158 | * 159 | * Free a parser and any associated memory structures, including any linked 160 | * buffers 161 | * 162 | * @param [in,out] parser parser struct to free 163 | */ 164 | void vktor_parser_free(vktor_parser *parser); 165 | 166 | /** 167 | * @brief Free an error struct 168 | * 169 | * Free an error struct 170 | * 171 | * @param [in,out] err Error struct to free 172 | */ 173 | void vktor_error_free(vktor_error *err); 174 | 175 | /** 176 | * @brief Feed the parser's internal buffer with more JSON data 177 | * 178 | * Feed the parser's internal buffer with more JSON data, to be used later when 179 | * parsing. This function should be called before starting to parse at least 180 | * once, and again whenever new data is available and the VKTOR_MORE_DATA 181 | * status is returned from vktor_parse(). 182 | * 183 | * @param [in] parser parser object 184 | * @param [in] text text to add to buffer 185 | * @param [in] text_len length of text to add to buffer 186 | * @param [in] free whether to free the buffer when done (1) or not (0) 187 | * @param [in,out] err pointer to an unallocated error struct to return any 188 | * errors, or NULL if there is no need for error handling 189 | * 190 | * @return vktor status code 191 | * - VKTOR_OK on success 192 | * - VKTOR_ERROR otherwise 193 | */ 194 | vktor_status vktor_feed(vktor_parser *parser, char *text, long text_len, 195 | char free, vktor_error **err); 196 | 197 | /** 198 | * @brief Parse some JSON text and return on the next token 199 | * 200 | * Parse the text buffer until the next JSON token is encountered 201 | * 202 | * In case of error, if error is not NULL, it will be populated with error 203 | * information, and VKTOR_ERROR will be returned 204 | * 205 | * @param [in,out] parser The parser object to work with 206 | * @param [out] error A vktor_error pointer pointer, or NULL 207 | * 208 | * @return Status code: 209 | * - VKTOR_OK if a token was encountered 210 | * - VKTOR_ERROR if an error has occured 211 | * - VKTOR_MORE_DATA if we need more data in order to continue parsing 212 | * - VKTOR_COMPLETE if parsing is complete and no further data is expected 213 | */ 214 | vktor_status vktor_parse(vktor_parser *parser, vktor_error **error); 215 | 216 | /** 217 | * @brief Get the current token type 218 | * 219 | * Get the type of the current token pointed to by the parser 220 | * 221 | * @param [in] parser Parser object 222 | * 223 | * @return Token type (one of the VKTOR_T_* tokens) 224 | */ 225 | vktor_token vktor_get_token_type(vktor_parser *parser); 226 | 227 | /** 228 | * @brief Get the current nesting depth 229 | * 230 | * Get the current array/object nesting depth of the current token the parser 231 | * is pointing to 232 | * 233 | * @param [in] parser Parser object 234 | * 235 | * @return nesting level - 0 means top level 236 | */ 237 | int vktor_get_depth(vktor_parser *parser); 238 | 239 | /** 240 | * @brief Get the current struct type 241 | * 242 | * Get the struct type (object, array or none) containing the current token 243 | * pointed to by the parser 244 | * 245 | * @param [in] parser Parser object 246 | * 247 | * @return A vktor_struct value or VKTOR_STRUCT_NONE if we are in the top 248 | * level 249 | */ 250 | vktor_struct vktor_get_current_struct(vktor_parser *parser); 251 | 252 | /** 253 | * @brief Get the token value as a long integer 254 | * 255 | * Get the value of the current token as a long integer. Suitable for reading 256 | * the value of VKTOR_T_INT tokens, but can also be used to get the integer 257 | * value of VKTOR_T_FLOAT tokens and even any numeric prefix of a VKTOR_T_STRING 258 | * token. 259 | * 260 | * If the value of a number token is larger than the system's maximal long, 261 | * 0 is returned and error will indicate overflow. In such cases, 262 | * vktor_get_value_string() should be used to get the value as a string. 263 | * 264 | * @param [in] parser Parser object 265 | * @param [out] error Error object pointer pointer or null 266 | * 267 | * @return The numeric value of the current token as a long int, 268 | * @retval 0 in case of error (although 0 might also be normal, so check the 269 | * value of error) 270 | */ 271 | long vktor_get_value_long(vktor_parser *parser, vktor_error **error); 272 | 273 | /** 274 | * @brief Get the token value as a double 275 | * 276 | * Get the value of the current token as a double precision floating point 277 | * number. Suitable for reading the value of VKTOR_T_FLOAT tokens. 278 | * 279 | * If the value of a number token is larger than the system's HUGE_VAL 0 is 280 | * returned and error will indicate overflow. In such cases, 281 | * vktor_get_value_string() should be used to get the value as a string. 282 | * 283 | * @param [in] parser Parser object 284 | * @param [out] error Error object pointer pointer or null 285 | * 286 | * @return The numeric value of the current token as a double 287 | * @retval 0 in case of error (although 0 might also be normal, so check the 288 | * value of error) 289 | */ 290 | double vktor_get_value_double(vktor_parser *parser, vktor_error **error); 291 | 292 | /** 293 | * @brief Get the value of the token as a string 294 | * 295 | * Get the value of the current token as a string, as well as the length of the 296 | * token. Suitable for getting the value of a VKTOR_T_STRING token, but also 297 | * for reading numeric values as a string. 298 | * 299 | * Note that the string pointer populated into val is owned by the parser and 300 | * should not be freed by the user. 301 | * 302 | * @param [in] parser Parser object 303 | * @param [out] val Pointer-pointer to be populated with the value 304 | * @param [out] error Error object pointer pointer or NULL 305 | * 306 | * @return The length of the string 307 | * @retval 0 in case of error (although 0 might also be normal, so check the 308 | * value of error) 309 | */ 310 | int vktor_get_value_str(vktor_parser *parser, char **val, vktor_error **error); 311 | 312 | /** 313 | * @brief Get the value of the token as a string 314 | * 315 | * Similar to vktor_get_value_str(), only this function will provide a copy of 316 | * the string, which will need to be freed by the user when it is no longer 317 | * used. 318 | * 319 | * @param [in] parser Parser object 320 | * @param [out] val Pointer-pointer to be populated with the value 321 | * @param [out] error Error object pointer pointer or NULL 322 | * 323 | * @return The length of the string 324 | * @retval 0 in case of error (although 0 might also be normal, so check the 325 | * value of error) 326 | */ 327 | int vktor_get_value_str_copy(vktor_parser *parser, char **val, vktor_error **error); 328 | 329 | /** 330 | * @brief Set memory handling function implementation 331 | * 332 | * Allows one to set alternative implementations of malloc, realloc and free. If 333 | * set, the alternative implementations will be used by vktor globally to manage 334 | * memory. Since this has global effect it is recommended to set this once before 335 | * doing anything with vktor, and not to change this. 336 | * 337 | * You can pass NULL as any of the functions, in which case the standard malloc, 338 | * realloc or free will be used. 339 | * 340 | * @param [in] vmalloc malloc implementation 341 | * @param [in] vrealloc realloc implementation 342 | * @param [in] free free implementation 343 | */ 344 | void vktor_set_memory_handlers(vktor_malloc vmallocf, vktor_realloc vreallocf, 345 | vktor_free vfreef); 346 | 347 | /** @} */ // end of external API 348 | 349 | #define _VKTOR_H 350 | #endif /* VKTOR_H */ 351 | -------------------------------------------------------------------------------- /libvktor/vktor_unicode.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vktor JSON pull-parser library 3 | * 4 | * Copyright (c) 2009 Shahar Evron 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, 10 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the 12 | * Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | #ifdef HAVE_CONFIG_H 29 | #include "config.h" 30 | #endif 31 | 32 | #include 33 | #include 34 | 35 | #include "vktor_unicode.h" 36 | 37 | #define SURROGATE_OFFSET (0x10000 - (0xd800 << 10) - 0xdc00) 38 | 39 | /** 40 | * @brief Convert a hexadecimal digit to it's integer value 41 | * 42 | * Convert a single char containing a hexadecimal digit to it's integer value 43 | * (0 - 15). Used when converting escaped Unicode sequences to UTF-* characters. 44 | * 45 | * Note that this function does not do any error checking because it is for 46 | * internal use only. If the character is not a valid hex digit, 0 is returned. 47 | * Obviously since 0 is a valid value it should not be used for error checking. 48 | * 49 | * @param [in] hex Hexadecimal character 50 | * 51 | * @return Integer value (0 - 15). 52 | */ 53 | unsigned char 54 | vktor_unicode_hex_to_int(unsigned char hex) 55 | { 56 | unsigned char i = 0; 57 | 58 | assert((hex >= '0' && hex <= '9') || 59 | (hex >= 'a' && hex <= 'f') || 60 | (hex >= 'A' && hex <= 'F')); 61 | 62 | if (hex >= '0' && hex <= '9') { 63 | i = hex - 48; 64 | } else if (hex >= 'A' && hex <= 'F') { 65 | i = hex - 55; 66 | } else if (hex >= 'a' && hex <= 'f') { 67 | i = hex - 87; 68 | } 69 | 70 | return i; 71 | } 72 | 73 | /** 74 | * @brief Encode a Unicode code point to a UTF-8 string 75 | * 76 | * Encode a 16 bit number representing a Unicode code point (UCS-2) to 77 | * a UTF-8 encoded string. Note that this function does not handle surrogate 78 | * pairs - you should use vktor_uncode_sp_to_utf8() in this case. 79 | * 80 | * @param [in] cp the unicode codepoint 81 | * @param [out] utf8 a pointer to a 5 byte long string (at least) that will 82 | * be populated with the UTF-8 encoded string 83 | * 84 | * @return the length of the UTF-8 string (1 - 3 bytes) or 0 in case of error 85 | */ 86 | short 87 | vktor_unicode_cp_to_utf8(unsigned short cp, unsigned char *utf8) 88 | { 89 | short len = 0; 90 | 91 | assert(sizeof(utf8) >= 4); 92 | assert(! VKTOR_UNICODE_HIGH_SURROGATE(cp)); 93 | 94 | if (cp <= 0x7f) { 95 | // 1 byte UTF-8, equivalent to ASCII 96 | utf8[0] = (unsigned char) cp; 97 | len = 1; 98 | 99 | } else if (cp <= 0x7ff) { 100 | // 2 byte UTF-8 101 | utf8[0] = 0xc0 | (cp >> 6); 102 | utf8[1] = 0x80 | (cp & 0x3f); 103 | len = 2; 104 | } else if (VKTOR_UNICODE_LOW_SURROGATE(cp)) { 105 | // Error - an unpaired low surrogate character 106 | return 0; 107 | 108 | } else { 109 | // 3 byte UTF-8 110 | utf8[0] = (unsigned char) 0xe0 | (cp >> 12); 111 | utf8[1] = (unsigned char) 0x80 | ((cp >> 6) & 0x3f); 112 | utf8[2] = (unsigned char) 0x80 | (cp & 0x3f); 113 | len = 3; 114 | } 115 | 116 | utf8[len] = '\0'; 117 | 118 | return len; 119 | } 120 | 121 | /** 122 | * @brief Convert a UTF-16 surrogate pair to a UTF-8 character 123 | * 124 | * Converts a UTF-16 surrogate pair (two 16 bit characters) into a single 4-byte 125 | * UTF-8 character. This function should be called only after checking that 126 | * you have a valid surrogate pair. 127 | * 128 | * @param [in] high High surrogate 129 | * @param [in] low Low surrogate 130 | * @param [out] utf8 Resulting UTF-8 character 131 | * 132 | * @return Byte-length of resulting character - should be 4, or 0 if there's an 133 | * error. 134 | */ 135 | short 136 | vktor_unicode_sp_to_utf8(unsigned short high, unsigned short low, unsigned char *utf8) 137 | { 138 | unsigned long utf32; 139 | 140 | assert(sizeof(utf8) >= 4); 141 | assert(VKTOR_UNICODE_HIGH_SURROGATE(high)); 142 | assert(VKTOR_UNICODE_LOW_SURROGATE(low)); 143 | 144 | // First, convert the UTF-16 surrogate pair into a single UTF32 character 145 | utf32 = (high << 10) + low + SURROGATE_OFFSET; 146 | 147 | // Now write the UTF-8 encoded character 148 | utf8[0] = (unsigned char) 0xf0 | (utf32 >> 18); 149 | utf8[1] = (unsigned char) 0x80 | ((utf32 >> 12) & 0x3f); 150 | utf8[2] = (unsigned char) 0x80 | ((utf32 >> 6) & 0x3f); 151 | utf8[3] = (unsigned char) 0x80 | (utf32 & 0x3f); 152 | utf8[4] = '\0'; 153 | 154 | return 4; 155 | } 156 | -------------------------------------------------------------------------------- /libvktor/vktor_unicode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vktor JSON pull-parser library 3 | * 4 | * Copyright (c) 2009 Shahar Evron 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, 10 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the 12 | * Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | /** 29 | * @file vktor_unicode.h 30 | * 31 | * vktor Unicode header file - Unicode related functions 32 | * 33 | * @internal 34 | */ 35 | 36 | #ifndef _VKTOR_UNICODE_H 37 | 38 | /** 39 | * @ingroup internal 40 | * @{ 41 | */ 42 | 43 | /** 44 | * Convenience macro to check if a codepoint is a high surrogate 45 | */ 46 | #define VKTOR_UNICODE_HIGH_SURROGATE(cp) (cp >= 0xd800 && cp <= 0xdbff) 47 | 48 | /** 49 | * Convenience macro to check if a codepoint is a low surrogate 50 | */ 51 | #define VKTOR_UNICODE_LOW_SURROGATE(cp) (cp >= 0xdc99 && cp <= 0xdfff) 52 | 53 | /** 54 | * @brief Convert a hexadecimal digit to it's integer value 55 | * 56 | * Convert a single char containing a hexadecimal digit to it's integer value 57 | * (0 - 15). Used when converting escaped Unicode sequences to UTF-* characters. 58 | * 59 | * Note that this function does not do any error checking because it is for 60 | * internal use only. If the character is not a valid hex digit, 0 is returned. 61 | * Obviously since 0 is a valid value it should not be used for error checking. 62 | * 63 | * @param [in] hex Hexadecimal character 64 | * 65 | * @return Integer value (0 - 15). 66 | */ 67 | unsigned char vktor_unicode_hex_to_int(unsigned char hex); 68 | 69 | /** 70 | * @brief Encode a Unicode code point to a UTF-8 string 71 | * 72 | * Encode a 16 bit number representing a Unicode code point (UCS-2) to 73 | * a UTF-8 encoded string. Note that this function does not handle surrogate 74 | * pairs - you should use vktor_uncode_sp_to_utf8() in this case. 75 | * 76 | * @param [in] cp the unicode codepoint 77 | * @param [out] utf8 a pointer to a 5 byte long string (at least) that will 78 | * be populated with the UTF-8 encoded string 79 | * 80 | * @return the length of the UTF-8 string (1 - 3 bytes) or 0 in case of error 81 | */ 82 | short vktor_unicode_cp_to_utf8(unsigned short cp, unsigned char *utf8); 83 | 84 | /** 85 | * @brief Convert a UTF-16 surrogate pair to a UTF-8 character 86 | * 87 | * Converts a UTF-16 surrogate pair (two 16 bit characters) into a single 4-byte 88 | * UTF-8 character. This function should be called only after checking that 89 | * you have a valid surrogate pair. 90 | * 91 | * @param [in] high High surrogate 92 | * @param [in] low Low surrogate 93 | * @param [out] utf8 Resulting UTF-8 character 94 | * 95 | * @return Byte-length of resulting character - should be 4, or 0 if there's an 96 | * error. 97 | */ 98 | short vktor_unicode_sp_to_utf8(unsigned short high, unsigned short low, unsigned char *utf8); 99 | 100 | /** @} */ // end of internal API 101 | 102 | #define _VKTOR_UNICODE_H 103 | #endif /* VKTOR_UNICODE_H */ 104 | -------------------------------------------------------------------------------- /php_jsonreader.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2008 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id: header,v 1.16.2.1.2.1.2.1 2008/02/07 19:39:50 iliaa Exp $ */ 20 | 21 | #ifndef PHP_JSONREADER_H 22 | #define PHP_JSONREADER_H 23 | 24 | extern zend_module_entry jsonreader_module_entry; 25 | #define phpext_jsonreader_ptr &jsonreader_module_entry 26 | 27 | #ifdef PHP_WIN32 28 | # define PHP_JSONREADER_API __declspec(dllexport) 29 | #elif defined(__GNUC__) && __GNUC__ >= 4 30 | # define PHP_JSONREADER_API __attribute__ ((visibility("default"))) 31 | #else 32 | # define PHP_JSONREADER_API 33 | #endif 34 | 35 | #ifdef ZTS 36 | #include "TSRM.h" 37 | #endif 38 | 39 | PHP_MINIT_FUNCTION(jsonreader); 40 | PHP_MSHUTDOWN_FUNCTION(jsonreader); 41 | PHP_MINFO_FUNCTION(jsonreader); 42 | 43 | ZEND_BEGIN_MODULE_GLOBALS(jsonreader) 44 | long max_depth; 45 | long read_buffer; 46 | ZEND_END_MODULE_GLOBALS(jsonreader) 47 | 48 | /* In every utility function you add that needs to use variables 49 | in php_jsonreader_globals, call TSRMLS_FETCH(); after declaring other 50 | variables used by that function, or better yet, pass in TSRMLS_CC 51 | after the last function argument and declare your utility function 52 | with TSRMLS_DC after the last declared argument. Always refer to 53 | the globals in your function as JSONREADER_G(variable). You are 54 | encouraged to rename these macros something shorter, see 55 | examples in any other php module directory. 56 | */ 57 | 58 | #ifdef ZTS 59 | #define JSONREADER_G(v) TSRMG(jsonreader_globals_id, zend_jsonreader_globals *, v) 60 | #else 61 | #define JSONREADER_G(v) (jsonreader_globals.v) 62 | #endif 63 | 64 | #endif /* PHP_JSONREADER_H */ 65 | 66 | 67 | /* 68 | * Local variables: 69 | * tab-width: 4 70 | * c-basic-offset: 4 71 | * End: 72 | * vim600: noet sw=4 ts=4 fdm=marker 73 | * vim<600: noet sw=4 ts=4 74 | */ 75 | -------------------------------------------------------------------------------- /tests/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for jsonreader presence 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 11 | --EXPECT-- 12 | jsonreader extension is available 13 | object(JSONReader)#1 (0) { 14 | } 15 | 16 | -------------------------------------------------------------------------------- /tests/002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Parse a simple short array 3 | --SKIPIF-- 4 | 5 | --STDIN-- 6 | ["Galahad", "Bedivere", "Lancelot", "Bors"] 7 | --FILE-- 8 | open('php://stdin'); 11 | while ($jr->read()) { 12 | if ($jr->tokenType & JSONReader::VALUE) { 13 | echo "- {$jr->value}\n"; 14 | } 15 | } 16 | $jr->close(); 17 | ?> 18 | --EXPECT-- 19 | - Galahad 20 | - Bedivere 21 | - Lancelot 22 | - Bors 23 | 24 | -------------------------------------------------------------------------------- /tests/003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Parse a simple JSON object into an associative array 3 | --SKIPIF-- 4 | 5 | --STDIN-- 6 | { 7 | "pure": "Sir Robin", 8 | "brave": "Sir Lancelot", 9 | "not-quite-so-brave": "Sir Robin", 10 | "aptly-named": "Sir Not-Appearing-in-this-Film" 11 | } 12 | --FILE-- 13 | open('php://stdin'); 16 | $arr = array(); 17 | while ($jr->read()) { 18 | if ($jr->tokenType == JSONReader::OBJECT_KEY) { 19 | $key = $jr->value; 20 | if (! $jr->read() || ! $jr->tokenType == JSONReader::VALUE) { 21 | echo "failed reading value of $key!\n"; 22 | exit; 23 | } 24 | $value = $jr->value; 25 | 26 | $arr[$key] = $value; 27 | } 28 | } 29 | var_dump($arr); 30 | --EXPECT-- 31 | array(4) { 32 | ["pure"]=> 33 | string(9) "Sir Robin" 34 | ["brave"]=> 35 | string(12) "Sir Lancelot" 36 | ["not-quite-so-brave"]=> 37 | string(9) "Sir Robin" 38 | ["aptly-named"]=> 39 | string(30) "Sir Not-Appearing-in-this-Film" 40 | } 41 | 42 | -------------------------------------------------------------------------------- /tests/004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Parse some integers and floating-point numbers 3 | --SKIPIF-- 4 | 5 | --STDIN-- 6 | [ 7 | 12345, 66.666, -444, 123544009, 8 | 12.5, 10011, 12.5e-1, 0.0005 ] 9 | --FILE-- 10 | open($in); 16 | 17 | $ints = array(); 18 | $floats = array(); 19 | while ($jr->read()) { 20 | if ($jr->tokenType & JSONReader::NUMBER) { 21 | switch($jr->tokenType) { 22 | case JSONReader::INT: 23 | $ints[] = $jr->value; 24 | break; 25 | 26 | case JSONReader::FLOAT: 27 | $floats[] = $jr->value; 28 | break; 29 | 30 | default: 31 | echo "Unexpected type: "; 32 | var_dump($jr->value); 33 | break; 34 | } 35 | } 36 | } 37 | var_dump($ints, $floats); 38 | ?> 39 | --EXPECT-- 40 | array(4) { 41 | [0]=> 42 | int(12345) 43 | [1]=> 44 | int(-444) 45 | [2]=> 46 | int(123544009) 47 | [3]=> 48 | int(10011) 49 | } 50 | array(4) { 51 | [0]=> 52 | float(66.666) 53 | [1]=> 54 | float(12.5) 55 | [2]=> 56 | float(1.25) 57 | [3]=> 58 | float(0.0005) 59 | } 60 | 61 | -------------------------------------------------------------------------------- /tests/005.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | parse a multi-dimentional JSON array and use the currentDepth property 3 | --SKIPIF-- 4 | 5 | --STDIN-- 6 | [ 7 | ["Windows XP", "Windows Vista", "Windows 7"], 8 | ["AIX", "Solaris", "OpenSolaris", 9 | ["FreeBSD", "NetBSD", "OpenBSD", "Darwin"], 10 | ["Gentoo Linux", "Slackware", "Mandriva", 11 | ["Debian GNU/Linux", "Ubuntu Linux"], 12 | ["RHEL", "CentOS", "Fedora Linux"], 13 | ["SLES", "OpenSUSE"] 14 | ] 15 | ], 16 | "AS/400", 17 | "Mac OS", 18 | "DOS" 19 | ] 20 | --FILE-- 21 | open('php://stdin'); 25 | 26 | while ($reader->read()) { 27 | if ($reader->tokenType == JSONReader::STRING) { 28 | // print indent 29 | echo str_repeat(" ", $reader->currentDepth); 30 | echo "* $reader->value\n"; 31 | } elseif ($reader->tokenType == JSONReader::ARRAY_START) { 32 | echo str_repeat("-", $reader->currentDepth) . ">\n"; 33 | } 34 | } 35 | 36 | $reader->close(); 37 | 38 | ?> 39 | --EXPECT-- 40 | -> 41 | --> 42 | * Windows XP 43 | * Windows Vista 44 | * Windows 7 45 | --> 46 | * AIX 47 | * Solaris 48 | * OpenSolaris 49 | ---> 50 | * FreeBSD 51 | * NetBSD 52 | * OpenBSD 53 | * Darwin 54 | ---> 55 | * Gentoo Linux 56 | * Slackware 57 | * Mandriva 58 | ----> 59 | * Debian GNU/Linux 60 | * Ubuntu Linux 61 | ----> 62 | * RHEL 63 | * CentOS 64 | * Fedora Linux 65 | ----> 66 | * SLES 67 | * OpenSUSE 68 | * AS/400 69 | * Mac OS 70 | * DOS 71 | 72 | -------------------------------------------------------------------------------- /tests/006.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test that an error occurs when passing the maximal nesting level 3 | --SKIPIF-- 4 | 5 | --STDIN-- 6 | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ 7 | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ 8 | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ 9 | "Some Value" 10 | ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] 11 | ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] 12 | ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] 13 | --FILE-- 14 | open('php://stdin'); 17 | while ($rdr->read()) { 18 | if ($rdr->tokenType & JSONReader::VALUE) echo "FOUND IT!\n"; 19 | } 20 | $rdr->close(); 21 | ?> 22 | --EXPECTF-- 23 | Warning: JSONReader::read(): parser error [#%d]: maximal nesting level of 64 reached in %s on line %d 24 | 25 | -------------------------------------------------------------------------------- /tests/007.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test that we can modify the max nesting level through an INI setting 3 | --SKIPIF-- 4 | 5 | --STDIN-- 6 | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ 7 | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ 8 | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ 9 | "Some Value" 10 | ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] 11 | ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] 12 | ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] 13 | --FILE-- 14 | open('php://stdin'); 18 | while ($rdr->read()) { 19 | if ($rdr->tokenType & JSONReader::VALUE) echo "FOUND IT at {$rdr->currentDepth}!\n"; 20 | } 21 | $rdr->close(); 22 | ?> 23 | --EXPECTF-- 24 | FOUND IT at 96! 25 | 26 | -------------------------------------------------------------------------------- /tests/008.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test that we can modify the max nesting level through a constructor attribute 3 | --SKIPIF-- 4 | 5 | --STDIN-- 6 | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ 7 | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ 8 | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ 9 | "Some Value" 10 | ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] 11 | ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] 12 | ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] 13 | --FILE-- 14 | 100 17 | )); 18 | $rdr->open('php://stdin'); 19 | while ($rdr->read()) { 20 | if ($rdr->tokenType & JSONReader::VALUE) echo "FOUND IT at {$rdr->currentDepth}!\n"; 21 | } 22 | $rdr->close(); 23 | ?> 24 | --EXPECTF-- 25 | FOUND IT at 96! 26 | 27 | -------------------------------------------------------------------------------- /tests/009.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test that the constructor takes the right argument(s) 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 'somevalue')); 11 | 12 | // Expected to fail 13 | echo 'String: '; 14 | $rdr = new JSONReader('somestring'); 15 | echo "NULL: "; 16 | $rdr = new JSONReader(null); 17 | ?> 18 | --EXPECTF-- 19 | String: 20 | Warning: JSONReader::__construct() expects parameter 1 to be array, string given in %s on line %d 21 | NULL: 22 | Warning: JSONReader::__construct() expects parameter 1 to be array, null given in %s on line %d 23 | 24 | -------------------------------------------------------------------------------- /tests/010.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check that the JSONReaderException class is available 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | getMessage()} {$e->getCode()}\n"; 11 | } 12 | ?> 13 | --EXPECT-- 14 | Testing 123 15 | 16 | -------------------------------------------------------------------------------- /tests/011.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test that a warning is thrown in case of a JSON parse error 3 | --SKIPIF-- 4 | 5 | --STDIN-- 6 | { 12: "<= Numeric keys are not valid in JSON!" } 7 | --FILE-- 8 | open('php://stdin'); 11 | while ($rdr->read()) { 12 | echo "- $rdr->tokenType\n"; 13 | } 14 | $rdr->close(); 15 | ?> 16 | --EXPECTF-- 17 | - %d 18 | 19 | Warning: JSONReader::read(): parser error [#%d]: Unexpected character in input: '1' (0x31) in %s on line %d 20 | 21 | -------------------------------------------------------------------------------- /tests/012.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test that an exception is thrown when ERRMODE_EXCEPT is set 3 | --SKIPIF-- 4 | 5 | --STDIN-- 6 | { 12: "<= Numeric keys are not valid in JSON!" } 7 | --FILE-- 8 | JSONReader::ERRMODE_EXCEPT 11 | )); 12 | $rdr->open('php://stdin'); 13 | try { 14 | while ($rdr->read()) { 15 | echo "- $rdr->tokenType\n"; 16 | } 17 | } catch (JSONReaderException $e) { 18 | echo "EX: {$e->getCode()} {$e->getMessage()}\n"; 19 | } 20 | $rdr->close(); 21 | ?> 22 | --EXPECTF-- 23 | - %d 24 | EX: %d Unexpected character in input: '1' (0x31) 25 | 26 | -------------------------------------------------------------------------------- /tests/013.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test that the value property is null for non-value tokens 3 | --SKIPIF-- 4 | 5 | --STDIN-- 6 | [{"foo":null}] 7 | --FILE-- 8 | open('php://stdin'); 11 | do { 12 | var_dump($rdr->value); 13 | } while ($rdr->read()); 14 | $rdr->close(); 15 | ?> 16 | --EXPECT-- 17 | NULL 18 | NULL 19 | NULL 20 | string(3) "foo" 21 | NULL 22 | NULL 23 | NULL 24 | 25 | -------------------------------------------------------------------------------- /tests/014.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test that we can get the current struct using currentStruct 3 | --SKIPIF-- 4 | 5 | --STDIN-- 6 | [{ 7 | "key": 12, 8 | "array": [1, 2, [3], {"a" : "B"}] 9 | }] 10 | --FILE-- 11 | open('php://stdin'); 14 | do { 15 | var_dump($rdr->currentStruct); 16 | } while ($rdr->read()); 17 | $rdr->close(); 18 | ?> 19 | --EXPECT-- 20 | NULL 21 | int(1) 22 | int(2) 23 | int(2) 24 | int(2) 25 | int(2) 26 | int(1) 27 | int(1) 28 | int(1) 29 | int(1) 30 | int(1) 31 | int(1) 32 | int(2) 33 | int(2) 34 | int(2) 35 | int(1) 36 | int(2) 37 | int(1) 38 | NULL 39 | 40 | --------------------------------------------------------------------------------