diff options
Diffstat (limited to 'ext/json/json.c')
-rw-r--r-- | ext/json/json.c | 95 |
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", ¶meter, &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; |