Skip to content

Commit 37466b0

Browse files
committed
Use special flag (HASH_FLAG_INITIALIZED) instead of (nTableMask == 0) to indicate that HashTable is allocated.
Make nTableMask to be 0 for packed arrays. Remove checks fo HASH_FLAG_PACKED in zend_hash_find/zend_hash_del and family (string keys are resolved through uninitialized_bucket). Change HashTable layout for better locality.
1 parent e4ef6bf commit 37466b0

File tree

6 files changed

+46
-75
lines changed

6 files changed

+46
-75
lines changed

Zend/zend_hash.c

Lines changed: 27 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -82,18 +82,19 @@ static void _zend_is_inconsistent(const HashTable *ht, const char *file, int lin
8282

8383
static void zend_hash_do_resize(HashTable *ht);
8484

85-
#define CHECK_INIT(ht, packed) do { \
86-
if (UNEXPECTED((ht)->nTableMask == 0)) { \
85+
#define CHECK_INIT(ht, packed) do { \
86+
if (UNEXPECTED(!((ht)->u.flags & HASH_FLAG_INITIALIZED))) { \
8787
if (packed) { \
88-
(ht)->arData = (Bucket *) safe_pemalloc((ht)->nTableSize, sizeof(Bucket), 0, (ht)->u.flags & HASH_FLAG_PERSISTENT); \
89-
(ht)->u.flags |= HASH_FLAG_PACKED; \
88+
(ht)->u.flags |= HASH_FLAG_INITIALIZED | HASH_FLAG_PACKED; \
89+
(ht)->arData = (Bucket *) safe_pemalloc((ht)->nTableSize, sizeof(Bucket), 0, (ht)->u.flags & HASH_FLAG_PERSISTENT); \
9090
} else { \
91-
(ht)->arData = (Bucket *) safe_pemalloc((ht)->nTableSize, sizeof(Bucket) + sizeof(uint32_t), 0, (ht)->u.flags & HASH_FLAG_PERSISTENT); \
92-
(ht)->arHash = (uint32_t*)((ht)->arData + (ht)->nTableSize); \
93-
memset((ht)->arHash, INVALID_IDX, (ht)->nTableSize * sizeof(uint32_t)); \
91+
(ht)->u.flags |= HASH_FLAG_INITIALIZED; \
92+
(ht)->nTableMask = (ht)->nTableSize - 1; \
93+
(ht)->arData = (Bucket *) safe_pemalloc((ht)->nTableSize, sizeof(Bucket) + sizeof(uint32_t), 0, (ht)->u.flags & HASH_FLAG_PERSISTENT); \
94+
(ht)->arHash = (uint32_t*)((ht)->arData + (ht)->nTableSize); \
95+
memset((ht)->arHash, INVALID_IDX, (ht)->nTableSize * sizeof(uint32_t)); \
9496
} \
95-
(ht)->nTableMask = (ht)->nTableSize - 1; \
96-
} \
97+
} \
9798
} while (0)
9899

99100
static const uint32_t uninitialized_bucket = {INVALID_IDX};
@@ -129,7 +130,7 @@ ZEND_API void _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestru
129130
# endif
130131
#endif
131132

132-
ht->nTableMask = 0; /* 0 means that ht->arBuckets is uninitialized */
133+
ht->nTableMask = 0;
133134
ht->nNumUsed = 0;
134135
ht->nNumOfElements = 0;
135136
ht->nNextFreeElement = 0;
@@ -145,7 +146,6 @@ static void zend_hash_packed_grow(HashTable *ht)
145146
HANDLE_BLOCK_INTERRUPTIONS();
146147
ht->arData = (Bucket *) safe_perealloc(ht->arData, (ht->nTableSize << 1), sizeof(Bucket), 0, ht->u.flags & HASH_FLAG_PERSISTENT);
147148
ht->nTableSize = (ht->nTableSize << 1);
148-
ht->nTableMask = ht->nTableSize - 1;
149149
HANDLE_UNBLOCK_INTERRUPTIONS();
150150
}
151151

@@ -170,6 +170,7 @@ ZEND_API void zend_hash_to_packed(HashTable *ht)
170170
{
171171
HANDLE_BLOCK_INTERRUPTIONS();
172172
ht->u.flags |= HASH_FLAG_PACKED;
173+
ht->nTableMask = 0;
173174
ht->arData = erealloc(ht->arData, ht->nTableSize * sizeof(Bucket));
174175
ht->arHash = (uint32_t*)&uninitialized_bucket;
175176
HANDLE_UNBLOCK_INTERRUPTIONS();
@@ -267,7 +268,7 @@ static zend_always_inline zval *_zend_hash_add_or_update_i(HashTable *ht, zend_s
267268

268269
IS_CONSISTENT(ht);
269270

270-
if (UNEXPECTED(ht->nTableMask == 0)) {
271+
if (UNEXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) {
271272
CHECK_INIT(ht, 0);
272273
goto add_to_hash;
273274
} else if (ht->u.flags & HASH_FLAG_PACKED) {
@@ -418,7 +419,7 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht,
418419

419420
IS_CONSISTENT(ht);
420421

421-
if (UNEXPECTED(ht->nTableMask == 0)) {
422+
if (UNEXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) {
422423
CHECK_INIT(ht, h < ht->nTableSize);
423424
if (h < ht->nTableSize) {
424425
p = ht->arData + h;
@@ -587,7 +588,7 @@ ZEND_API int zend_hash_rehash(HashTable *ht)
587588
IS_CONSISTENT(ht);
588589

589590
if (UNEXPECTED(ht->nNumOfElements == 0)) {
590-
if (ht->nTableMask) {
591+
if (ht->u.flags & HASH_FLAG_INITIALIZED) {
591592
memset(ht->arHash, INVALID_IDX, ht->nTableSize * sizeof(uint32_t));
592593
}
593594
return SUCCESS;
@@ -684,10 +685,6 @@ ZEND_API int zend_hash_del(HashTable *ht, zend_string *key)
684685

685686
IS_CONSISTENT(ht);
686687

687-
if (ht->u.flags & HASH_FLAG_PACKED) {
688-
return FAILURE;
689-
}
690-
691688
h = zend_string_hash_val(key);
692689
nIndex = h & ht->nTableMask;
693690

@@ -718,10 +715,6 @@ ZEND_API int zend_hash_del_ind(HashTable *ht, zend_string *key)
718715

719716
IS_CONSISTENT(ht);
720717

721-
if (ht->u.flags & HASH_FLAG_PACKED) {
722-
return FAILURE;
723-
}
724-
725718
h = zend_string_hash_val(key);
726719
nIndex = h & ht->nTableMask;
727720

@@ -765,10 +758,6 @@ ZEND_API int zend_hash_str_del(HashTable *ht, const char *str, size_t len)
765758

766759
IS_CONSISTENT(ht);
767760

768-
if (ht->u.flags & HASH_FLAG_PACKED) {
769-
return FAILURE;
770-
}
771-
772761
h = zend_inline_hash_func(str, len);
773762
nIndex = h & ht->nTableMask;
774763

@@ -905,7 +894,7 @@ ZEND_API void zend_hash_destroy(HashTable *ht)
905894
} while (++p != end);
906895
}
907896
}
908-
} else if (EXPECTED(!ht->nTableMask)) {
897+
} else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) {
909898
return;
910899
}
911900
pefree(ht->arData, ht->u.flags & HASH_FLAG_PERSISTENT);
@@ -947,7 +936,7 @@ ZEND_API void zend_array_destroy(HashTable *ht)
947936
}
948937

949938
SET_INCONSISTENT(HT_DESTROYED);
950-
} else if (EXPECTED(!ht->nTableMask)) {
939+
} else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) {
951940
return;
952941
}
953942
pefree(ht->arData, ht->u.flags & HASH_FLAG_PERSISTENT);
@@ -990,16 +979,14 @@ ZEND_API void zend_hash_clean(HashTable *ht)
990979
} while (++p != end);
991980
}
992981
}
982+
if (!(ht->u.flags & HASH_FLAG_PACKED)) {
983+
memset(ht->arHash, INVALID_IDX, ht->nTableSize * sizeof(uint32_t));
984+
}
993985
}
994986
ht->nNumUsed = 0;
995987
ht->nNumOfElements = 0;
996988
ht->nNextFreeElement = 0;
997989
ht->nInternalPointer = INVALID_IDX;
998-
if (ht->nTableMask) {
999-
if (!(ht->u.flags & HASH_FLAG_PACKED)) {
1000-
memset(ht->arHash, INVALID_IDX, ht->nTableSize * sizeof(uint32_t));
1001-
}
1002-
}
1003990
}
1004991

1005992
ZEND_API void zend_symtable_clean(HashTable *ht)
@@ -1019,16 +1006,14 @@ ZEND_API void zend_symtable_clean(HashTable *ht)
10191006
}
10201007
}
10211008
} while (++p != end);
1009+
if (!(ht->u.flags & HASH_FLAG_PACKED)) {
1010+
memset(ht->arHash, INVALID_IDX, ht->nTableSize * sizeof(uint32_t));
1011+
}
10221012
}
10231013
ht->nNumUsed = 0;
10241014
ht->nNumOfElements = 0;
10251015
ht->nNextFreeElement = 0;
10261016
ht->nInternalPointer = INVALID_IDX;
1027-
if (ht->nTableMask) {
1028-
if (!(ht->u.flags & HASH_FLAG_PACKED)) {
1029-
memset(ht->arHash, INVALID_IDX, ht->nTableSize * sizeof(uint32_t));
1030-
}
1031-
}
10321017
}
10331018

10341019
ZEND_API void zend_hash_graceful_destroy(HashTable *ht)
@@ -1043,7 +1028,7 @@ ZEND_API void zend_hash_graceful_destroy(HashTable *ht)
10431028
if (Z_TYPE(p->val) == IS_UNDEF) continue;
10441029
_zend_hash_del_el(ht, idx, p);
10451030
}
1046-
if (ht->nTableMask) {
1031+
if (ht->u.flags & HASH_FLAG_INITIALIZED) {
10471032
pefree(ht->arData, ht->u.flags & HASH_FLAG_PERSISTENT);
10481033
}
10491034

@@ -1065,7 +1050,7 @@ ZEND_API void zend_hash_graceful_reverse_destroy(HashTable *ht)
10651050
_zend_hash_del_el(ht, idx, p);
10661051
}
10671052

1068-
if (ht->nTableMask) {
1053+
if (ht->u.flags & HASH_FLAG_INITIALIZED) {
10691054
pefree(ht->arData, ht->u.flags & HASH_FLAG_PERSISTENT);
10701055
}
10711056

@@ -1257,7 +1242,7 @@ ZEND_API void zend_array_dup(HashTable *target, HashTable *source)
12571242
target->u.flags = (source->u.flags & ~HASH_FLAG_PERSISTENT) | HASH_FLAG_APPLY_PROTECTION;
12581243

12591244
target_idx = 0;
1260-
if (target->nTableMask) {
1245+
if (target->u.flags & HASH_FLAG_INITIALIZED) {
12611246
if (target->u.flags & HASH_FLAG_PACKED) {
12621247
target->nNumUsed = source->nNumUsed;
12631248
target->nNumOfElements = source->nNumOfElements;
@@ -1448,10 +1433,6 @@ ZEND_API zval *zend_hash_find(const HashTable *ht, zend_string *key)
14481433

14491434
IS_CONSISTENT(ht);
14501435

1451-
if (ht->u.flags & HASH_FLAG_PACKED) {
1452-
return NULL;
1453-
}
1454-
14551436
p = zend_hash_find_bucket(ht, key);
14561437
return p ? &p->val : NULL;
14571438
}
@@ -1463,10 +1444,6 @@ ZEND_API zval *zend_hash_str_find(const HashTable *ht, const char *str, size_t l
14631444

14641445
IS_CONSISTENT(ht);
14651446

1466-
if (ht->u.flags & HASH_FLAG_PACKED) {
1467-
return NULL;
1468-
}
1469-
14701447
h = zend_inline_hash_func(str, len);
14711448
p = zend_hash_str_find_bucket(ht, str, len, h);
14721449
return p ? &p->val : NULL;
@@ -1478,10 +1455,6 @@ ZEND_API zend_bool zend_hash_exists(const HashTable *ht, zend_string *key)
14781455

14791456
IS_CONSISTENT(ht);
14801457

1481-
if (ht->u.flags & HASH_FLAG_PACKED) {
1482-
return 0;
1483-
}
1484-
14851458
p = zend_hash_find_bucket(ht, key);
14861459
return p ? 1 : 0;
14871460
}
@@ -1493,10 +1466,6 @@ ZEND_API zend_bool zend_hash_str_exists(const HashTable *ht, const char *str, si
14931466

14941467
IS_CONSISTENT(ht);
14951468

1496-
if (ht->u.flags & HASH_FLAG_PACKED) {
1497-
return 0;
1498-
}
1499-
15001469
h = zend_inline_hash_func(str, len);
15011470
p = zend_hash_str_find_bucket(ht, str, len, h);
15021471
return p ? 1 : 0;
@@ -1744,6 +1713,7 @@ ZEND_API int zend_hash_sort(HashTable *ht, sort_func_t sort_func,
17441713
} else {
17451714
if (renumber) {
17461715
ht->u.flags |= HASH_FLAG_PACKED;
1716+
ht->nTableMask = 0;
17471717
ht->arData = erealloc(ht->arData, ht->nTableSize * sizeof(Bucket));
17481718
ht->arHash = (uint32_t*)&uninitialized_bucket;
17491719
} else {

Zend/zend_hash.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#define HASH_FLAG_PERSISTENT (1<<0)
4040
#define HASH_FLAG_APPLY_PROTECTION (1<<1)
4141
#define HASH_FLAG_PACKED (1<<2)
42+
#define HASH_FLAG_INITIALIZED (1<<3)
4243

4344
#define HASH_MASK_CONSISTENCY 0x60
4445

Zend/zend_types.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -157,15 +157,6 @@ typedef struct _Bucket {
157157
} Bucket;
158158

159159
typedef struct _HashTable {
160-
uint32_t nTableSize;
161-
uint32_t nTableMask;
162-
uint32_t nNumUsed;
163-
uint32_t nNumOfElements;
164-
zend_long nNextFreeElement;
165-
Bucket *arData;
166-
uint32_t *arHash;
167-
dtor_func_t pDestructor;
168-
uint32_t nInternalPointer;
169160
union {
170161
struct {
171162
ZEND_ENDIAN_LOHI_3(
@@ -175,6 +166,15 @@ typedef struct _HashTable {
175166
} v;
176167
uint32_t flags;
177168
} u;
169+
uint32_t nTableSize;
170+
uint32_t nTableMask;
171+
uint32_t nNumUsed;
172+
uint32_t nNumOfElements;
173+
uint32_t nInternalPointer;
174+
zend_long nNextFreeElement;
175+
Bucket *arData;
176+
uint32_t *arHash;
177+
dtor_func_t pDestructor;
178178
} HashTable;
179179

180180
struct _zend_array {

ext/opcache/zend_accelerator_util_funcs.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -301,12 +301,12 @@ static void zend_hash_clone_zval(HashTable *ht, HashTable *source, int bind)
301301
ht->nNumOfElements = source->nNumOfElements;
302302
ht->nNextFreeElement = source->nNextFreeElement;
303303
ht->pDestructor = ZVAL_PTR_DTOR;
304-
ht->u.flags = HASH_FLAG_APPLY_PROTECTION;
304+
ht->u.flags = (source->u.flags & HASH_FLAG_INITIALIZED) | HASH_FLAG_APPLY_PROTECTION;
305305
ht->arData = NULL;
306306
ht->arHash = NULL;
307307
ht->nInternalPointer = source->nNumOfElements ? 0 : INVALID_IDX;
308308

309-
if (!ht->nTableMask) {
309+
if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
310310
ht->arHash = (uint32_t*)&uninitialized_bucket;
311311
return;
312312
}
@@ -382,10 +382,10 @@ static void zend_hash_clone_methods(HashTable *ht, HashTable *source, zend_class
382382
ht->nNumOfElements = source->nNumOfElements;
383383
ht->nNextFreeElement = source->nNextFreeElement;
384384
ht->pDestructor = ZEND_FUNCTION_DTOR;
385-
ht->u.flags = 0;
385+
ht->u.flags = (source->u.flags & HASH_FLAG_INITIALIZED);
386386
ht->nInternalPointer = source->nNumOfElements ? 0 : INVALID_IDX;
387387

388-
if (!ht->nTableMask) {
388+
if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
389389
ht->arHash = (uint32_t*)&uninitialized_bucket;
390390
return;
391391
}
@@ -458,10 +458,10 @@ static void zend_hash_clone_prop_info(HashTable *ht, HashTable *source, zend_cla
458458
ht->nNumOfElements = source->nNumOfElements;
459459
ht->nNextFreeElement = source->nNextFreeElement;
460460
ht->pDestructor = zend_destroy_property_info;
461-
ht->u.flags = 0;
461+
ht->u.flags = (source->u.flags & HASH_FLAG_INITIALIZED);
462462
ht->nInternalPointer = source->nNumOfElements ? 0 : INVALID_IDX;
463463

464-
if (!ht->nTableMask) {
464+
if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
465465
ht->arHash = (uint32_t*)&uninitialized_bucket;
466466
return;
467467
}

ext/opcache/zend_persist.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ static void zend_hash_persist(HashTable *ht, zend_persist_func_t pPersistElement
7474
uint idx;
7575
Bucket *p;
7676

77-
if (!ht->nTableMask) {
77+
if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
7878
ht->arHash = (uint32_t*)&uninitialized_bucket;
7979
return;
8080
}
@@ -111,7 +111,7 @@ static void zend_hash_persist_immutable(HashTable *ht)
111111
uint idx;
112112
Bucket *p;
113113

114-
if (!ht->nTableMask) {
114+
if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
115115
ht->arHash = (uint32_t*)&uninitialized_bucket;
116116
return;
117117
}

ext/opcache/zend_persist_calc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ static void zend_hash_persist_calc(HashTable *ht, void (*pPersistElement)(zval *
5454
uint idx;
5555
Bucket *p;
5656

57-
if (!ht->nTableMask) {
57+
if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
5858
return;
5959
}
6060
if (ht->u.flags & HASH_FLAG_PACKED) {

0 commit comments

Comments
 (0)