├── .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 |
--------------------------------------------------------------------------------