Skip to content

Commit

Permalink
Merge pull request #86 from TysonAndre/fix-c-bindings
Browse files Browse the repository at this point in the history
3.0.0: Throw errors/SimdJsonException in more cases to be consistent with json_decode, make C bindings usable by other PECLs
  • Loading branch information
crazyxman authored Oct 17, 2022
2 parents aec72eb + 222ad9a commit d9489d3
Show file tree
Hide file tree
Showing 22 changed files with 551 additions and 271 deletions.
69 changes: 56 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,47 +108,75 @@ var_dump($res) //int(5)
<?php

/**
* Takes a JSON encoded string and converts it into a PHP variable.
* Similar to json_decode()
*
* @param string $json The JSON string being decoded
* @param bool $associative When true, JSON objects will be returned as associative arrays.
* When false, JSON objects will be returned as objects.
* @param int $depth the maximum nesting depth of the structure being decoded.
* @return array|stdClass|string|float|int|bool|null
* @throws SimdJsonException for invalid JSON
* (or document over 4GB, or out of range integer/float)
* (or $json over 4GB long, or out of range integer/float)
* @throws SimdJsonValueError for invalid $depth
*/
function simdjson_decode(string $json, bool $assoc = false, int $depth = 512) {}
function simdjson_decode(string $json, bool $associative = false, int $depth = 512) {}

/**
* Returns true if json is valid.
*
* @return ?bool (null if depth is invalid)
* @param string $json The JSON string being decoded
* @param int $depth the maximum nesting depth of the structure being decoded.
* @return bool
* @throws SimdJsonValueError for invalid $depth
*/
function simdjson_is_valid(string $json, int $depth = 512) : ?bool {}
function simdjson_is_valid(string $json, int $depth = 512) : bool {}

/**
* Parses $json and returns the number of keys in $json matching the JSON pointer $key
*
* @return ?int (null if depth is invalid)
* @throws SimdJsonException for invalid JSON
* @param string $json The JSON string being decoded
* @param string $key The JSON pointer being requested
* @param int $depth The maximum nesting depth of the structure being decoded.
* @param bool $throw_if_uncountable If true, then throw SimdJsonException instead of returning 0 for JSON pointers
to values that are neither objects nor arrays.
* @return int
* @throws SimdJsonException for invalid JSON or invalid JSON pointer
* (or document over 4GB, or out of range integer/float)
* @throws SimdJsonValueError for invalid $depth
* @see https://www.rfc-editor.org/rfc/rfc6901.html
*/
function simdjson_key_count(string $json, string $key, int $depth = 512) : ?int {}
function simdjson_key_count(string $json, string $key, int $depth = 512, bool $throw_if_uncountable = false) : int {}

/**
* Returns true if the JSON pointer $key could be found.
*
* @return ?bool (null if depth is invalid, false if json is invalid or key is not found)
* @throws SimdJsonException for invalid JSON
* @param string $json The JSON string being decoded
* @param string $key The JSON pointer being requested
* @param int $depth the maximum nesting depth of the structure being decoded.
* @return bool (false if key is not found)
* @throws SimdJsonException for invalid JSON or invalid JSON pointer
* (or document over 4GB, or out of range integer/float)
* @throws SimdJsonValueError for invalid $depth
* @see https://www.rfc-editor.org/rfc/rfc6901.html
*/
function simdjson_key_exists(string $json, string $key, int $depth = 512) : ?bool {}
function simdjson_key_exists(string $json, string $key, int $depth = 512) : bool {}

/**
* Returns the value at $key
* Returns the value at the json pointer $key
*
* @param string $json The JSON string being decoded
* @param string $key The JSON pointer being requested
* @param int $depth the maximum nesting depth of the structure being decoded.
* @param bool $associative When true, JSON objects will be returned as associative arrays.
* When false, JSON objects will be returned as objects.
* @return array|stdClass|string|float|int|bool|null the value at $key
* @throws SimdJsonException for invalid JSON
* @throws SimdJsonException for invalid JSON or invalid JSON pointer
* (or document over 4GB, or out of range integer/float)
* @throws SimdJsonValueError for invalid $depth
* @see https://www.rfc-editor.org/rfc/rfc6901.html
*/
function simdjson_key_value(string $json, string $key, bool $assoc = unknown, int $depth = unknown) {}
function simdjson_key_value(string $json, string $key, bool $associative = false, int $depth = 512) {}

/**
* An error thrown by simdjson when processing json.
Expand All @@ -160,12 +188,27 @@ function simdjson_key_value(string $json, string $key, bool $assoc = unknown, in
*/
class SimdJsonException extends RuntimeException {
}

/**
* Thrown for error conditions on fields such as $depth that are not expected to be
* from user-provided JSON, with similar behavior to php 8.0.
*
* NOTE: https://www.php.net/valueerror was added in php 8.0.
* In older php versions, this extends Error instead.
*
* When support for php 8.0 is dropped completely,
* a major release of simdjson will likely switch to a standard ValueError.
*/
class SimdJsonValueError extends ValueError {
}
```

## Edge cases

There are some differences from `json_decode()` due to the implementation of the underlying simdjson library. This will throw a RuntimeException if simdjson rejects the JSON.

Note that the simdjson PECL is using a fork of the simdjson C library to imitate php's handling of integers and floats in JSON.

1) **Until simdjson 2.1.0,** `simdjson_decode()` differed in how out of range 64-bit integers and floats are handled.

See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#standard-compliance
Expand Down
4 changes: 2 additions & 2 deletions config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ if test "$PHP_SIMDJSON" != "no"; then
dnl Disable development checks of C simdjson library in php debug builds (can manually override)
PHP_NEW_EXTENSION(simdjson, [
php_simdjson.cpp \
src/bindings.cpp \
src/simdjson_bindings.cpp \
src/simdjson.cpp],
$ext_shared,, "-std=c++17 -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -DSIMDJSON_EXCEPTIONS=0 -DSIMDJSON_DEVELOPMENT_CHECKS=0", cxx)

PHP_INSTALL_HEADERS([ext/simdjson], [php_simdjson.h, src/bindings.h src/bindings_defs.h])
PHP_INSTALL_HEADERS([ext/simdjson], [php_simdjson.h src/simdjson_bindings_defs.h])
PHP_ADD_MAKEFILE_FRAGMENT
PHP_ADD_BUILD_DIR(src, 1)
fi
4 changes: 2 additions & 2 deletions config.w32
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ if (PHP_SIMDJSON == "yes") {
'php_simdjson.cpp',
'yes',
'/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 /std:c++latest');
ADD_SOURCES(configure_module_dirname + '/src', 'simdjson.cpp bindings.cpp', 'simdjson');
ADD_SOURCES(configure_module_dirname + '/src', 'simdjson.cpp simdjson_bindings.cpp', 'simdjson');
ADD_FLAG('CFLAGS_SIMDJSON', '/I' + configure_module_dirname);
PHP_INSTALL_HEADERS('ext/simdjson', 'php_simdjson.h src/bindings.h src/bindings_defs.h');
PHP_INSTALL_HEADERS('ext/simdjson', 'php_simdjson.h src/simdjson_bindings_defs.h');
}
// vim:ft=javascript
49 changes: 35 additions & 14 deletions package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,23 @@
-->
<date>2022-10-14</date>
<version>
<release>2.1.0</release>
<api>2.1.0</api>
<release>3.0.0dev</release>
<api>3.0.0dev</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="https://www.apache.org/licenses/LICENSE-2.0.html">Apache 2.0</license>
<notes>
* Allow out of range 64-bit values in JSON integer syntax and allow floating point values outside of the max/min finite floating point values (i.e. parsing to +/- infinity).

This allows simdjson_decode() to be used as a replacement for json_decode() in more use cases.
* Return the correct value in simdjson_key_count() for JSON pointers to arrays/objects exceeding size 0xFFFFFF.
Previously, this would be limited to returning at most 0xFFFFFF(16777215).
* Throw 'SimdJsonException extends RuntimeException' instead of RuntimeException.
* Set the error code from simdjson as SimdJsonException->getCode()
* Expose error_code constants from simdjson as `SIMDJSON_ERR_$ERRCODENAME`
* Add SimdJsonValueError. In php 8.0+, it extends ValueError, and it extends Error in older php versions.
This provides an API similar to the JSON module, which started throwing ValueError for invalid depths in php 8.0.
* Throw SimdJsonValueError instead of emitting notices if $depth is too small or too large in all simdjson PHP functions.
simdjson_is_valid(), simdjson_key_count() and simdjson_key_exists() now have non-null return types.
* Throw a SimdJsonException in simdjson_key_exists on error conditions such as invalid json, to be consistent with other simdjson PHP functions.
* Add an optional boolean `$throw_if_uncountable = false` to simdjson_key_count.
When this is overridden to be true, simdjson_key_count will throw a SimdJsonException if the JSON pointer refers to a value that exists but is neither an array nor an object instead of returning 0.
* Rename the parameter $assoc to $associative in simdjson_decode and simdjson_key_value, to match naming practices used in json_decode()
</notes>
<contents>
<dir name="/">
Expand All @@ -51,9 +51,8 @@
<file name="simdjson.stub.php" role="src"/>
<file name="simdjson_arginfo.h" role="src"/>
<dir name="src">
<file name="bindings.cpp" role="src"/>
<file name="bindings.h" role="src"/>
<file name="bindings_defs.h" role="src"/>
<file name="simdjson_bindings.cpp" role="src"/>
<file name="simdjson_bindings_defs.h" role="src"/>
<file name="simdjson.cpp" role="src"/>
<file name="simdjson.h" role="src"/>
</dir>
Expand All @@ -71,6 +70,7 @@
<file name="decode_strict_types.phpt" role="test"/>
<file name="decode_types.phpt" role="test"/>
<file name="depth.phpt" role="test"/>
<file name="dump.inc" role="test"/>
<file name="is_valid.phpt" role="test"/>
<file name="is_valid_args.phpt" role="test"/>
<file name="key_count.phpt" role="test"/>
Expand Down Expand Up @@ -101,7 +101,6 @@
<file name="bug69187.phpt" role="test"/>
<file name="fail001.phpt" role="test"/>
<file name="json_decode_basic.phpt" role="test"/>
<file name="json_decode_error.phpt" role="test"/>
<file name="json_decode_invalid_utf8.phpt" role="test"/>
<file name="pass001.1_64bit.phpt" role="test"/>
<file name="pass001.1.phpt" role="test"/>
Expand All @@ -125,6 +124,28 @@
<providesextension>simdjson</providesextension>
<extsrcrelease/>
<changelog>
<release>
<date>2022-10-14</date>
<version>
<release>2.1.0</release>
<api>2.1.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="https://www.apache.org/licenses/LICENSE-2.0.html">Apache 2.0</license>
<notes>
* Allow out of range 64-bit values in JSON integer syntax and allow floating point values outside of the max/min finite floating point values (i.e. parsing to +/- infinity).

This allows simdjson_decode() to be used as a replacement for json_decode() in more use cases.
* Return the correct value in simdjson_key_count() for JSON pointers to arrays/objects exceeding size 0xFFFFFF.
Previously, this would be limited to returning at most 0xFFFFFF(16777215).
* Throw 'SimdJsonException extends RuntimeException' instead of RuntimeException.
* Set the error code from simdjson as SimdJsonException->getCode()
* Expose error_code constants from simdjson as `SIMDJSON_ERR_$ERRCODENAME`
</notes>
</release>
<release>
<date>2022-10-01</date>
<version>
Expand Down
Loading

0 comments on commit d9489d3

Please sign in to comment.