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