|
|
|
@ -2477,6 +2477,122 @@ ZEND_API int ZEND_FASTCALL _zend_handle_numeric_str_ex(const char *key, size_t l |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* Takes a "symtable" hashtable (contains integer and non-numeric string keys) |
|
|
|
* and converts it to a "proptable" (contains only string keys). |
|
|
|
* If the symtable didn't need duplicating, its refcount is incremented. |
|
|
|
*/ |
|
|
|
ZEND_API HashTable* ZEND_FASTCALL zend_symtable_to_proptable(HashTable *ht) |
|
|
|
{ |
|
|
|
zend_ulong num_key; |
|
|
|
zend_string *str_key; |
|
|
|
zval *zv; |
|
|
|
|
|
|
|
if (UNEXPECTED(HT_IS_PACKED(ht))) { |
|
|
|
goto convert; |
|
|
|
} |
|
|
|
|
|
|
|
ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, str_key, zv) { |
|
|
|
if (!str_key) { |
|
|
|
goto convert; |
|
|
|
} |
|
|
|
} ZEND_HASH_FOREACH_END(); |
|
|
|
|
|
|
|
if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { |
|
|
|
GC_REFCOUNT(ht)++; |
|
|
|
} |
|
|
|
|
|
|
|
return ht; |
|
|
|
|
|
|
|
convert: |
|
|
|
{ |
|
|
|
HashTable *new_ht = emalloc(sizeof(HashTable)); |
|
|
|
|
|
|
|
zend_hash_init(new_ht, zend_hash_num_elements(ht), NULL, ZVAL_PTR_DTOR, 0); |
|
|
|
|
|
|
|
ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, str_key, zv) { |
|
|
|
if (!str_key) { |
|
|
|
str_key = zend_long_to_str(num_key); |
|
|
|
zend_string_delref(str_key); |
|
|
|
} |
|
|
|
do { |
|
|
|
if (Z_OPT_REFCOUNTED_P(zv)) { |
|
|
|
if (Z_ISREF_P(zv) && Z_REFCOUNT_P(zv) == 1) { |
|
|
|
zv = Z_REFVAL_P(zv); |
|
|
|
if (!Z_OPT_REFCOUNTED_P(zv)) { |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
Z_ADDREF_P(zv); |
|
|
|
} |
|
|
|
} while (0); |
|
|
|
zend_hash_update(new_ht, str_key, zv); |
|
|
|
} ZEND_HASH_FOREACH_END(); |
|
|
|
|
|
|
|
return new_ht; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* Takes a "proptable" hashtable (contains only string keys) and converts it to |
|
|
|
* a "symtable" (contains integer and non-numeric string keys). |
|
|
|
* If the proptable didn't need duplicating, its refcount is incremented. |
|
|
|
*/ |
|
|
|
ZEND_API HashTable* ZEND_FASTCALL zend_proptable_to_symtable(HashTable *ht, zend_bool always_duplicate) |
|
|
|
{ |
|
|
|
zend_ulong num_key; |
|
|
|
zend_string *str_key; |
|
|
|
zval *zv; |
|
|
|
|
|
|
|
ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, str_key, zv) { |
|
|
|
/* The `str_key &&` here might seem redundant: property tables should |
|
|
|
* only have string keys. Unfortunately, this isn't true, at the very |
|
|
|
* least because of ArrayObject, which stores a symtable where the |
|
|
|
* property table should be. |
|
|
|
*/ |
|
|
|
if (str_key && ZEND_HANDLE_NUMERIC(str_key, num_key)) { |
|
|
|
goto convert; |
|
|
|
} |
|
|
|
} ZEND_HASH_FOREACH_END(); |
|
|
|
|
|
|
|
if (always_duplicate) { |
|
|
|
return zend_array_dup(ht); |
|
|
|
} |
|
|
|
|
|
|
|
if (EXPECTED(!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE))) { |
|
|
|
GC_REFCOUNT(ht)++; |
|
|
|
} |
|
|
|
|
|
|
|
return ht; |
|
|
|
|
|
|
|
convert: |
|
|
|
{ |
|
|
|
HashTable *new_ht = emalloc(sizeof(HashTable)); |
|
|
|
|
|
|
|
zend_hash_init(new_ht, zend_hash_num_elements(ht), NULL, ZVAL_PTR_DTOR, 0); |
|
|
|
|
|
|
|
ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, str_key, zv) { |
|
|
|
do { |
|
|
|
if (Z_OPT_REFCOUNTED_P(zv)) { |
|
|
|
if (Z_ISREF_P(zv) && Z_REFCOUNT_P(zv) == 1) { |
|
|
|
zv = Z_REFVAL_P(zv); |
|
|
|
if (!Z_OPT_REFCOUNTED_P(zv)) { |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
Z_ADDREF_P(zv); |
|
|
|
} |
|
|
|
} while (0); |
|
|
|
/* Again, thank ArrayObject for `!str_key ||`. */ |
|
|
|
if (!str_key || ZEND_HANDLE_NUMERIC(str_key, num_key)) { |
|
|
|
zend_hash_index_update(new_ht, num_key, zv); |
|
|
|
} else { |
|
|
|
zend_hash_update(new_ht, str_key, zv); |
|
|
|
} |
|
|
|
} ZEND_HASH_FOREACH_END(); |
|
|
|
|
|
|
|
return new_ht; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
* Local variables: |
|
|
|
* tab-width: 4 |
|
|
|
|