summaryrefslogtreecommitdiff
path: root/ext/json/json_encoder.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/json/json_encoder.c')
-rw-r--r--ext/json/json_encoder.c131
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);