summaryrefslogtreecommitdiff
path: root/ext/json/json.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/json/json.c')
-rw-r--r--ext/json/json.c95
1 files changed, 54 insertions, 41 deletions
diff --git a/ext/json/json.c b/ext/json/json.c
index bc0544808f..24324bbe99 100644
--- a/ext/json/json.c
+++ b/ext/json/json.c
@@ -167,7 +167,7 @@ static PHP_MINFO_FUNCTION(json)
}
/* }}} */
-static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC);
+static void json_escape_string(smart_str *buf, char *s, size_t len, int options TSRMLS_DC);
static int json_determine_array_type(zval *val TSRMLS_DC) /* {{{ */
{
@@ -177,7 +177,7 @@ static int json_determine_array_type(zval *val TSRMLS_DC) /* {{{ */
i = myht ? zend_hash_num_elements(myht) : 0;
if (i > 0) {
zend_string *key;
- ulong index, idx;
+ zend_ulong index, idx;
idx = 0;
ZEND_HASH_FOREACH_KEY(myht, index, key) {
@@ -252,7 +252,7 @@ static void json_encode_array(smart_str *buf, zval *val, int options TSRMLS_DC)
if (i > 0) {
zend_string *key;
zval *data;
- ulong index;
+ zend_ulong index;
HashTable *tmp_ht;
ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, data) {
@@ -308,7 +308,7 @@ static void json_encode_array(smart_str *buf, zval *val, int options TSRMLS_DC)
json_pretty_print_indent(buf, options TSRMLS_CC);
smart_str_appendc(buf, '"');
- smart_str_append_long(buf, (long) index);
+ smart_str_append_long(buf, (zend_long) index);
smart_str_appendc(buf, '"');
smart_str_appendc(buf, ':');
@@ -380,12 +380,11 @@ static int json_utf8_to_utf16(unsigned short *utf16, char utf8[], int len) /* {{
}
/* }}} */
-static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC) /* {{{ */
+static void json_escape_string(smart_str *buf, char *s, size_t len, int options TSRMLS_DC) /* {{{ */
{
- int pos = 0, ulen = 0;
- unsigned short us;
- unsigned short *utf16;
- size_t newlen;
+ int status;
+ unsigned int us, next_us = 0;
+ size_t pos, checkpoint, newlen;
if (len == 0) {
smart_str_appendl(buf, "\"\"", 2);
@@ -395,7 +394,7 @@ static void json_escape_string(smart_str *buf, char *s, int len, int options TSR
if (options & PHP_JSON_NUMERIC_CHECK) {
double d;
int type;
- long p;
+ zend_long p;
if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) {
if (type == IS_LONG) {
@@ -416,34 +415,51 @@ static void json_escape_string(smart_str *buf, char *s, int len, int options TSR
}
- utf16 = (options & PHP_JSON_UNESCAPED_UNICODE) ? NULL : (unsigned short *) safe_emalloc(len, sizeof(unsigned short), 0);
- ulen = json_utf8_to_utf16(utf16, s, len);
- if (ulen <= 0) {
- if (utf16) {
- efree(utf16);
- }
- if (ulen < 0) {
+ if (options & PHP_JSON_UNESCAPED_UNICODE) {
+ /* validate UTF-8 string first */
+ if (json_utf8_to_utf16(NULL, s, len) < 0) {
JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
smart_str_appendl(buf, "null", 4);
- } else {
- smart_str_appendl(buf, "\"\"", 2);
+ return;
}
- return;
- }
- if (!(options & PHP_JSON_UNESCAPED_UNICODE)) {
- len = ulen;
}
+ pos = 0;
+ checkpoint = buf->s ? buf->s->len : 0;
+
/* pre-allocate for string length plus 2 quotes */
smart_str_alloc(buf, len+2, 0);
smart_str_appendc(buf, '"');
- while (pos < len)
- {
- us = (options & PHP_JSON_UNESCAPED_UNICODE) ? s[pos++] : utf16[pos++];
+ do {
+ if (UNEXPECTED(next_us)) {
+ us = next_us;
+ next_us = 0;
+ } else {
+ us = (unsigned char)s[pos];
+ if (!(options & PHP_JSON_UNESCAPED_UNICODE) && us >= 0x80) {
+ /* UTF-8 character */
+ us = php_next_utf8_char((const unsigned char *)s, len, &pos, &status);
+ if (status != SUCCESS) {
+ if (buf->s) {
+ buf->s->len = checkpoint;
+ }
+ JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
+ smart_str_appendl(buf, "null", 4);
+ return;
+ }
+ /* From http://en.wikipedia.org/wiki/UTF16 */
+ if (us >= 0x10000) {
+ us -= 0x10000;
+ next_us = (unsigned short)((us & 0x3ff) | 0xdc00);
+ us = (unsigned short)((us >> 10) | 0xd800);
+ }
+ } else {
+ pos++;
+ }
+ }
- switch (us)
- {
+ switch (us) {
case '"':
if (options & PHP_JSON_HEX_QUOT) {
smart_str_appendl(buf, "\\u0022", 6);
@@ -528,12 +544,9 @@ static void json_escape_string(smart_str *buf, char *s, int len, int options TSR
}
break;
}
- }
-
+ } while (pos < len || next_us);
+
smart_str_appendc(buf, '"');
- if (utf16) {
- efree(utf16);
- }
}
/* }}} */
@@ -651,9 +664,9 @@ again:
}
/* }}} */
-PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, int options, long depth TSRMLS_DC) /* {{{ */
+PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, size_t str_len, zend_long options, zend_long depth TSRMLS_DC) /* {{{ */
{
- int utf16_len;
+ size_t utf16_len;
unsigned short *utf16;
JSON_parser jp;
@@ -678,7 +691,7 @@ PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len,
if (!parse_JSON_ex(jp, return_value, utf16, utf16_len, options TSRMLS_CC)) {
double d;
int type, overflow_info;
- long p;
+ zend_long p;
char *trim = str;
int trim_len = str_len;
@@ -758,8 +771,8 @@ static PHP_FUNCTION(json_encode)
{
zval *parameter;
smart_str buf = {0};
- long options = 0;
- long depth = JSON_PARSER_DEFAULT_DEPTH;
+ zend_long options = 0;
+ zend_long depth = JSON_PARSER_DEFAULT_DEPTH;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", &parameter, &options, &depth) == FAILURE) {
return;
@@ -786,10 +799,10 @@ static PHP_FUNCTION(json_encode)
static PHP_FUNCTION(json_decode)
{
char *str;
- int str_len;
+ size_t str_len;
zend_bool assoc = 0; /* return JS objects as PHP objects by default */
- long depth = JSON_PARSER_DEFAULT_DEPTH;
- long options = 0;
+ zend_long depth = JSON_PARSER_DEFAULT_DEPTH;
+ zend_long options = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bll", &str, &str_len, &assoc, &depth, &options) == FAILURE) {
return;