diff options
Diffstat (limited to 'ext/json/json_encoder.c')
-rw-r--r-- | ext/json/json_encoder.c | 131 |
1 files changed, 78 insertions, 53 deletions
diff --git a/ext/json/json_encoder.c b/ext/json/json_encoder.c index 8e3eecc0d8..c79e694f26 100644 --- a/ext/json/json_encoder.c +++ b/ext/json/json_encoder.c @@ -33,7 +33,7 @@ static const char digits[] = "0123456789abcdef"; static int php_json_escape_string( - smart_str *buf, char *s, size_t len, + smart_str *buf, const char *s, size_t len, int options, php_json_encoder *encoder); static int php_json_determine_array_type(zval *val) /* {{{ */ @@ -113,17 +113,17 @@ static inline void php_json_encode_double(smart_str *buf, double d, int options) } /* }}} */ -#define PHP_JSON_HASH_APPLY_PROTECTION_INC(_tmp_ht) \ +#define PHP_JSON_HASH_PROTECT_RECURSION(_tmp_ht) \ do { \ - if (_tmp_ht && ZEND_HASH_APPLY_PROTECTION(_tmp_ht)) { \ - ZEND_HASH_INC_APPLY_COUNT(_tmp_ht); \ + if (_tmp_ht && !(GC_FLAGS(_tmp_ht) & GC_IMMUTABLE)) { \ + GC_PROTECT_RECURSION(_tmp_ht); \ } \ } while (0) -#define PHP_JSON_HASH_APPLY_PROTECTION_DEC(_tmp_ht) \ +#define PHP_JSON_HASH_UNPROTECT_RECURSION(_tmp_ht) \ do { \ - if (_tmp_ht && ZEND_HASH_APPLY_PROTECTION(_tmp_ht)) { \ - ZEND_HASH_DEC_APPLY_COUNT(_tmp_ht); \ + if (_tmp_ht && !(GC_FLAGS(_tmp_ht) & GC_IMMUTABLE)) { \ + GC_UNPROTECT_RECURSION(_tmp_ht); \ } \ } while (0) @@ -140,13 +140,13 @@ static int php_json_encode_array(smart_str *buf, zval *val, int options, php_jso r = PHP_JSON_OUTPUT_OBJECT; } - if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 0) { + if (myht && GC_IS_RECURSIVE(myht)) { encoder->error_code = PHP_JSON_ERROR_RECURSION; smart_str_appendl(buf, "null", 4); return FAILURE; } - PHP_JSON_HASH_APPLY_PROTECTION_INC(myht); + PHP_JSON_HASH_PROTECT_RECURSION(myht); if (r == PHP_JSON_OUTPUT_ARRAY) { smart_str_appendc(buf, '['); @@ -217,13 +217,13 @@ static int php_json_encode_array(smart_str *buf, zval *val, int options, php_jso if (php_json_encode_zval(buf, data, options, encoder) == FAILURE && !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) { - PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht); + PHP_JSON_HASH_UNPROTECT_RECURSION(myht); return FAILURE; } } ZEND_HASH_FOREACH_END(); } - PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht); + PHP_JSON_HASH_UNPROTECT_RECURSION(myht); if (encoder->depth > encoder->max_depth) { encoder->error_code = PHP_JSON_ERROR_DEPTH; @@ -250,12 +250,13 @@ static int php_json_encode_array(smart_str *buf, zval *val, int options, php_jso /* }}} */ static int php_json_escape_string( - smart_str *buf, char *s, size_t len, + smart_str *buf, const char *s, size_t len, int options, php_json_encoder *encoder) /* {{{ */ { int status; unsigned int us; size_t pos, checkpoint; + char *dst; if (len == 0) { smart_str_appendl(buf, "\"\"", 2); @@ -287,72 +288,89 @@ static int php_json_escape_string( do { us = (unsigned char)s[pos]; - if (us >= 0x80) { - int utf8_sub = 0; - size_t prev_pos = pos; - + if (UNEXPECTED(us >= 0x80)) { + if (pos) { + smart_str_appendl(buf, s, pos); + s += pos; + pos = 0; + } us = php_next_utf8_char((unsigned char *)s, len, &pos, &status); + len -= pos; /* check whether UTF8 character is correct */ - if (status != SUCCESS) { + if (UNEXPECTED(status != SUCCESS)) { + s += pos; + pos = 0; if (options & PHP_JSON_INVALID_UTF8_IGNORE) { /* ignore invalid UTF8 character */ continue; } else if (options & PHP_JSON_INVALID_UTF8_SUBSTITUTE) { /* Use Unicode character 'REPLACEMENT CHARACTER' (U+FFFD) */ - us = 0xfffd; - utf8_sub = 1; - } else { - if (buf->s) { - ZSTR_LEN(buf->s) = checkpoint; + if (options & PHP_JSON_UNESCAPED_UNICODE) { + smart_str_appendl(buf, "\xef\xbf\xbd", 3); + } else { + smart_str_appendl(buf, "\\ufffd", 6); } + continue; + } else { + ZSTR_LEN(buf->s) = checkpoint; encoder->error_code = PHP_JSON_ERROR_UTF8; if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) { smart_str_appendl(buf, "null", 4); } return FAILURE; } - } /* Escape U+2028/U+2029 line terminators, UNLESS both JSON_UNESCAPED_UNICODE and JSON_UNESCAPED_LINE_TERMINATORS were provided */ - if ((options & PHP_JSON_UNESCAPED_UNICODE) + } else if ((options & PHP_JSON_UNESCAPED_UNICODE) && ((options & PHP_JSON_UNESCAPED_LINE_TERMINATORS) || us < 0x2028 || us > 0x2029)) { - if (utf8_sub) { - smart_str_appendl(buf, "\xef\xbf\xbd", 3); - } else { - smart_str_appendl(buf, s + prev_pos, pos - prev_pos); - } + smart_str_appendl(buf, s, pos); + s += pos; + pos = 0; continue; } /* From http://en.wikipedia.org/wiki/UTF16 */ if (us >= 0x10000) { unsigned int next_us; + us -= 0x10000; next_us = (unsigned short)((us & 0x3ff) | 0xdc00); us = (unsigned short)((us >> 10) | 0xd800); - smart_str_appendl(buf, "\\u", 2); - smart_str_appendc(buf, digits[(us & 0xf000) >> 12]); - smart_str_appendc(buf, digits[(us & 0xf00) >> 8]); - smart_str_appendc(buf, digits[(us & 0xf0) >> 4]); - smart_str_appendc(buf, digits[(us & 0xf)]); + dst = smart_str_extend(buf, 6); + dst[0] = '\\'; + dst[1] = 'u'; + dst[2] = digits[(us >> 12) & 0xf]; + dst[3] = digits[(us >> 8) & 0xf]; + dst[4] = digits[(us >> 4) & 0xf]; + dst[5] = digits[us & 0xf]; us = next_us; } - smart_str_appendl(buf, "\\u", 2); - smart_str_appendc(buf, digits[(us & 0xf000) >> 12]); - smart_str_appendc(buf, digits[(us & 0xf00) >> 8]); - smart_str_appendc(buf, digits[(us & 0xf0) >> 4]); - smart_str_appendc(buf, digits[(us & 0xf)]); + dst = smart_str_extend(buf, 6); + dst[0] = '\\'; + dst[1] = 'u'; + dst[2] = digits[(us >> 12) & 0xf]; + dst[3] = digits[(us >> 8) & 0xf]; + dst[4] = digits[(us >> 4) & 0xf]; + dst[5] = digits[us & 0xf]; + s += pos; + pos = 0; } else { static const uint32_t charmap[4] = { 0xffffffff, 0x500080c4, 0x10000000, 0x00000000}; - pos++; - if (EXPECTED(!(charmap[us >> 5] & (1 << (us & 0x1f))))) { - smart_str_appendc(buf, (unsigned char) us); + len--; + if (EXPECTED(!ZEND_BIT_TEST(charmap, us))) { + pos++; } else { + if (pos) { + smart_str_appendl(buf, s, pos); + s += pos; + pos = 0; + } + s++; switch (us) { case '"': if (options & PHP_JSON_HEX_QUOT) { @@ -428,15 +446,22 @@ static int php_json_escape_string( default: ZEND_ASSERT(us < ' '); - smart_str_appendl(buf, "\\u00", sizeof("\\u00")-1); - smart_str_appendc(buf, digits[(us & 0xf0) >> 4]); - smart_str_appendc(buf, digits[(us & 0xf)]); + dst = smart_str_extend(buf, 6); + dst[0] = '\\'; + dst[1] = 'u'; + dst[2] = '0'; + dst[3] = '0'; + dst[4] = digits[(us >> 4) & 0xf]; + dst[5] = digits[us & 0xf]; break; } } } - } while (pos < len); + } while (len); + if (EXPECTED(pos)) { + smart_str_appendl(buf, s, pos); + } smart_str_appendc(buf, '"'); return SUCCESS; @@ -450,7 +475,7 @@ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int op zval retval, fname; int return_code; - if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 0) { + if (myht && GC_IS_RECURSIVE(myht)) { encoder->error_code = PHP_JSON_ERROR_RECURSION; if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) { smart_str_appendl(buf, "null", 4); @@ -458,11 +483,11 @@ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int op return FAILURE; } - PHP_JSON_HASH_APPLY_PROTECTION_INC(myht); + PHP_JSON_HASH_PROTECT_RECURSION(myht); ZVAL_STRING(&fname, "jsonSerialize"); - if (FAILURE == call_user_function_ex(EG(function_table), val, &fname, &retval, 0, NULL, 1, NULL) || Z_TYPE(retval) == IS_UNDEF) { + if (FAILURE == call_user_function(EG(function_table), val, &fname, &retval, 0, NULL) || Z_TYPE(retval) == IS_UNDEF) { if (!EG(exception)) { zend_throw_exception_ex(NULL, 0, "Failed calling %s::jsonSerialize()", ZSTR_VAL(ce->name)); } @@ -471,7 +496,7 @@ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int op if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) { smart_str_appendl(buf, "null", 4); } - PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht); + PHP_JSON_HASH_UNPROTECT_RECURSION(myht); return FAILURE; } @@ -483,19 +508,19 @@ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int op if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) { smart_str_appendl(buf, "null", 4); } - PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht); + PHP_JSON_HASH_UNPROTECT_RECURSION(myht); return FAILURE; } if ((Z_TYPE(retval) == IS_OBJECT) && (Z_OBJ(retval) == Z_OBJ_P(val))) { /* Handle the case where jsonSerialize does: return $this; by going straight to encode array */ - PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht); + PHP_JSON_HASH_UNPROTECT_RECURSION(myht); return_code = php_json_encode_array(buf, &retval, options, encoder); } else { /* All other types, encode as normal */ return_code = php_json_encode_zval(buf, &retval, options, encoder); - PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht); + PHP_JSON_HASH_UNPROTECT_RECURSION(myht); } zval_ptr_dtor(&retval); |