Skip to content

Commit 22d3eb3

Browse files
committed
Add zend_hash_splice
This implements the original functionality of php_splice, but as an in-place operation, thus avoiding copying the HT. This is much faster (~10x) if the splice removes a small portion of the array and doesn't insert many elements.
1 parent 1aa8719 commit 22d3eb3

File tree

4 files changed

+125
-168
lines changed

4 files changed

+125
-168
lines changed

Zend/zend_hash.c

Lines changed: 91 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,24 @@
2929
(element)->pNext->pLast = (element); \
3030
}
3131

32-
#define CONNECT_TO_GLOBAL_DLLIST(element, ht) \
33-
(element)->pListLast = (ht)->pListTail; \
34-
(ht)->pListTail = (element); \
35-
(element)->pListNext = NULL; \
36-
if ((element)->pListLast != NULL) { \
37-
(element)->pListLast->pListNext = (element); \
38-
} \
39-
if (!(ht)->pListHead) { \
32+
#define CONNECT_TO_GLOBAL_DLLIST_EX(element, ht, last, next)\
33+
(element)->pListLast = (last); \
34+
(element)->pListNext = (next); \
35+
if ((last) != NULL) { \
36+
(last)->pListNext = (element); \
37+
} else { \
4038
(ht)->pListHead = (element); \
4139
} \
42-
if ((ht)->pInternalPointer == NULL) { \
43-
(ht)->pInternalPointer = (element); \
40+
if ((next) != NULL) { \
41+
(next)->pListLast = (element); \
42+
} else { \
43+
(ht)->pListTail = (element); \
44+
} \
45+
46+
#define CONNECT_TO_GLOBAL_DLLIST(element, ht) \
47+
CONNECT_TO_GLOBAL_DLLIST_EX(element, ht, (ht)->pListTail, (Bucket *) NULL); \
48+
if ((ht)->pInternalPointer == NULL) { \
49+
(ht)->pInternalPointer = (element); \
4450
}
4551

4652
#if ZEND_DEBUG
@@ -122,13 +128,13 @@ ZEND_API ulong zend_hash_func(const char *arKey, uint nKeyLength)
122128
memcpy((p)->pData, pData, nDataSize); \
123129
}
124130

125-
#define INIT_DATA(ht, p, pData, nDataSize); \
131+
#define INIT_DATA(ht, p, _pData, nDataSize); \
126132
if (nDataSize == sizeof(void*)) { \
127-
memcpy(&(p)->pDataPtr, pData, sizeof(void *)); \
133+
memcpy(&(p)->pDataPtr, (_pData), sizeof(void *)); \
128134
(p)->pData = &(p)->pDataPtr; \
129135
} else { \
130136
(p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent);\
131-
memcpy((p)->pData, pData, nDataSize); \
137+
memcpy((p)->pData, (_pData), nDataSize); \
132138
(p)->pDataPtr=NULL; \
133139
}
134140

@@ -1246,21 +1252,12 @@ ZEND_API int zend_hash_update_current_key_ex(HashTable *ht, int key_type, const
12461252
q->pData = p->pData;
12471253
}
12481254
q->pDataPtr = p->pDataPtr;
1249-
q->pListNext = p->pListNext;
1250-
q->pListLast = p->pListLast;
1251-
if (q->pListNext) {
1252-
p->pListNext->pListLast = q;
1253-
} else {
1254-
ht->pListTail = q;
1255-
}
1256-
if (q->pListLast) {
1257-
p->pListLast->pListNext = q;
1258-
} else {
1259-
ht->pListHead = q;
1260-
}
1255+
1256+
CONNECT_TO_GLOBAL_DLLIST_EX(q, ht, p->pListLast, p->pListNext);
12611257
if (ht->pInternalPointer == p) {
12621258
ht->pInternalPointer = q;
12631259
}
1260+
12641261
if (pos) {
12651262
*pos = q;
12661263
}
@@ -1291,6 +1288,75 @@ ZEND_API int zend_hash_update_current_key_ex(HashTable *ht, int key_type, const
12911288
}
12921289
}
12931290

1291+
/* Performs an in-place splice operation on a hashtable:
1292+
* The elements between offset and offset+length are removed and the elements in list[list_count]
1293+
* are inserted in their place. The removed elements can be optionally collected into a hashtable.
1294+
* This operation reindexes the hashtable, i.e. integer keys will be zero-based and sequential,
1295+
* while string keys stay intact. The same applies to the elements inserted into the removed HT. */
1296+
ZEND_API void _zend_hash_splice(HashTable *ht, uint nDataSize, copy_ctor_func_t pCopyConstructor, uint offset, uint length, void **list, uint list_count, HashTable *removed ZEND_FILE_LINE_DC) /* {{{ */
1297+
{
1298+
int pos;
1299+
Bucket *p;
1300+
1301+
IS_CONSISTENT(ht);
1302+
CHECK_INIT(ht);
1303+
1304+
/* Skip all elements until offset */
1305+
for (pos = 0, p = ht->pListHead; pos < offset && p; pos++, p = p->pListNext);
1306+
1307+
while (pos < offset + length && p) {
1308+
/* Copy removed element into HT, if it was specified */
1309+
if (removed != NULL) {
1310+
void *new_entry;
1311+
1312+
if (p->nKeyLength == 0) {
1313+
zend_hash_next_index_insert(removed, p->pData, sizeof(zval *), &new_entry);
1314+
} else {
1315+
zend_hash_quick_update(removed, p->arKey, p->nKeyLength, p->h, p->pData, sizeof(zval *), &new_entry);
1316+
}
1317+
1318+
if (pCopyConstructor) {
1319+
pCopyConstructor(new_entry);
1320+
}
1321+
}
1322+
1323+
/* Remove element */
1324+
{
1325+
Bucket *p_next = p->pListNext;
1326+
zend_hash_bucket_delete(ht, p);
1327+
p = p_next;
1328+
}
1329+
1330+
pos++;
1331+
}
1332+
1333+
if (list != NULL) {
1334+
int i;
1335+
for (i = 0; i < list_count; i++) {
1336+
/* Add new element only to the global linked list, not the bucket list.
1337+
* Also use key 0 for everything, as we'll reindex the hashtable anyways. */
1338+
Bucket *q = pemalloc_rel(sizeof(Bucket), ht->persistent);
1339+
q->arKey = NULL;
1340+
q->nKeyLength = 0;
1341+
q->h = 0;
1342+
INIT_DATA(ht, q, list[i], nDataSize);
1343+
1344+
CONNECT_TO_GLOBAL_DLLIST_EX(q, ht, p ? p->pListLast : ht->pListTail, p);
1345+
1346+
ht->nNumOfElements++;
1347+
1348+
if (pCopyConstructor) {
1349+
pCopyConstructor(q->pData);
1350+
}
1351+
}
1352+
1353+
ZEND_HASH_IF_FULL_DO_RESIZE(ht);
1354+
}
1355+
1356+
zend_hash_reindex(ht);
1357+
}
1358+
/* }}} */
1359+
12941360
ZEND_API int zend_hash_sort(HashTable *ht, sort_func_t sort_func,
12951361
compare_func_t compar, int renumber TSRMLS_DC)
12961362
{

Zend/zend_hash.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,10 @@ ZEND_API int zend_hash_num_elements(const HashTable *ht);
229229
ZEND_API int zend_hash_rehash(HashTable *ht);
230230
ZEND_API void zend_hash_reindex(HashTable *ht);
231231

232+
ZEND_API void _zend_hash_splice(HashTable *ht, uint nDataSize, copy_ctor_func_t pCopyConstructor, uint offset, uint length, void **list, uint list_count, HashTable *removed ZEND_FILE_LINE_DC);
233+
#define zend_hash_splice(ht, nDataSize, pCopyConstructor, offset, length, list, list_count, removed) \
234+
_zend_hash_splice(ht, nDataSize, pCopyConstructor, offset, length, list, list_count, removed ZEND_FILE_LINE_CC)
235+
232236
/*
233237
* DJBX33A (Daniel J. Bernstein, Times 33 with Addition)
234238
*

0 commit comments

Comments
 (0)