|
29 | 29 | (element)->pNext->pLast = (element); \
|
30 | 30 | }
|
31 | 31 |
|
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 { \ |
40 | 38 | (ht)->pListHead = (element); \
|
41 | 39 | } \
|
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); \ |
44 | 50 | }
|
45 | 51 |
|
46 | 52 | #if ZEND_DEBUG
|
@@ -122,13 +128,13 @@ ZEND_API ulong zend_hash_func(const char *arKey, uint nKeyLength)
|
122 | 128 | memcpy((p)->pData, pData, nDataSize); \
|
123 | 129 | }
|
124 | 130 |
|
125 |
| -#define INIT_DATA(ht, p, pData, nDataSize); \ |
| 131 | +#define INIT_DATA(ht, p, _pData, nDataSize); \ |
126 | 132 | if (nDataSize == sizeof(void*)) { \
|
127 |
| - memcpy(&(p)->pDataPtr, pData, sizeof(void *)); \ |
| 133 | + memcpy(&(p)->pDataPtr, (_pData), sizeof(void *)); \ |
128 | 134 | (p)->pData = &(p)->pDataPtr; \
|
129 | 135 | } else { \
|
130 | 136 | (p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent);\
|
131 |
| - memcpy((p)->pData, pData, nDataSize); \ |
| 137 | + memcpy((p)->pData, (_pData), nDataSize); \ |
132 | 138 | (p)->pDataPtr=NULL; \
|
133 | 139 | }
|
134 | 140 |
|
@@ -1246,21 +1252,12 @@ ZEND_API int zend_hash_update_current_key_ex(HashTable *ht, int key_type, const
|
1246 | 1252 | q->pData = p->pData;
|
1247 | 1253 | }
|
1248 | 1254 | 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); |
1261 | 1257 | if (ht->pInternalPointer == p) {
|
1262 | 1258 | ht->pInternalPointer = q;
|
1263 | 1259 | }
|
| 1260 | + |
1264 | 1261 | if (pos) {
|
1265 | 1262 | *pos = q;
|
1266 | 1263 | }
|
@@ -1291,6 +1288,75 @@ ZEND_API int zend_hash_update_current_key_ex(HashTable *ht, int key_type, const
|
1291 | 1288 | }
|
1292 | 1289 | }
|
1293 | 1290 |
|
| 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 | + |
1294 | 1360 | ZEND_API int zend_hash_sort(HashTable *ht, sort_func_t sort_func,
|
1295 | 1361 | compare_func_t compar, int renumber TSRMLS_DC)
|
1296 | 1362 | {
|
|
0 commit comments