]> granicus.if.org Git - php/commitdiff
Added an optimization which saves memory and emalloc/efree calls for empty HashTables
authorDmitry Stogov <dmitry@php.net>
Tue, 20 Apr 2010 10:49:22 +0000 (10:49 +0000)
committerDmitry Stogov <dmitry@php.net>
Tue, 20 Apr 2010 10:49:22 +0000 (10:49 +0000)
NEWS
Zend/zend_hash.c

diff --git a/NEWS b/NEWS
index d83cf0669a6b880cdc3b5e5fda8b718e4fc9aad0..d3ed2af71082e1b1a7a849b79255fb0ef316d910 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,8 @@
 - Upgraded bundled sqlite to version 3.6.23.1. (Ilia)
 - Upgraded bundled PCRE to version 8.02. (Ilia)
 
+- Added an optimization which saves memory and emalloc/efree calls for empty
+  HashTables (Stas, Dmitry)
 - Added Tokyo Cabinet abstract DB support to ext/dba. (Michael Maclean)
 - Added Jenkins's one-at-a-time hash support to ext/hash. (Martin Jansen)
 - Added FNV-1 hash support to ext/hash. (Michael Maclean)
index d8b40872db20407128f8079060f9c0233f839240..69b6bdc16922b6bb5fe7f4559e42aaf809ed84a6 100644 (file)
@@ -132,12 +132,18 @@ ZEND_API ulong zend_hash_func(const char *arKey, uint nKeyLength)
                (p)->pDataPtr=NULL;                                                                                             \
        }
 
-
+#define CHECK_INIT(ht) do {                                                                                            \
+       if (UNEXPECTED((ht)->nTableMask == 0)) {                                                                \
+               (ht)->nTableMask = (ht)->nTableSize - 1;                                                \
+               (ht)->arBuckets = (Bucket **) pecalloc((ht)->nTableSize, sizeof(Bucket *), (ht)->persistent);   \
+       }                                                                                                                                       \
+} while (0)
+static const Bucket *uninitialized_bucket = NULL;
 
 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)
 {
        uint i = 3;
-       Bucket **tmp;
 
        SET_INCONSISTENT(HT_OK);
 
@@ -151,9 +157,9 @@ ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunctio
                ht->nTableSize = 1 << i;
        }
 
-       ht->nTableMask = ht->nTableSize - 1;
+       ht->nTableMask = 0;     /* 0 means that ht->arBuckets is uninitialized */
        ht->pDestructor = pDestructor;
-       ht->arBuckets = NULL;
+       ht->arBuckets = (Bucket**)&uninitialized_bucket;
        ht->pListHead = NULL;
        ht->pListTail = NULL;
        ht->nNumOfElements = 0;
@@ -162,21 +168,6 @@ ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunctio
        ht->persistent = persistent;
        ht->nApplyCount = 0;
        ht->bApplyProtection = 1;
-       
-       /* Uses ecalloc() so that Bucket* == NULL */
-       if (persistent) {
-               tmp = (Bucket **) calloc(ht->nTableSize, sizeof(Bucket *));
-               if (!tmp) {
-                       return FAILURE;
-               }
-               ht->arBuckets = tmp;
-       } else {
-               tmp = (Bucket **) ecalloc_rel(ht->nTableSize, sizeof(Bucket *));
-               if (tmp) {
-                       ht->arBuckets = tmp;
-               }
-       }
-       
        return SUCCESS;
 }
 
@@ -212,6 +203,8 @@ ZEND_API int _zend_hash_add_or_update(HashTable *ht, const char *arKey, uint nKe
                return FAILURE;
        }
 
+       CHECK_INIT(ht);
+
        h = zend_inline_hash_func(arKey, nKeyLength);
        nIndex = h & ht->nTableMask;
 
@@ -278,6 +271,7 @@ ZEND_API int _zend_hash_quick_add_or_update(HashTable *ht, const char *arKey, ui
                return zend_hash_index_update(ht, h, pData, nDataSize, pDest);
        }
 
+       CHECK_INIT(ht);
        nIndex = h & ht->nTableMask;
        
        p = ht->arBuckets[nIndex];
@@ -350,6 +344,7 @@ ZEND_API int _zend_hash_index_update_or_next_insert(HashTable *ht, ulong h, void
        Bucket *p;
 
        IS_CONSISTENT(ht);
+       CHECK_INIT(ht);
 
        if (flag & HASH_NEXT_INSERT) {
                h = ht->nNextFreeElement;
@@ -440,6 +435,9 @@ ZEND_API int zend_hash_rehash(HashTable *ht)
        uint nIndex;
 
        IS_CONSISTENT(ht);
+       if (UNEXPECTED(ht->nNumOfElements == 0)) {
+               return SUCCESS;
+       }
 
        memset(ht->arBuckets, 0, ht->nTableSize * sizeof(Bucket *));
        p = ht->pListHead;
@@ -530,7 +528,9 @@ ZEND_API void zend_hash_destroy(HashTable *ht)
                }
                pefree(q, ht->persistent);
        }
-       pefree(ht->arBuckets, ht->persistent);
+       if (ht->nTableMask) {
+               pefree(ht->arBuckets, ht->persistent);
+       }
 
        SET_INCONSISTENT(HT_DESTROYED);
 }
@@ -556,7 +556,9 @@ ZEND_API void zend_hash_clean(HashTable *ht)
                }
                pefree(q, ht->persistent);
        }
-       memset(ht->arBuckets, 0, ht->nTableSize*sizeof(Bucket *));
+       if (ht->nTableMask) {
+               memset(ht->arBuckets, 0, ht->nTableSize*sizeof(Bucket *));
+       }
        ht->pListHead = NULL;
        ht->pListTail = NULL;
        ht->nNumOfElements = 0;
@@ -630,7 +632,9 @@ ZEND_API void zend_hash_graceful_destroy(HashTable *ht)
        while (p != NULL) {
                p = zend_hash_apply_deleter(ht, p);
        }
-       pefree(ht->arBuckets, ht->persistent);
+       if (ht->nTableMask) {
+               pefree(ht->arBuckets, ht->persistent);
+       }
 
        SET_INCONSISTENT(HT_DESTROYED);
 }
@@ -647,7 +651,9 @@ ZEND_API void zend_hash_graceful_reverse_destroy(HashTable *ht)
                p = ht->pListTail;
        }
 
-       pefree(ht->arBuckets, ht->persistent);
+       if (ht->nTableMask) {
+               pefree(ht->arBuckets, ht->persistent);
+       }
 
        SET_INCONSISTENT(HT_DESTROYED);
 }
@@ -1177,6 +1183,7 @@ ZEND_API int zend_hash_update_current_key_ex(HashTable *ht, int key_type, const
        p = pos ? (*pos) : ht->pInternalPointer;
 
        IS_CONSISTENT(ht);
+       CHECK_INIT(ht);
 
        if (p) {
                if (key_type == HASH_KEY_IS_LONG) {
@@ -1533,6 +1540,10 @@ void zend_hash_display(const HashTable *ht)
        Bucket *p;
        uint i;
 
+       if (UNEXPECTED(ht->nNumOfElements == 0)) {
+               zend_output_debug_string(0, "The hash is empty");
+               return;
+       }
        for (i = 0; i < ht->nTableSize; i++) {
                p = ht->arBuckets[i];
                while (p != NULL) {