xref: /PHP-5.6/Zend/zend_hash.c (revision 3537e95d)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend Engine                                                          |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1998-2016 Zend Technologies Ltd. (http://www.zend.com) |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 2.00 of the Zend license,     |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.zend.com/license/2_00.txt.                                |
11    | If you did not receive a copy of the Zend license and are unable to  |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@zend.com so we can mail you a copy immediately.              |
14    +----------------------------------------------------------------------+
15    | Authors: Andi Gutmans <andi@zend.com>                                |
16    |          Zeev Suraski <zeev@zend.com>                                |
17    +----------------------------------------------------------------------+
18 */
19 
20 /* $Id$ */
21 
22 #include "zend.h"
23 #include "zend_globals.h"
24 
25 #define CONNECT_TO_BUCKET_DLLIST(element, list_head)		\
26 	(element)->pNext = (list_head);							\
27 	(element)->pLast = NULL;								\
28 	if ((element)->pNext) {									\
29 		(element)->pNext->pLast = (element);				\
30 	}
31 
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 {												\
38 		(ht)->pListHead = (element);						\
39 	}														\
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);										\
50 	}
51 
52 #if ZEND_DEBUG
53 #define HT_OK				0
54 #define HT_IS_DESTROYING	1
55 #define HT_DESTROYED		2
56 #define HT_CLEANING			3
57 
_zend_is_inconsistent(const HashTable * ht,const char * file,int line)58 static void _zend_is_inconsistent(const HashTable *ht, const char *file, int line)
59 {
60 	if (ht->inconsistent==HT_OK) {
61 		return;
62 	}
63 	switch (ht->inconsistent) {
64 		case HT_IS_DESTROYING:
65 			zend_output_debug_string(1, "%s(%d) : ht=%p is being destroyed", file, line, ht);
66 			break;
67 		case HT_DESTROYED:
68 			zend_output_debug_string(1, "%s(%d) : ht=%p is already destroyed", file, line, ht);
69 			break;
70 		case HT_CLEANING:
71 			zend_output_debug_string(1, "%s(%d) : ht=%p is being cleaned", file, line, ht);
72 			break;
73 		default:
74 			zend_output_debug_string(1, "%s(%d) : ht=%p is inconsistent", file, line, ht);
75 			break;
76 	}
77 	zend_bailout();
78 }
79 #define IS_CONSISTENT(a) _zend_is_inconsistent(a, __FILE__, __LINE__);
80 #define SET_INCONSISTENT(n) ht->inconsistent = n;
81 #else
82 #define IS_CONSISTENT(a)
83 #define SET_INCONSISTENT(n)
84 #endif
85 
86 #define HASH_PROTECT_RECURSION(ht)														\
87 	if ((ht)->bApplyProtection) {														\
88 		if ((ht)->nApplyCount++ >= 3) {													\
89 			zend_error(E_ERROR, "Nesting level too deep - recursive dependency?");		\
90 		}																				\
91 	}
92 
93 
94 #define HASH_UNPROTECT_RECURSION(ht)													\
95 	if ((ht)->bApplyProtection) {														\
96 		(ht)->nApplyCount--;															\
97 	}
98 
99 
100 #define ZEND_HASH_IF_FULL_DO_RESIZE(ht)				\
101 	if ((ht)->nNumOfElements > (ht)->nTableSize) {	\
102 		zend_hash_do_resize(ht);					\
103 	}
104 
105 static void zend_hash_do_resize(HashTable *ht);
106 
zend_hash_func(const char * arKey,uint nKeyLength)107 ZEND_API ulong zend_hash_func(const char *arKey, uint nKeyLength)
108 {
109 	return zend_inline_hash_func(arKey, nKeyLength);
110 }
111 
112 
113 #define UPDATE_DATA(ht, p, pData, nDataSize)											\
114 	if (nDataSize == sizeof(void*)) {													\
115 		if ((p)->pData != &(p)->pDataPtr) {												\
116 			pefree_rel((p)->pData, (ht)->persistent);									\
117 		}																				\
118 		memcpy(&(p)->pDataPtr, pData, sizeof(void *));									\
119 		(p)->pData = &(p)->pDataPtr;													\
120 	} else {																			\
121 		if ((p)->pData == &(p)->pDataPtr) {												\
122 			(p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent);			\
123 			(p)->pDataPtr=NULL;															\
124 		} else {																		\
125 			(p)->pData = (void *) perealloc_rel((p)->pData, nDataSize, (ht)->persistent);	\
126 			/* (p)->pDataPtr is already NULL so no need to initialize it */				\
127 		}																				\
128 		memcpy((p)->pData, pData, nDataSize);											\
129 	}
130 
131 #define INIT_DATA(ht, p, _pData, nDataSize);								\
132 	if (nDataSize == sizeof(void*)) {									\
133 		memcpy(&(p)->pDataPtr, (_pData), sizeof(void *));					\
134 		(p)->pData = &(p)->pDataPtr;									\
135 	} else {															\
136 		(p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent);\
137 		memcpy((p)->pData, (_pData), nDataSize);							\
138 		(p)->pDataPtr=NULL;												\
139 	}
140 
141 #define CHECK_INIT(ht) do {												\
142 	if (UNEXPECTED((ht)->nTableMask == 0)) {								\
143 		(ht)->arBuckets = (Bucket **) pecalloc((ht)->nTableSize, sizeof(Bucket *), (ht)->persistent);	\
144 		(ht)->nTableMask = (ht)->nTableSize - 1;						\
145 	}																	\
146 } while (0)
147 
148 static const Bucket *uninitialized_bucket = NULL;
149 
i_zend_hash_bucket_delete(HashTable * ht,Bucket * p)150 static zend_always_inline void i_zend_hash_bucket_delete(HashTable *ht, Bucket *p)
151 {
152 #ifdef ZEND_SIGNALS
153 	TSRMLS_FETCH();
154 #endif
155 
156 	HANDLE_BLOCK_INTERRUPTIONS();
157 	if (p->pLast) {
158 		p->pLast->pNext = p->pNext;
159 	} else {
160 		ht->arBuckets[p->h & ht->nTableMask] = p->pNext;
161 	}
162 	if (p->pNext) {
163 		p->pNext->pLast = p->pLast;
164 	}
165 	if (p->pListLast != NULL) {
166 		p->pListLast->pListNext = p->pListNext;
167 	} else {
168 		/* Deleting the head of the list */
169 		ht->pListHead = p->pListNext;
170 	}
171 	if (p->pListNext != NULL) {
172 		p->pListNext->pListLast = p->pListLast;
173 	} else {
174 		/* Deleting the tail of the list */
175 		ht->pListTail = p->pListLast;
176 	}
177 	if (ht->pInternalPointer == p) {
178 		ht->pInternalPointer = p->pListNext;
179 	}
180 	ht->nNumOfElements--;
181 	if (ht->pDestructor) {
182 		ht->pDestructor(p->pData);
183 	}
184 	if (p->pData != &p->pDataPtr) {
185 		pefree(p->pData, ht->persistent);
186 	}
187 	pefree(p, ht->persistent);
188 	HANDLE_UNBLOCK_INTERRUPTIONS();
189 }
190 
zend_hash_bucket_delete(HashTable * ht,Bucket * p)191 static void zend_hash_bucket_delete(HashTable *ht, Bucket *p) {
192 	i_zend_hash_bucket_delete(ht, p);
193 }
194 
_zend_hash_init(HashTable * ht,uint nSize,dtor_func_t pDestructor,zend_bool persistent ZEND_FILE_LINE_DC)195 ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC)
196 {
197 	uint i = 3;
198 
199 	SET_INCONSISTENT(HT_OK);
200 
201 	if (nSize >= 0x80000000) {
202 		/* prevent overflow */
203 		ht->nTableSize = 0x80000000;
204 	} else {
205 		while ((1U << i) < nSize) {
206 			i++;
207 		}
208 		ht->nTableSize = 1 << i;
209 	}
210 
211 	ht->nTableMask = 0;	/* 0 means that ht->arBuckets is uninitialized */
212 	ht->pDestructor = pDestructor;
213 	ht->arBuckets = (Bucket**)&uninitialized_bucket;
214 	ht->pListHead = NULL;
215 	ht->pListTail = NULL;
216 	ht->nNumOfElements = 0;
217 	ht->nNextFreeElement = 0;
218 	ht->pInternalPointer = NULL;
219 	ht->persistent = persistent;
220 	ht->nApplyCount = 0;
221 	ht->bApplyProtection = 1;
222 	return SUCCESS;
223 }
224 
225 
_zend_hash_init_ex(HashTable * ht,uint nSize,dtor_func_t pDestructor,zend_bool persistent,zend_bool bApplyProtection ZEND_FILE_LINE_DC)226 ZEND_API int _zend_hash_init_ex(HashTable *ht, uint nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection ZEND_FILE_LINE_DC)
227 {
228 	int retval = _zend_hash_init(ht, nSize, pDestructor, persistent ZEND_FILE_LINE_CC);
229 
230 	ht->bApplyProtection = bApplyProtection;
231 	return retval;
232 }
233 
234 
zend_hash_set_apply_protection(HashTable * ht,zend_bool bApplyProtection)235 ZEND_API void zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProtection)
236 {
237 	ht->bApplyProtection = bApplyProtection;
238 }
239 
240 
241 
_zend_hash_add_or_update(HashTable * ht,const char * arKey,uint nKeyLength,void * pData,uint nDataSize,void ** pDest,int flag ZEND_FILE_LINE_DC)242 ZEND_API int _zend_hash_add_or_update(HashTable *ht, const char *arKey, uint nKeyLength, void *pData, uint nDataSize, void **pDest, int flag ZEND_FILE_LINE_DC)
243 {
244 	ulong h;
245 	uint nIndex;
246 	Bucket *p;
247 #ifdef ZEND_SIGNALS
248 	TSRMLS_FETCH();
249 #endif
250 
251 	IS_CONSISTENT(ht);
252 
253 	ZEND_ASSERT(nKeyLength != 0);
254 
255 	CHECK_INIT(ht);
256 
257 	h = zend_inline_hash_func(arKey, nKeyLength);
258 	nIndex = h & ht->nTableMask;
259 
260 	p = ht->arBuckets[nIndex];
261 	while (p != NULL) {
262 		if (p->arKey == arKey ||
263 			((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
264 				if (flag & HASH_ADD) {
265 					return FAILURE;
266 				}
267 				ZEND_ASSERT(p->pData != pData);
268 				HANDLE_BLOCK_INTERRUPTIONS();
269 				if (ht->pDestructor) {
270 					ht->pDestructor(p->pData);
271 				}
272 				UPDATE_DATA(ht, p, pData, nDataSize);
273 				if (pDest) {
274 					*pDest = p->pData;
275 				}
276 				HANDLE_UNBLOCK_INTERRUPTIONS();
277 				return SUCCESS;
278 		}
279 		p = p->pNext;
280 	}
281 
282 	if (IS_INTERNED(arKey)) {
283 		p = (Bucket *) pemalloc(sizeof(Bucket), ht->persistent);
284 		p->arKey = arKey;
285 	} else {
286 		p = (Bucket *) pemalloc(sizeof(Bucket) + nKeyLength, ht->persistent);
287 		p->arKey = (const char*)(p + 1);
288 		memcpy((char*)p->arKey, arKey, nKeyLength);
289 	}
290 	p->nKeyLength = nKeyLength;
291 	INIT_DATA(ht, p, pData, nDataSize);
292 	p->h = h;
293 	CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
294 	if (pDest) {
295 		*pDest = p->pData;
296 	}
297 
298 	HANDLE_BLOCK_INTERRUPTIONS();
299 	CONNECT_TO_GLOBAL_DLLIST(p, ht);
300 	ht->arBuckets[nIndex] = p;
301 	HANDLE_UNBLOCK_INTERRUPTIONS();
302 
303 	ht->nNumOfElements++;
304 	ZEND_HASH_IF_FULL_DO_RESIZE(ht);		/* If the Hash table is full, resize it */
305 	return SUCCESS;
306 }
307 
_zend_hash_quick_add_or_update(HashTable * ht,const char * arKey,uint nKeyLength,ulong h,void * pData,uint nDataSize,void ** pDest,int flag ZEND_FILE_LINE_DC)308 ZEND_API int _zend_hash_quick_add_or_update(HashTable *ht, const char *arKey, uint nKeyLength, ulong h, void *pData, uint nDataSize, void **pDest, int flag ZEND_FILE_LINE_DC)
309 {
310 	uint nIndex;
311 	Bucket *p;
312 #ifdef ZEND_SIGNALS
313 	TSRMLS_FETCH();
314 #endif
315 
316 	IS_CONSISTENT(ht);
317 
318 	ZEND_ASSERT(nKeyLength != 0);
319 
320 	CHECK_INIT(ht);
321 	nIndex = h & ht->nTableMask;
322 
323 	p = ht->arBuckets[nIndex];
324 	while (p != NULL) {
325 		if (p->arKey == arKey ||
326 			((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
327 				if (flag & HASH_ADD) {
328 					return FAILURE;
329 				}
330 				ZEND_ASSERT(p->pData != pData);
331 				HANDLE_BLOCK_INTERRUPTIONS();
332 				if (ht->pDestructor) {
333 					ht->pDestructor(p->pData);
334 				}
335 				UPDATE_DATA(ht, p, pData, nDataSize);
336 				if (pDest) {
337 					*pDest = p->pData;
338 				}
339 				HANDLE_UNBLOCK_INTERRUPTIONS();
340 				return SUCCESS;
341 		}
342 		p = p->pNext;
343 	}
344 
345 	if (IS_INTERNED(arKey)) {
346 		p = (Bucket *) pemalloc(sizeof(Bucket), ht->persistent);
347 		p->arKey = arKey;
348 	} else {
349 		p = (Bucket *) pemalloc(sizeof(Bucket) + nKeyLength, ht->persistent);
350 		p->arKey = (const char*)(p + 1);
351 		memcpy((char*)p->arKey, arKey, nKeyLength);
352 	}
353 
354 	p->nKeyLength = nKeyLength;
355 	INIT_DATA(ht, p, pData, nDataSize);
356 	p->h = h;
357 
358 	CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
359 
360 	if (pDest) {
361 		*pDest = p->pData;
362 	}
363 
364 	HANDLE_BLOCK_INTERRUPTIONS();
365 	ht->arBuckets[nIndex] = p;
366 	CONNECT_TO_GLOBAL_DLLIST(p, ht);
367 	HANDLE_UNBLOCK_INTERRUPTIONS();
368 
369 	ht->nNumOfElements++;
370 	ZEND_HASH_IF_FULL_DO_RESIZE(ht);		/* If the Hash table is full, resize it */
371 	return SUCCESS;
372 }
373 
374 
zend_hash_add_empty_element(HashTable * ht,const char * arKey,uint nKeyLength)375 ZEND_API int zend_hash_add_empty_element(HashTable *ht, const char *arKey, uint nKeyLength)
376 {
377 	void *dummy = (void *) 1;
378 
379 	return zend_hash_add(ht, arKey, nKeyLength, &dummy, sizeof(void *), NULL);
380 }
381 
382 
_zend_hash_index_update_or_next_insert(HashTable * ht,ulong h,void * pData,uint nDataSize,void ** pDest,int flag ZEND_FILE_LINE_DC)383 ZEND_API int _zend_hash_index_update_or_next_insert(HashTable *ht, ulong h, void *pData, uint nDataSize, void **pDest, int flag ZEND_FILE_LINE_DC)
384 {
385 	uint nIndex;
386 	Bucket *p;
387 #ifdef ZEND_SIGNALS
388 	TSRMLS_FETCH();
389 #endif
390 
391 	IS_CONSISTENT(ht);
392 	CHECK_INIT(ht);
393 
394 	if (flag & HASH_NEXT_INSERT) {
395 		h = ht->nNextFreeElement;
396 	}
397 	nIndex = h & ht->nTableMask;
398 
399 	p = ht->arBuckets[nIndex];
400 	while (p != NULL) {
401 		if ((p->nKeyLength == 0) && (p->h == h)) {
402 			if (flag & HASH_NEXT_INSERT || flag & HASH_ADD) {
403 				return FAILURE;
404 			}
405 			ZEND_ASSERT(p->pData != pData);
406 			HANDLE_BLOCK_INTERRUPTIONS();
407 			if (ht->pDestructor) {
408 				ht->pDestructor(p->pData);
409 			}
410 			UPDATE_DATA(ht, p, pData, nDataSize);
411 			HANDLE_UNBLOCK_INTERRUPTIONS();
412 			if (pDest) {
413 				*pDest = p->pData;
414 			}
415 			return SUCCESS;
416 		}
417 		p = p->pNext;
418 	}
419 	p = (Bucket *) pemalloc_rel(sizeof(Bucket), ht->persistent);
420 	p->arKey = NULL;
421 	p->nKeyLength = 0; /* Numeric indices are marked by making the nKeyLength == 0 */
422 	p->h = h;
423 	INIT_DATA(ht, p, pData, nDataSize);
424 	if (pDest) {
425 		*pDest = p->pData;
426 	}
427 
428 	CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
429 
430 	HANDLE_BLOCK_INTERRUPTIONS();
431 	ht->arBuckets[nIndex] = p;
432 	CONNECT_TO_GLOBAL_DLLIST(p, ht);
433 	HANDLE_UNBLOCK_INTERRUPTIONS();
434 
435 	if ((long)h >= (long)ht->nNextFreeElement) {
436 		ht->nNextFreeElement = h < LONG_MAX ? h + 1 : LONG_MAX;
437 	}
438 	ht->nNumOfElements++;
439 	ZEND_HASH_IF_FULL_DO_RESIZE(ht);
440 	return SUCCESS;
441 }
442 
443 
zend_hash_do_resize(HashTable * ht)444 static void zend_hash_do_resize(HashTable *ht)
445 {
446 	Bucket **t;
447 #ifdef ZEND_SIGNALS
448 	TSRMLS_FETCH();
449 #endif
450 
451 	IS_CONSISTENT(ht);
452 
453 	if ((ht->nTableSize << 1) > 0) {	/* Let's double the table size */
454 		t = (Bucket **) perealloc(ht->arBuckets, (ht->nTableSize << 1) * sizeof(Bucket *), ht->persistent);
455 		HANDLE_BLOCK_INTERRUPTIONS();
456 		ht->arBuckets = t;
457 		ht->nTableSize = (ht->nTableSize << 1);
458 		ht->nTableMask = ht->nTableSize - 1;
459 		zend_hash_rehash(ht);
460 		HANDLE_UNBLOCK_INTERRUPTIONS();
461 	}
462 }
463 
zend_hash_rehash(HashTable * ht)464 ZEND_API int zend_hash_rehash(HashTable *ht)
465 {
466 	Bucket *p;
467 	uint nIndex;
468 
469 	IS_CONSISTENT(ht);
470 	if (UNEXPECTED(ht->nNumOfElements == 0)) {
471 		return SUCCESS;
472 	}
473 
474 	memset(ht->arBuckets, 0, ht->nTableSize * sizeof(Bucket *));
475 	for (p = ht->pListHead; p != NULL; p = p->pListNext) {
476 		nIndex = p->h & ht->nTableMask;
477 		CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
478 		ht->arBuckets[nIndex] = p;
479 	}
480 	return SUCCESS;
481 }
482 
zend_hash_reindex(HashTable * ht,zend_bool only_integer_keys)483 ZEND_API void zend_hash_reindex(HashTable *ht, zend_bool only_integer_keys) {
484 	Bucket *p;
485 	uint nIndex;
486 	ulong offset = 0;
487 
488 	IS_CONSISTENT(ht);
489 	if (UNEXPECTED(ht->nNumOfElements == 0)) {
490 		ht->nNextFreeElement = 0;
491 		return;
492 	}
493 
494 	memset(ht->arBuckets, 0, ht->nTableSize * sizeof(Bucket *));
495 	for (p = ht->pListHead; p != NULL; p = p->pListNext) {
496 		if (!only_integer_keys || p->nKeyLength == 0) {
497 			p->h = offset++;
498 			p->nKeyLength = 0;
499 		}
500 
501 		nIndex = p->h & ht->nTableMask;
502 		CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
503 		ht->arBuckets[nIndex] = p;
504 	}
505 	ht->nNextFreeElement = offset;
506 }
507 
zend_hash_del_key_or_index(HashTable * ht,const char * arKey,uint nKeyLength,ulong h,int flag)508 ZEND_API int zend_hash_del_key_or_index(HashTable *ht, const char *arKey, uint nKeyLength, ulong h, int flag)
509 {
510 	uint nIndex;
511 	Bucket *p;
512 
513 	IS_CONSISTENT(ht);
514 
515 	if (flag == HASH_DEL_KEY) {
516 		h = zend_inline_hash_func(arKey, nKeyLength);
517 	}
518 	nIndex = h & ht->nTableMask;
519 
520 	p = ht->arBuckets[nIndex];
521 	while (p != NULL) {
522 		if ((p->h == h)
523 			 && (p->nKeyLength == nKeyLength)
524 			 && ((p->nKeyLength == 0) /* Numeric index (short circuits the memcmp() check) */
525 				 || !memcmp(p->arKey, arKey, nKeyLength))) { /* String index */
526 			i_zend_hash_bucket_delete(ht, p);
527 			return SUCCESS;
528 		}
529 		p = p->pNext;
530 	}
531 	return FAILURE;
532 }
533 
534 
zend_hash_destroy(HashTable * ht)535 ZEND_API void zend_hash_destroy(HashTable *ht)
536 {
537 	Bucket *p, *q;
538 
539 	IS_CONSISTENT(ht);
540 
541 	SET_INCONSISTENT(HT_IS_DESTROYING);
542 
543 	p = ht->pListHead;
544 	while (p != NULL) {
545 		q = p;
546 		p = p->pListNext;
547 		if (ht->pDestructor) {
548 			ht->pDestructor(q->pData);
549 		}
550 		if (q->pData != &q->pDataPtr) {
551 			pefree(q->pData, ht->persistent);
552 		}
553 		pefree(q, ht->persistent);
554 	}
555 	if (ht->nTableMask) {
556 		pefree(ht->arBuckets, ht->persistent);
557 	}
558 
559 	SET_INCONSISTENT(HT_DESTROYED);
560 }
561 
562 
zend_hash_clean(HashTable * ht)563 ZEND_API void zend_hash_clean(HashTable *ht)
564 {
565 	Bucket *p, *q;
566 
567 	IS_CONSISTENT(ht);
568 
569 	p = ht->pListHead;
570 
571 	if (ht->nTableMask) {
572 		memset(ht->arBuckets, 0, ht->nTableSize*sizeof(Bucket *));
573 	}
574 	ht->pListHead = NULL;
575 	ht->pListTail = NULL;
576 	ht->nNumOfElements = 0;
577 	ht->nNextFreeElement = 0;
578 	ht->pInternalPointer = NULL;
579 
580 	while (p != NULL) {
581 		q = p;
582 		p = p->pListNext;
583 		if (ht->pDestructor) {
584 			ht->pDestructor(q->pData);
585 		}
586 		if (q->pData != &q->pDataPtr) {
587 			pefree(q->pData, ht->persistent);
588 		}
589 		pefree(q, ht->persistent);
590 	}
591 }
592 
zend_hash_graceful_destroy(HashTable * ht)593 ZEND_API void zend_hash_graceful_destroy(HashTable *ht)
594 {
595 	IS_CONSISTENT(ht);
596 
597 	while (ht->pListHead != NULL) {
598 		zend_hash_bucket_delete(ht, ht->pListHead);
599 	}
600 
601 	if (ht->nTableMask) {
602 		pefree(ht->arBuckets, ht->persistent);
603 	}
604 
605 	SET_INCONSISTENT(HT_DESTROYED);
606 }
607 
zend_hash_graceful_reverse_destroy(HashTable * ht)608 ZEND_API void zend_hash_graceful_reverse_destroy(HashTable *ht)
609 {
610 	IS_CONSISTENT(ht);
611 
612 	while (ht->pListTail != NULL) {
613 		zend_hash_bucket_delete(ht, ht->pListTail);
614 	}
615 
616 	if (ht->nTableMask) {
617 		pefree(ht->arBuckets, ht->persistent);
618 	}
619 
620 	SET_INCONSISTENT(HT_DESTROYED);
621 }
622 
623 /* This is used to recurse elements and selectively delete certain entries
624  * from a hashtable. apply_func() receives the data and decides if the entry
625  * should be deleted or recursion should be stopped. The following three
626  * return codes are possible:
627  * ZEND_HASH_APPLY_KEEP   - continue
628  * ZEND_HASH_APPLY_STOP   - stop iteration
629  * ZEND_HASH_APPLY_REMOVE - delete the element, combineable with the former
630  */
631 
zend_hash_apply(HashTable * ht,apply_func_t apply_func TSRMLS_DC)632 ZEND_API void zend_hash_apply(HashTable *ht, apply_func_t apply_func TSRMLS_DC)
633 {
634 	Bucket *p;
635 
636 	IS_CONSISTENT(ht);
637 
638 	HASH_PROTECT_RECURSION(ht);
639 	p = ht->pListHead;
640 	while (p != NULL) {
641 		int result = apply_func(p->pData TSRMLS_CC);
642 
643 		Bucket *p_next = p->pListNext;
644 		if (result & ZEND_HASH_APPLY_REMOVE) {
645 			zend_hash_bucket_delete(ht, p);
646 		}
647 		p = p_next;
648 
649 		if (result & ZEND_HASH_APPLY_STOP) {
650 			break;
651 		}
652 	}
653 	HASH_UNPROTECT_RECURSION(ht);
654 }
655 
656 
zend_hash_apply_with_argument(HashTable * ht,apply_func_arg_t apply_func,void * argument TSRMLS_DC)657 ZEND_API void zend_hash_apply_with_argument(HashTable *ht, apply_func_arg_t apply_func, void *argument TSRMLS_DC)
658 {
659 	Bucket *p;
660 
661 	IS_CONSISTENT(ht);
662 
663 	HASH_PROTECT_RECURSION(ht);
664 	p = ht->pListHead;
665 	while (p != NULL) {
666 		int result = apply_func(p->pData, argument TSRMLS_CC);
667 
668 		Bucket *p_next = p->pListNext;
669 		if (result & ZEND_HASH_APPLY_REMOVE) {
670 			zend_hash_bucket_delete(ht, p);
671 		}
672 		p = p_next;
673 
674 		if (result & ZEND_HASH_APPLY_STOP) {
675 			break;
676 		}
677 	}
678 	HASH_UNPROTECT_RECURSION(ht);
679 }
680 
681 
zend_hash_apply_with_arguments(HashTable * ht TSRMLS_DC,apply_func_args_t apply_func,int num_args,...)682 ZEND_API void zend_hash_apply_with_arguments(HashTable *ht TSRMLS_DC, apply_func_args_t apply_func, int num_args, ...)
683 {
684 	Bucket *p;
685 	va_list args;
686 	zend_hash_key hash_key;
687 
688 	IS_CONSISTENT(ht);
689 
690 	HASH_PROTECT_RECURSION(ht);
691 
692 	p = ht->pListHead;
693 	while (p != NULL) {
694 		int result;
695 		Bucket *p_next;
696 
697 		va_start(args, num_args);
698 		hash_key.arKey = p->arKey;
699 		hash_key.nKeyLength = p->nKeyLength;
700 		hash_key.h = p->h;
701 		result = apply_func(p->pData TSRMLS_CC, num_args, args, &hash_key);
702 
703 		p_next = p->pListNext;
704 		if (result & ZEND_HASH_APPLY_REMOVE) {
705 			zend_hash_bucket_delete(ht, p);
706 		}
707 		p = p_next;
708 
709 		if (result & ZEND_HASH_APPLY_STOP) {
710 			va_end(args);
711 			break;
712 		}
713 		va_end(args);
714 	}
715 
716 	HASH_UNPROTECT_RECURSION(ht);
717 }
718 
719 
zend_hash_reverse_apply(HashTable * ht,apply_func_t apply_func TSRMLS_DC)720 ZEND_API void zend_hash_reverse_apply(HashTable *ht, apply_func_t apply_func TSRMLS_DC)
721 {
722 	Bucket *p;
723 
724 	IS_CONSISTENT(ht);
725 
726 	HASH_PROTECT_RECURSION(ht);
727 	p = ht->pListTail;
728 	while (p != NULL) {
729 		int result = apply_func(p->pData TSRMLS_CC);
730 
731 		Bucket *p_last = p->pListLast;
732 		if (result & ZEND_HASH_APPLY_REMOVE) {
733 			zend_hash_bucket_delete(ht, p);
734 		}
735 		p = p_last;
736 
737 		if (result & ZEND_HASH_APPLY_STOP) {
738 			break;
739 		}
740 	}
741 	HASH_UNPROTECT_RECURSION(ht);
742 }
743 
744 
zend_hash_copy(HashTable * target,HashTable * source,copy_ctor_func_t pCopyConstructor,void * tmp,uint size)745 ZEND_API void zend_hash_copy(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, void *tmp, uint size)
746 {
747 	Bucket *p;
748 	void *new_entry;
749 	zend_bool setTargetPointer;
750 
751 	IS_CONSISTENT(source);
752 	IS_CONSISTENT(target);
753 
754 	setTargetPointer = !target->pInternalPointer;
755 	p = source->pListHead;
756 	while (p) {
757 		if (setTargetPointer && source->pInternalPointer == p) {
758 			target->pInternalPointer = NULL;
759 		}
760 		if (p->nKeyLength) {
761 			zend_hash_quick_update(target, p->arKey, p->nKeyLength, p->h, p->pData, size, &new_entry);
762 		} else {
763 			zend_hash_index_update(target, p->h, p->pData, size, &new_entry);
764 		}
765 		if (pCopyConstructor) {
766 			pCopyConstructor(new_entry);
767 		}
768 		p = p->pListNext;
769 	}
770 	if (!target->pInternalPointer) {
771 		target->pInternalPointer = target->pListHead;
772 	}
773 }
774 
775 
_zend_hash_merge(HashTable * target,HashTable * source,copy_ctor_func_t pCopyConstructor,void * tmp,uint size,int overwrite ZEND_FILE_LINE_DC)776 ZEND_API void _zend_hash_merge(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, void *tmp, uint size, int overwrite ZEND_FILE_LINE_DC)
777 {
778 	Bucket *p;
779 	void *t;
780 	int mode = (overwrite?HASH_UPDATE:HASH_ADD);
781 
782 	IS_CONSISTENT(source);
783 	IS_CONSISTENT(target);
784 
785 	p = source->pListHead;
786 	while (p) {
787 		if (p->nKeyLength>0) {
788 			if (_zend_hash_quick_add_or_update(target, p->arKey, p->nKeyLength, p->h, p->pData, size, &t, mode ZEND_FILE_LINE_RELAY_CC)==SUCCESS && pCopyConstructor) {
789 				pCopyConstructor(t);
790 			}
791 		} else {
792 			if ((mode==HASH_UPDATE || !zend_hash_index_exists(target, p->h)) && zend_hash_index_update(target, p->h, p->pData, size, &t)==SUCCESS && pCopyConstructor) {
793 				pCopyConstructor(t);
794 			}
795 		}
796 		p = p->pListNext;
797 	}
798 	target->pInternalPointer = target->pListHead;
799 }
800 
801 
zend_hash_replace_checker_wrapper(HashTable * target,void * source_data,Bucket * p,void * pParam,merge_checker_func_t merge_checker_func)802 static zend_bool zend_hash_replace_checker_wrapper(HashTable *target, void *source_data, Bucket *p, void *pParam, merge_checker_func_t merge_checker_func)
803 {
804 	zend_hash_key hash_key;
805 
806 	hash_key.arKey = p->arKey;
807 	hash_key.nKeyLength = p->nKeyLength;
808 	hash_key.h = p->h;
809 	return merge_checker_func(target, source_data, &hash_key, pParam);
810 }
811 
812 
zend_hash_merge_ex(HashTable * target,HashTable * source,copy_ctor_func_t pCopyConstructor,uint size,merge_checker_func_t pMergeSource,void * pParam)813 ZEND_API void zend_hash_merge_ex(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, uint size, merge_checker_func_t pMergeSource, void *pParam)
814 {
815 	Bucket *p;
816 	void *t;
817 
818 	IS_CONSISTENT(source);
819 	IS_CONSISTENT(target);
820 
821 	p = source->pListHead;
822 	while (p) {
823 		if (zend_hash_replace_checker_wrapper(target, p->pData, p, pParam, pMergeSource)) {
824 			if (zend_hash_quick_update(target, p->arKey, p->nKeyLength, p->h, p->pData, size, &t)==SUCCESS && pCopyConstructor) {
825 				pCopyConstructor(t);
826 			}
827 		}
828 		p = p->pListNext;
829 	}
830 	target->pInternalPointer = target->pListHead;
831 }
832 
833 
834 /* Returns SUCCESS if found and FAILURE if not. The pointer to the
835  * data is returned in pData. The reason is that there's no reason
836  * someone using the hash table might not want to have NULL data
837  */
zend_hash_find(const HashTable * ht,const char * arKey,uint nKeyLength,void ** pData)838 ZEND_API int zend_hash_find(const HashTable *ht, const char *arKey, uint nKeyLength, void **pData)
839 {
840 	ulong h;
841 	uint nIndex;
842 	Bucket *p;
843 
844 	IS_CONSISTENT(ht);
845 
846 	h = zend_inline_hash_func(arKey, nKeyLength);
847 	nIndex = h & ht->nTableMask;
848 
849 	p = ht->arBuckets[nIndex];
850 	while (p != NULL) {
851 		if (p->arKey == arKey ||
852 			((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
853 				*pData = p->pData;
854 				return SUCCESS;
855 		}
856 		p = p->pNext;
857 	}
858 	return FAILURE;
859 }
860 
861 
zend_hash_quick_find(const HashTable * ht,const char * arKey,uint nKeyLength,ulong h,void ** pData)862 ZEND_API int zend_hash_quick_find(const HashTable *ht, const char *arKey, uint nKeyLength, ulong h, void **pData)
863 {
864 	uint nIndex;
865 	Bucket *p;
866 
867 	ZEND_ASSERT(nKeyLength != 0);
868 
869 	IS_CONSISTENT(ht);
870 
871 	nIndex = h & ht->nTableMask;
872 
873 	p = ht->arBuckets[nIndex];
874 	while (p != NULL) {
875 		if (p->arKey == arKey ||
876 			((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
877 				*pData = p->pData;
878 				return SUCCESS;
879 		}
880 		p = p->pNext;
881 	}
882 	return FAILURE;
883 }
884 
885 
zend_hash_exists(const HashTable * ht,const char * arKey,uint nKeyLength)886 ZEND_API int zend_hash_exists(const HashTable *ht, const char *arKey, uint nKeyLength)
887 {
888 	ulong h;
889 	uint nIndex;
890 	Bucket *p;
891 
892 	IS_CONSISTENT(ht);
893 
894 	h = zend_inline_hash_func(arKey, nKeyLength);
895 	nIndex = h & ht->nTableMask;
896 
897 	p = ht->arBuckets[nIndex];
898 	while (p != NULL) {
899 		if (p->arKey == arKey ||
900 			((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
901 				return 1;
902 		}
903 		p = p->pNext;
904 	}
905 	return 0;
906 }
907 
908 
zend_hash_quick_exists(const HashTable * ht,const char * arKey,uint nKeyLength,ulong h)909 ZEND_API int zend_hash_quick_exists(const HashTable *ht, const char *arKey, uint nKeyLength, ulong h)
910 {
911 	uint nIndex;
912 	Bucket *p;
913 
914 	ZEND_ASSERT(nKeyLength != 0);
915 
916 	IS_CONSISTENT(ht);
917 
918 	nIndex = h & ht->nTableMask;
919 
920 	p = ht->arBuckets[nIndex];
921 	while (p != NULL) {
922 		if (p->arKey == arKey ||
923 			((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
924 				return 1;
925 		}
926 		p = p->pNext;
927 	}
928 	return 0;
929 
930 }
931 
932 
zend_hash_index_find(const HashTable * ht,ulong h,void ** pData)933 ZEND_API int zend_hash_index_find(const HashTable *ht, ulong h, void **pData)
934 {
935 	uint nIndex;
936 	Bucket *p;
937 
938 	IS_CONSISTENT(ht);
939 
940 	nIndex = h & ht->nTableMask;
941 
942 	p = ht->arBuckets[nIndex];
943 	while (p != NULL) {
944 		if ((p->h == h) && (p->nKeyLength == 0)) {
945 			*pData = p->pData;
946 			return SUCCESS;
947 		}
948 		p = p->pNext;
949 	}
950 	return FAILURE;
951 }
952 
953 
zend_hash_index_exists(const HashTable * ht,ulong h)954 ZEND_API int zend_hash_index_exists(const HashTable *ht, ulong h)
955 {
956 	uint nIndex;
957 	Bucket *p;
958 
959 	IS_CONSISTENT(ht);
960 
961 	nIndex = h & ht->nTableMask;
962 
963 	p = ht->arBuckets[nIndex];
964 	while (p != NULL) {
965 		if ((p->h == h) && (p->nKeyLength == 0)) {
966 			return 1;
967 		}
968 		p = p->pNext;
969 	}
970 	return 0;
971 }
972 
973 
zend_hash_num_elements(const HashTable * ht)974 ZEND_API int zend_hash_num_elements(const HashTable *ht)
975 {
976 	IS_CONSISTENT(ht);
977 
978 	return ht->nNumOfElements;
979 }
980 
981 
zend_hash_get_pointer(const HashTable * ht,HashPointer * ptr)982 ZEND_API int zend_hash_get_pointer(const HashTable *ht, HashPointer *ptr)
983 {
984 	ptr->pos = ht->pInternalPointer;
985 	if (ht->pInternalPointer) {
986 		ptr->h = ht->pInternalPointer->h;
987 		return 1;
988 	} else {
989 		ptr->h = 0;
990 		return 0;
991 	}
992 }
993 
zend_hash_set_pointer(HashTable * ht,const HashPointer * ptr)994 ZEND_API int zend_hash_set_pointer(HashTable *ht, const HashPointer *ptr)
995 {
996 	if (ptr->pos == NULL) {
997 		ht->pInternalPointer = NULL;
998 	} else if (ht->pInternalPointer != ptr->pos) {
999 		Bucket *p;
1000 
1001 		IS_CONSISTENT(ht);
1002 		p = ht->arBuckets[ptr->h & ht->nTableMask];
1003 		while (p != NULL) {
1004 			if (p == ptr->pos) {
1005 				ht->pInternalPointer = p;
1006 				return 1;
1007 			}
1008 			p = p->pNext;
1009 		}
1010 		return 0;
1011 	}
1012 	return 1;
1013 }
1014 
zend_hash_internal_pointer_reset_ex(HashTable * ht,HashPosition * pos)1015 ZEND_API void zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *pos)
1016 {
1017 	IS_CONSISTENT(ht);
1018 
1019 	if (pos)
1020 		*pos = ht->pListHead;
1021 	else
1022 		ht->pInternalPointer = ht->pListHead;
1023 }
1024 
1025 
1026 /* This function will be extremely optimized by remembering
1027  * the end of the list
1028  */
zend_hash_internal_pointer_end_ex(HashTable * ht,HashPosition * pos)1029 ZEND_API void zend_hash_internal_pointer_end_ex(HashTable *ht, HashPosition *pos)
1030 {
1031 	IS_CONSISTENT(ht);
1032 
1033 	if (pos)
1034 		*pos = ht->pListTail;
1035 	else
1036 		ht->pInternalPointer = ht->pListTail;
1037 }
1038 
1039 
zend_hash_move_forward_ex(HashTable * ht,HashPosition * pos)1040 ZEND_API int zend_hash_move_forward_ex(HashTable *ht, HashPosition *pos)
1041 {
1042 	HashPosition *current = pos ? pos : &ht->pInternalPointer;
1043 
1044 	IS_CONSISTENT(ht);
1045 
1046 	if (*current) {
1047 		*current = (*current)->pListNext;
1048 		return SUCCESS;
1049 	} else
1050 		return FAILURE;
1051 }
1052 
zend_hash_move_backwards_ex(HashTable * ht,HashPosition * pos)1053 ZEND_API int zend_hash_move_backwards_ex(HashTable *ht, HashPosition *pos)
1054 {
1055 	HashPosition *current = pos ? pos : &ht->pInternalPointer;
1056 
1057 	IS_CONSISTENT(ht);
1058 
1059 	if (*current) {
1060 		*current = (*current)->pListLast;
1061 		return SUCCESS;
1062 	} else
1063 		return FAILURE;
1064 }
1065 
1066 
1067 /* This function should be made binary safe  */
zend_hash_get_current_key_ex(const HashTable * ht,char ** str_index,uint * str_length,ulong * num_index,zend_bool duplicate,HashPosition * pos)1068 ZEND_API int zend_hash_get_current_key_ex(const HashTable *ht, char **str_index, uint *str_length, ulong *num_index, zend_bool duplicate, HashPosition *pos)
1069 {
1070 	Bucket *p;
1071 
1072 	p = pos ? (*pos) : ht->pInternalPointer;
1073 
1074 	IS_CONSISTENT(ht);
1075 
1076 	if (p) {
1077 		if (p->nKeyLength) {
1078 			if (duplicate) {
1079 				*str_index = estrndup(p->arKey, p->nKeyLength - 1);
1080 			} else {
1081 				*str_index = (char*)p->arKey;
1082 			}
1083 			if (str_length) {
1084 				*str_length = p->nKeyLength;
1085 			}
1086 			return HASH_KEY_IS_STRING;
1087 		} else {
1088 			*num_index = p->h;
1089 			return HASH_KEY_IS_LONG;
1090 		}
1091 	}
1092 	return HASH_KEY_NON_EXISTENT;
1093 }
1094 
zend_hash_get_current_key_zval_ex(const HashTable * ht,zval * key,HashPosition * pos)1095 ZEND_API void zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, HashPosition *pos) {
1096 	Bucket *p;
1097 
1098 	IS_CONSISTENT(ht);
1099 
1100 	p = pos ? (*pos) : ht->pInternalPointer;
1101 
1102 	if (!p) {
1103 		Z_TYPE_P(key) = IS_NULL;
1104 	} else if (p->nKeyLength) {
1105 		Z_TYPE_P(key) = IS_STRING;
1106 		Z_STRVAL_P(key) = IS_INTERNED(p->arKey) ? (char*)p->arKey : estrndup(p->arKey, p->nKeyLength - 1);
1107 		Z_STRLEN_P(key) = p->nKeyLength - 1;
1108 	} else {
1109 		Z_TYPE_P(key) = IS_LONG;
1110 		Z_LVAL_P(key) = p->h;
1111 	}
1112 }
1113 
zend_hash_get_current_key_type_ex(HashTable * ht,HashPosition * pos)1114 ZEND_API int zend_hash_get_current_key_type_ex(HashTable *ht, HashPosition *pos)
1115 {
1116 	Bucket *p;
1117 
1118 	p = pos ? (*pos) : ht->pInternalPointer;
1119 
1120 	IS_CONSISTENT(ht);
1121 
1122 	if (p) {
1123 		if (p->nKeyLength) {
1124 			return HASH_KEY_IS_STRING;
1125 		} else {
1126 			return HASH_KEY_IS_LONG;
1127 		}
1128 	}
1129 	return HASH_KEY_NON_EXISTENT;
1130 }
1131 
1132 
zend_hash_get_current_data_ex(HashTable * ht,void ** pData,HashPosition * pos)1133 ZEND_API int zend_hash_get_current_data_ex(HashTable *ht, void **pData, HashPosition *pos)
1134 {
1135 	Bucket *p;
1136 
1137 	p = pos ? (*pos) : ht->pInternalPointer;
1138 
1139 	IS_CONSISTENT(ht);
1140 
1141 	if (p) {
1142 		*pData = p->pData;
1143 		return SUCCESS;
1144 	} else {
1145 		return FAILURE;
1146 	}
1147 }
1148 
1149 /* This function changes key of current element without changing elements'
1150  * order. If element with target key already exists, it will be deleted first.
1151  */
zend_hash_update_current_key_ex(HashTable * ht,int key_type,const char * str_index,uint str_length,ulong num_index,int mode,HashPosition * pos)1152 ZEND_API int zend_hash_update_current_key_ex(HashTable *ht, int key_type, const char *str_index, uint str_length, ulong num_index, int mode, HashPosition *pos)
1153 {
1154 	Bucket *p, *q;
1155 	ulong h;
1156 #ifdef ZEND_SIGNALS
1157 	TSRMLS_FETCH();
1158 #endif
1159 
1160 	p = pos ? (*pos) : ht->pInternalPointer;
1161 
1162 	IS_CONSISTENT(ht);
1163 
1164 	if (p) {
1165 		if (key_type == HASH_KEY_IS_LONG) {
1166 			str_length = 0;
1167 			if (!p->nKeyLength && p->h == num_index) {
1168 				return SUCCESS;
1169 			}
1170 
1171 			q = ht->arBuckets[num_index & ht->nTableMask];
1172 			while (q != NULL) {
1173 				if (!q->nKeyLength && q->h == num_index) {
1174 					break;
1175 				}
1176 				q = q->pNext;
1177 			}
1178 		} else if (key_type == HASH_KEY_IS_STRING) {
1179 			if (IS_INTERNED(str_index)) {
1180 				h = INTERNED_HASH(str_index);
1181 			} else {
1182 				h = zend_inline_hash_func(str_index, str_length);
1183 			}
1184 
1185 			if (p->arKey == str_index ||
1186 			    (p->nKeyLength == str_length &&
1187 			     p->h == h &&
1188 			     memcmp(p->arKey, str_index, str_length) == 0)) {
1189 				return SUCCESS;
1190 			}
1191 
1192 			q = ht->arBuckets[h & ht->nTableMask];
1193 
1194 			while (q != NULL) {
1195 				if (q->arKey == str_index ||
1196 				    (q->h == h && q->nKeyLength == str_length &&
1197 				     memcmp(q->arKey, str_index, str_length) == 0)) {
1198 					break;
1199 				}
1200 				q = q->pNext;
1201 			}
1202 		} else {
1203 			return FAILURE;
1204 		}
1205 
1206 		if (q) {
1207 			if (mode != HASH_UPDATE_KEY_ANYWAY) {
1208 				Bucket *r = p->pListLast;
1209 				int found = HASH_UPDATE_KEY_IF_BEFORE;
1210 
1211 				while (r) {
1212 					if (r == q) {
1213 						found = HASH_UPDATE_KEY_IF_AFTER;
1214 						break;
1215 					}
1216 					r = r->pListLast;
1217 				}
1218 				if (mode & found) {
1219 					/* delete current bucket */
1220 					zend_hash_bucket_delete(ht, p);
1221 					return FAILURE;
1222 				}
1223 			}
1224 
1225 			/* delete another bucket with the same key */
1226 			zend_hash_bucket_delete(ht, q);
1227 		}
1228 
1229 		HANDLE_BLOCK_INTERRUPTIONS();
1230 
1231 		if (p->pNext) {
1232 			p->pNext->pLast = p->pLast;
1233 		}
1234 		if (p->pLast) {
1235 			p->pLast->pNext = p->pNext;
1236 		} else {
1237 			ht->arBuckets[p->h & ht->nTableMask] = p->pNext;
1238 		}
1239 
1240 		if ((IS_INTERNED(p->arKey) != IS_INTERNED(str_index)) ||
1241 		    (!IS_INTERNED(p->arKey) && p->nKeyLength != str_length)) {
1242 			Bucket *q;
1243 
1244 			if (IS_INTERNED(str_index)) {
1245 				q = (Bucket *) pemalloc(sizeof(Bucket), ht->persistent);
1246 			} else {
1247 				q = (Bucket *) pemalloc(sizeof(Bucket) + str_length, ht->persistent);
1248 			}
1249 
1250 			q->nKeyLength = str_length;
1251 			if (p->pData == &p->pDataPtr) {
1252 				q->pData = &q->pDataPtr;
1253 			} else {
1254 				q->pData = p->pData;
1255 			}
1256 			q->pDataPtr = p->pDataPtr;
1257 
1258 			CONNECT_TO_GLOBAL_DLLIST_EX(q, ht, p->pListLast, p->pListNext);
1259 			if (ht->pInternalPointer == p) {
1260 				ht->pInternalPointer = q;
1261 			}
1262 
1263 			if (pos) {
1264 				*pos = q;
1265 			}
1266 			pefree(p, ht->persistent);
1267 			p = q;
1268 		}
1269 
1270 		if (key_type == HASH_KEY_IS_LONG) {
1271 			p->h = num_index;
1272 			if ((long)num_index >= (long)ht->nNextFreeElement) {
1273 				ht->nNextFreeElement = num_index < LONG_MAX ? num_index + 1 : LONG_MAX;
1274 			}
1275 		} else {
1276 			p->h = h;
1277 			p->nKeyLength = str_length;
1278 			if (IS_INTERNED(str_index)) {
1279 				p->arKey = str_index;
1280 			} else {
1281 				p->arKey = (const char*)(p+1);
1282 				memcpy((char*)p->arKey, str_index, str_length);
1283 			}
1284 		}
1285 
1286 		CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[p->h & ht->nTableMask]);
1287 		ht->arBuckets[p->h & ht->nTableMask] = p;
1288 		HANDLE_UNBLOCK_INTERRUPTIONS();
1289 
1290 		return SUCCESS;
1291 	} else {
1292 		return FAILURE;
1293 	}
1294 }
1295 
1296 /* Performs an in-place splice operation on a hashtable:
1297  * The elements between offset and offset+length are removed and the elements in list[list_count]
1298  * are inserted in their place. The removed elements can be optionally collected into a hashtable.
1299  * This operation reindexes the hashtable, i.e. integer keys will be zero-based and sequential,
1300  * while string keys stay intact. The same applies to the elements inserted into the removed HT. */
_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)1301 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) /* {{{ */
1302 {
1303 	int pos;
1304 	Bucket *p;
1305 
1306 	IS_CONSISTENT(ht);
1307 	CHECK_INIT(ht);
1308 
1309 	/* Skip all elements until offset */
1310 	for (pos = 0, p = ht->pListHead; pos < offset && p; pos++, p = p->pListNext);
1311 
1312 	while (pos < offset + length && p) {
1313 		/* Copy removed element into HT, if it was specified */
1314 		if (removed != NULL) {
1315 			void *new_entry;
1316 
1317 			if (p->nKeyLength == 0) {
1318 				zend_hash_next_index_insert(removed, p->pData, sizeof(zval *), &new_entry);
1319 			} else {
1320 				zend_hash_quick_update(removed, p->arKey, p->nKeyLength, p->h, p->pData, sizeof(zval *), &new_entry);
1321 			}
1322 
1323 			if (pCopyConstructor) {
1324 				pCopyConstructor(new_entry);
1325 			}
1326 		}
1327 
1328 		/* Remove element */
1329 		{
1330 			Bucket *p_next = p->pListNext;
1331 			zend_hash_bucket_delete(ht, p);
1332 			p = p_next;
1333 		}
1334 
1335 		pos++;
1336 	}
1337 
1338 	if (list != NULL) {
1339 		int i;
1340 		for (i = 0; i < list_count; i++) {
1341 			/* Add new element only to the global linked list, not the bucket list.
1342 			 * Also use key 0 for everything, as we'll reindex the hashtable anyways. */
1343 			Bucket *q = pemalloc_rel(sizeof(Bucket), ht->persistent);
1344 			q->arKey = NULL;
1345 			q->nKeyLength = 0;
1346 			q->h = 0;
1347 			INIT_DATA(ht, q, list[i], nDataSize);
1348 
1349 			CONNECT_TO_GLOBAL_DLLIST_EX(q, ht, p ? p->pListLast : ht->pListTail, p);
1350 
1351 			ht->nNumOfElements++;
1352 
1353 			if (pCopyConstructor) {
1354 				pCopyConstructor(q->pData);
1355 			}
1356 		}
1357 
1358 		ZEND_HASH_IF_FULL_DO_RESIZE(ht);
1359 	}
1360 
1361 	zend_hash_reindex(ht, 1);
1362 }
1363 /* }}} */
1364 
zend_hash_sort(HashTable * ht,sort_func_t sort_func,compare_func_t compar,int renumber TSRMLS_DC)1365 ZEND_API int zend_hash_sort(HashTable *ht, sort_func_t sort_func,
1366 							compare_func_t compar, int renumber TSRMLS_DC)
1367 {
1368 	Bucket **arTmp;
1369 	Bucket *p;
1370 	int i, j;
1371 
1372 	IS_CONSISTENT(ht);
1373 
1374 	if (!(ht->nNumOfElements>1) && !(renumber && ht->nNumOfElements>0)) { /* Doesn't require sorting */
1375 		return SUCCESS;
1376 	}
1377 	arTmp = (Bucket **) pemalloc(ht->nNumOfElements * sizeof(Bucket *), ht->persistent);
1378 	p = ht->pListHead;
1379 	i = 0;
1380 	while (p) {
1381 		arTmp[i] = p;
1382 		p = p->pListNext;
1383 		i++;
1384 	}
1385 
1386 	(*sort_func)((void *) arTmp, i, sizeof(Bucket *), compar TSRMLS_CC);
1387 
1388 	HANDLE_BLOCK_INTERRUPTIONS();
1389 	ht->pListHead = arTmp[0];
1390 	ht->pListTail = NULL;
1391 	ht->pInternalPointer = ht->pListHead;
1392 
1393 	arTmp[0]->pListLast = NULL;
1394 	if (i > 1) {
1395 		arTmp[0]->pListNext = arTmp[1];
1396 		for (j = 1; j < i-1; j++) {
1397 			arTmp[j]->pListLast = arTmp[j-1];
1398 			arTmp[j]->pListNext = arTmp[j+1];
1399 		}
1400 		arTmp[j]->pListLast = arTmp[j-1];
1401 		arTmp[j]->pListNext = NULL;
1402 	} else {
1403 		arTmp[0]->pListNext = NULL;
1404 	}
1405 	ht->pListTail = arTmp[i-1];
1406 
1407 	pefree(arTmp, ht->persistent);
1408 	HANDLE_UNBLOCK_INTERRUPTIONS();
1409 
1410 	if (renumber) {
1411 		zend_hash_reindex(ht, 0);
1412 	}
1413 	return SUCCESS;
1414 }
1415 
1416 
zend_hash_compare(HashTable * ht1,HashTable * ht2,compare_func_t compar,zend_bool ordered TSRMLS_DC)1417 ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, zend_bool ordered TSRMLS_DC)
1418 {
1419 	Bucket *p1, *p2 = NULL;
1420 	int result;
1421 	void *pData2;
1422 
1423 	IS_CONSISTENT(ht1);
1424 	IS_CONSISTENT(ht2);
1425 
1426 	HASH_PROTECT_RECURSION(ht1);
1427 	HASH_PROTECT_RECURSION(ht2);
1428 
1429 	result = ht1->nNumOfElements - ht2->nNumOfElements;
1430 	if (result!=0) {
1431 		HASH_UNPROTECT_RECURSION(ht1);
1432 		HASH_UNPROTECT_RECURSION(ht2);
1433 		return result;
1434 	}
1435 
1436 	p1 = ht1->pListHead;
1437 	if (ordered) {
1438 		p2 = ht2->pListHead;
1439 	}
1440 
1441 	while (p1) {
1442 		if (ordered && !p2) {
1443 			HASH_UNPROTECT_RECURSION(ht1);
1444 			HASH_UNPROTECT_RECURSION(ht2);
1445 			return 1; /* That's not supposed to happen */
1446 		}
1447 		if (ordered) {
1448 			if (p1->nKeyLength==0 && p2->nKeyLength==0) { /* numeric indices */
1449 				if (p1->h != p2->h) {
1450 					HASH_UNPROTECT_RECURSION(ht1);
1451 					HASH_UNPROTECT_RECURSION(ht2);
1452 					return p1->h > p2->h ? 1 : -1;
1453 				}
1454 			} else { /* string indices */
1455 				result = p1->nKeyLength - p2->nKeyLength;
1456 				if (result!=0) {
1457 					HASH_UNPROTECT_RECURSION(ht1);
1458 					HASH_UNPROTECT_RECURSION(ht2);
1459 					return result;
1460 				}
1461 				result = memcmp(p1->arKey, p2->arKey, p1->nKeyLength);
1462 				if (result!=0) {
1463 					HASH_UNPROTECT_RECURSION(ht1);
1464 					HASH_UNPROTECT_RECURSION(ht2);
1465 					return result;
1466 				}
1467 			}
1468 			pData2 = p2->pData;
1469 		} else {
1470 			if (p1->nKeyLength==0) { /* numeric index */
1471 				if (zend_hash_index_find(ht2, p1->h, &pData2)==FAILURE) {
1472 					HASH_UNPROTECT_RECURSION(ht1);
1473 					HASH_UNPROTECT_RECURSION(ht2);
1474 					return 1;
1475 				}
1476 			} else { /* string index */
1477 				if (zend_hash_quick_find(ht2, p1->arKey, p1->nKeyLength, p1->h, &pData2)==FAILURE) {
1478 					HASH_UNPROTECT_RECURSION(ht1);
1479 					HASH_UNPROTECT_RECURSION(ht2);
1480 					return 1;
1481 				}
1482 			}
1483 		}
1484 		result = compar(p1->pData, pData2 TSRMLS_CC);
1485 		if (result!=0) {
1486 			HASH_UNPROTECT_RECURSION(ht1);
1487 			HASH_UNPROTECT_RECURSION(ht2);
1488 			return result;
1489 		}
1490 		p1 = p1->pListNext;
1491 		if (ordered) {
1492 			p2 = p2->pListNext;
1493 		}
1494 	}
1495 
1496 	HASH_UNPROTECT_RECURSION(ht1);
1497 	HASH_UNPROTECT_RECURSION(ht2);
1498 	return 0;
1499 }
1500 
1501 
zend_hash_minmax(const HashTable * ht,compare_func_t compar,int flag,void ** pData TSRMLS_DC)1502 ZEND_API int zend_hash_minmax(const HashTable *ht, compare_func_t compar, int flag, void **pData TSRMLS_DC)
1503 {
1504 	Bucket *p, *res;
1505 
1506 	IS_CONSISTENT(ht);
1507 
1508 	if (ht->nNumOfElements == 0 ) {
1509 		*pData=NULL;
1510 		return FAILURE;
1511 	}
1512 
1513 	res = p = ht->pListHead;
1514 	while ((p = p->pListNext)) {
1515 		if (flag) {
1516 			if (compar(&res, &p TSRMLS_CC) < 0) { /* max */
1517 				res = p;
1518 			}
1519 		} else {
1520 			if (compar(&res, &p TSRMLS_CC) > 0) { /* min */
1521 				res = p;
1522 			}
1523 		}
1524 	}
1525 	*pData = res->pData;
1526 	return SUCCESS;
1527 }
1528 
zend_hash_next_free_element(const HashTable * ht)1529 ZEND_API ulong zend_hash_next_free_element(const HashTable *ht)
1530 {
1531 	IS_CONSISTENT(ht);
1532 
1533 	return ht->nNextFreeElement;
1534 
1535 }
1536 
1537 
1538 #if ZEND_DEBUG
zend_hash_display_pListTail(const HashTable * ht)1539 void zend_hash_display_pListTail(const HashTable *ht)
1540 {
1541 	Bucket *p;
1542 
1543 	p = ht->pListTail;
1544 	while (p != NULL) {
1545 		zend_output_debug_string(0, "pListTail has key %s\n", p->arKey);
1546 		p = p->pListLast;
1547 	}
1548 }
1549 
zend_hash_display(const HashTable * ht)1550 void zend_hash_display(const HashTable *ht)
1551 {
1552 	Bucket *p;
1553 	uint i;
1554 
1555 	if (UNEXPECTED(ht->nNumOfElements == 0)) {
1556 		zend_output_debug_string(0, "The hash is empty");
1557 		return;
1558 	}
1559 	for (i = 0; i < ht->nTableSize; i++) {
1560 		p = ht->arBuckets[i];
1561 		while (p != NULL) {
1562 			zend_output_debug_string(0, "%s <==> 0x%lX\n", p->arKey, p->h);
1563 			p = p->pNext;
1564 		}
1565 	}
1566 
1567 	p = ht->pListTail;
1568 	while (p != NULL) {
1569 		zend_output_debug_string(0, "%s <==> 0x%lX\n", p->arKey, p->h);
1570 		p = p->pListLast;
1571 	}
1572 }
1573 #endif
1574 
1575 /*
1576  * Local variables:
1577  * tab-width: 4
1578  * c-basic-offset: 4
1579  * indent-tabs-mode: t
1580  * End:
1581  */
1582