Skip to content

Commit 4034d83

Browse files
committed
unserialize() optimization
2 parents 0496d22 + b3e59dc commit 4034d83

File tree

1 file changed

+52
-62
lines changed

1 file changed

+52
-62
lines changed

ext/standard/var_unserializer.re

Lines changed: 52 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,17 @@ PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval *rval)
142142
}
143143
}
144144

145+
static zend_never_inline void var_push_dtor_value(php_unserialize_data_t *var_hashx, zval *rval)
146+
{
147+
if (Z_REFCOUNTED_P(rval)) {
148+
zval *tmp_var = var_tmp_var(var_hashx);
149+
if (!tmp_var) {
150+
return;
151+
}
152+
ZVAL_COPY_VALUE(tmp_var, rval);
153+
}
154+
}
155+
145156
static zend_always_inline zval *tmp_var(php_unserialize_data_t *var_hashx, zend_long num)
146157
{
147158
var_dtor_entries *var_hash;
@@ -451,7 +462,7 @@ static inline size_t parse_uiv(const unsigned char *p)
451462
#define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash
452463
#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash
453464

454-
static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER, int as_key);
465+
static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER);
455466

456467
static zend_always_inline int process_nested_array_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements)
457468
{
@@ -468,54 +479,43 @@ static zend_always_inline int process_nested_array_data(UNSERIALIZE_PARAMETER, H
468479
}
469480

470481
while (elements-- > 0) {
471-
zval key, *data, d, *old_data;
482+
zval key, *data;
472483
zend_ulong idx;
473484

474485
ZVAL_UNDEF(&key);
475486

476-
if (!php_var_unserialize_internal(&key, p, max, NULL, 1)) {
487+
if (!php_var_unserialize_internal(&key, p, max, NULL)) {
477488
zval_ptr_dtor(&key);
478489
goto failure;
479490
}
480491

481-
data = NULL;
482-
ZVAL_UNDEF(&d);
483-
484492
if (Z_TYPE(key) == IS_LONG) {
485493
idx = Z_LVAL(key);
486494
numeric_key:
487-
if (UNEXPECTED((old_data = zend_hash_index_find(ht, idx)) != NULL)) {
488-
//??? update hash
489-
var_push_dtor(var_hash, old_data);
490-
data = zend_hash_index_update(ht, idx, &d);
491-
} else {
492-
data = zend_hash_index_add_new(ht, idx, &d);
495+
data = zend_hash_index_lookup(ht, idx);
496+
if (UNEXPECTED(Z_TYPE_INFO_P(data) != IS_NULL)) {
497+
var_push_dtor_value(var_hash, data);
498+
ZVAL_NULL(data);
493499
}
494500
} else if (Z_TYPE(key) == IS_STRING) {
495501
if (UNEXPECTED(ZEND_HANDLE_NUMERIC(Z_STR(key), idx))) {
496502
goto numeric_key;
497503
}
498-
if (UNEXPECTED((old_data = zend_hash_find(ht, Z_STR(key))) != NULL)) {
499-
//??? update hash
500-
var_push_dtor(var_hash, old_data);
501-
data = zend_hash_update(ht, Z_STR(key), &d);
502-
} else {
503-
data = zend_hash_add_new(ht, Z_STR(key), &d);
504+
data = zend_hash_lookup(ht, Z_STR(key));
505+
if (UNEXPECTED(Z_TYPE_INFO_P(data) != IS_NULL)) {
506+
var_push_dtor_value(var_hash, data);
507+
ZVAL_NULL(data);
504508
}
505509
} else {
506510
zval_ptr_dtor(&key);
507511
goto failure;
508512
}
509513

510-
if (!php_var_unserialize_internal(data, p, max, var_hash, 0)) {
511-
zval_ptr_dtor(&key);
512-
goto failure;
513-
}
514+
zval_ptr_dtor_str(&key);
514515

515-
if (BG(unserialize).level > 1) {
516-
var_push_dtor(var_hash, data);
516+
if (!php_var_unserialize_internal(data, p, max, var_hash)) {
517+
goto failure;
517518
}
518-
zval_ptr_dtor_str(&key);
519519

520520
if (elements && *(*p-1) != ';' && *(*p-1) != '}') {
521521
(*p)--;
@@ -587,49 +587,45 @@ static zend_always_inline int process_nested_object_data(UNSERIALIZE_PARAMETER,
587587
}
588588

589589
while (elements-- > 0) {
590-
zval key, *data, d, *old_data;
590+
zval key, *data;
591591
zend_property_info *info = NULL;
592592

593593
ZVAL_UNDEF(&key);
594594

595-
if (!php_var_unserialize_internal(&key, p, max, NULL, 1)) {
595+
if (!php_var_unserialize_internal(&key, p, max, NULL)) {
596596
zval_ptr_dtor(&key);
597597
goto failure;
598598
}
599599

600-
data = NULL;
601-
ZVAL_UNDEF(&d);
602-
603600
if (EXPECTED(Z_TYPE(key) == IS_STRING)) {
604601
string_key:
605-
if ((old_data = zend_hash_find(ht, Z_STR(key))) != NULL) {
606-
if (Z_TYPE_P(old_data) == IS_INDIRECT) {
602+
data = zend_hash_find(ht, Z_STR(key));
603+
if (data != NULL) {
604+
if (Z_TYPE_P(data) == IS_INDIRECT) {
607605
declared_property:
608606
/* This is a property with a declaration */
609-
old_data = Z_INDIRECT_P(old_data);
610-
info = zend_get_typed_property_info_for_slot(obj, old_data);
607+
data = Z_INDIRECT_P(data);
608+
info = zend_get_typed_property_info_for_slot(obj, data);
611609
if (info) {
612-
if (Z_ISREF_P(old_data)) {
610+
if (Z_ISREF_P(data)) {
613611
/* If the value is overwritten, remove old type source from ref. */
614-
ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(old_data), info);
612+
ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(data), info);
615613
}
616614

617615
if ((*var_hash)->ref_props) {
618616
/* Remove old entry from ref_props table, if it exists. */
619617
zend_hash_index_del(
620-
(*var_hash)->ref_props, (zend_uintptr_t) old_data);
618+
(*var_hash)->ref_props, (zend_uintptr_t) data);
621619
}
622620
}
623-
var_push_dtor(var_hash, old_data);
624-
Z_TRY_DELREF_P(old_data);
625-
ZVAL_UNDEF(old_data);
626-
data = old_data;
621+
var_push_dtor_value(var_hash, data);
622+
ZVAL_NULL(data);
627623
} else {
628624
int ret = is_property_visibility_changed(obj->ce, &key);
629625

630626
if (EXPECTED(!ret)) {
631-
var_push_dtor(var_hash, old_data);
632-
data = zend_hash_update(ht, Z_STR(key), &d);
627+
var_push_dtor_value(var_hash, data);
628+
ZVAL_NULL(data);
633629
} else if (ret < 0) {
634630
goto failure;
635631
} else {
@@ -640,20 +636,17 @@ declared_property:
640636
int ret = is_property_visibility_changed(obj->ce, &key);
641637

642638
if (EXPECTED(!ret)) {
643-
data = zend_hash_add_new(ht, Z_STR(key), &d);
639+
data = zend_hash_add_new(ht, Z_STR(key), &EG(uninitialized_zval));
644640
} else if (ret < 0) {
645641
goto failure;
646642
} else {
647643
second_try:
648-
if ((old_data = zend_hash_find(ht, Z_STR(key))) != NULL) {
649-
if (Z_TYPE_P(old_data) == IS_INDIRECT) {
650-
goto declared_property;
651-
} else {
652-
var_push_dtor(var_hash, old_data);
653-
data = zend_hash_update(ht, Z_STR(key), &d);
654-
}
655-
} else {
656-
data = zend_hash_add_new(ht, Z_STR(key), &d);
644+
data = zend_hash_lookup(ht, Z_STR(key));
645+
if (Z_TYPE_P(data) == IS_INDIRECT) {
646+
goto declared_property;
647+
} else if (UNEXPECTED(Z_TYPE_P(data) != IS_NULL)) {
648+
var_push_dtor_value(var_hash, data);
649+
ZVAL_NULL(data);
657650
}
658651
}
659652
}
@@ -667,7 +660,7 @@ second_try:
667660
goto failure;
668661
}
669662

670-
if (!php_var_unserialize_internal(data, p, max, var_hash, 0)) {
663+
if (!php_var_unserialize_internal(data, p, max, var_hash)) {
671664
if (info && Z_ISREF_P(data)) {
672665
/* Add type source even if we failed to unserialize.
673666
* The data is still stored in the property. */
@@ -697,10 +690,6 @@ second_try:
697690
}
698691
}
699692

700-
if (BG(unserialize).level > 1) {
701-
var_push_dtor(var_hash, data);
702-
}
703-
704693
if (elements && *(*p-1) != ';' && *(*p-1) != '}') {
705694
(*p)--;
706695
goto failure;
@@ -834,7 +823,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
834823
zend_long orig_used_slots = orig_var_entries ? orig_var_entries->used_slots : 0;
835824
int result;
836825

837-
result = php_var_unserialize_internal(UNSERIALIZE_PASSTHRU, 0);
826+
result = php_var_unserialize_internal(UNSERIALIZE_PASSTHRU);
838827

839828
if (!result) {
840829
/* If the unserialization failed, mark all elements that have been added to var_hash
@@ -855,7 +844,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
855844
return result;
856845
}
857846

858-
static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER, int as_key)
847+
static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER)
859848
{
860849
const unsigned char *cursor, *limit, *marker, *start;
861850
zval *rval_ref;
@@ -886,7 +875,7 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER, int as_key)
886875
return 0;
887876
}
888877
889-
if (Z_ISUNDEF_P(rval_ref) || (Z_ISREF_P(rval_ref) && Z_ISUNDEF_P(Z_REFVAL_P(rval_ref)))) {
878+
if (rval_ref == rval || (Z_ISREF_P(rval_ref) && Z_REFVAL_P(rval_ref) == rval)) {
890879
return 0;
891880
}
892881
@@ -1028,7 +1017,8 @@ use_double:
10281017
YYCURSOR += 2;
10291018
*p = YYCURSOR;
10301019
1031-
if (as_key) {
1020+
if (!var_hash) {
1021+
/* Array or object key unserialization */
10321022
ZVAL_STR(rval, zend_string_init_interned(str, len, 0));
10331023
} else {
10341024
ZVAL_STRINGL_FAST(rval, str, len);

0 commit comments

Comments
 (0)