diff --git a/Zend/zend_func_types.h b/Zend/zend_func_types.h new file mode 100644 index 0000000000000..efbd5a50c6b14 --- /dev/null +++ b/Zend/zend_func_types.h @@ -0,0 +1,30 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_FUNC_TYPES_H +#define ZEND_FUNC_TYPES_H + +#include + +typedef struct _zval_struct zval; + +typedef int (*compare_func_t)(const void *, const void *); +typedef void (*swap_func_t)(void *, void *); +typedef void (*sort_func_t)(void *, size_t, size_t, compare_func_t, swap_func_t); +typedef void (*dtor_func_t)(zval *pDest); +typedef void (*copy_ctor_func_t)(zval *pElement); + +#endif /* ZEND_FUNC_TYPES_H */ diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 93d10119519b0..db1c0a6b49d58 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -18,6 +18,7 @@ +----------------------------------------------------------------------+ */ +#include "zend_hash.h" #include "zend.h" #include "zend_globals.h" #include "zend_variables.h" diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index dfcf9c0c8b508..7c68625625741 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -21,9 +21,12 @@ #ifndef ZEND_HASH_H #define ZEND_HASH_H -#include "zend.h" +#include "zend_alloc.h" +#include "zend_func_types.h" #include "zend_sort.h" +#include "zend_string.h" #include "zend_type_code.h" +#include "zend_types.h" #define HASH_KEY_IS_STRING 1 #define HASH_KEY_IS_LONG 2 @@ -46,49 +49,259 @@ /* Only the low byte are real flags */ #define HASH_FLAG_MASK 0xff +typedef struct _Bucket { + zval val; + zend_ulong h; /* hash value (or numeric index) */ + zend_string *key; /* string key or NULL for numerics */ +} Bucket; + +typedef struct _zend_array HashTable; + +struct _zend_array { + zend_refcounted_h gc; + union { + struct { + ZEND_ENDIAN_LOHI_4( + uint8_t flags, + uint8_t _unused, + uint8_t nIteratorsCount, + uint8_t _unused2) + } v; + uint32_t flags; + } u; + uint32_t nTableMask; + union { + uint32_t *arHash; /* hash table (allocated above this pointer) */ + Bucket *arData; /* array of hash buckets */ + zval *arPacked; /* packed array of zvals */ + }; + uint32_t nNumUsed; + uint32_t nNumOfElements; + uint32_t nTableSize; + uint32_t nInternalPointer; + zend_long nNextFreeElement; + dtor_func_t pDestructor; +}; + +/* + * HashTable Data Layout + * ===================== + * + * +=============================+ + * | HT_HASH(ht, ht->nTableMask) | +=============================+ + * | ... | | HT_INVALID_IDX | + * | HT_HASH(ht, -1) | | HT_INVALID_IDX | + * +-----------------------------+ +-----------------------------+ + * ht->arData ---> | Bucket[0] | ht->arPacked ---> | ZVAL[0] | + * | ... | | ... | + * | Bucket[ht->nTableSize-1] | | ZVAL[ht->nTableSize-1] | + * +=============================+ +=============================+ + */ + +#define HT_INVALID_IDX ((uint32_t) -1) + +#define HT_MIN_MASK ((uint32_t) -2) +#define HT_MIN_SIZE 8 + +/* HT_MAX_SIZE is chosen to satisfy the following constraints: + * - HT_SIZE_TO_MASK(HT_MAX_SIZE) != 0 + * - HT_SIZE_EX(HT_MAX_SIZE, HT_SIZE_TO_MASK(HT_MAX_SIZE)) does not overflow or + * wrapparound, and is <= the addressable space size + * - HT_MAX_SIZE must be a power of two: + * (nTableSizearHash, idx) + +static zend_always_inline uint32_t HT_SIZE_TO_MASK(uint32_t nTableSize) +{ + return (uint32_t)(-(nTableSize + nTableSize)); +} + +static zend_always_inline size_t HT_HASH_SIZE(uint32_t nTableMask) +{ + return ((size_t)-nTableMask) * sizeof(uint32_t); +} + +static zend_always_inline size_t HT_DATA_SIZE(size_t nTableSize) +{ + return nTableSize * sizeof(Bucket); +} + +static zend_always_inline size_t HT_SIZE_EX(size_t nTableSize, uint32_t nTableMask) +{ + return HT_DATA_SIZE(nTableSize) + HT_HASH_SIZE(nTableMask); +} + +static zend_always_inline size_t HT_SIZE(const HashTable *ht) +{ + return HT_SIZE_EX(ht->nTableSize, ht->nTableMask); +} + +static zend_always_inline size_t HT_USED_SIZE(const HashTable *ht) +{ + return HT_HASH_SIZE(ht->nTableMask) + (size_t)ht->nNumUsed * sizeof(Bucket); +} + +static zend_always_inline size_t HT_PACKED_DATA_SIZE(size_t nTableSize) +{ + return nTableSize * sizeof(zval); +} + +static zend_always_inline size_t HT_PACKED_SIZE_EX(size_t nTableSize, uint32_t nTableMask) +{ + return HT_PACKED_DATA_SIZE(nTableSize) + HT_HASH_SIZE(nTableMask); +} + +static zend_always_inline size_t HT_PACKED_SIZE(const HashTable *ht) +{ + return HT_PACKED_SIZE_EX(ht->nTableSize, ht->nTableMask); +} + +static zend_always_inline size_t HT_PACKED_USED_SIZE(const HashTable *ht) +{ + return HT_HASH_SIZE(ht->nTableMask) + (size_t)ht->nNumUsed * sizeof(zval); +} + +static zend_always_inline void HT_HASH_RESET(HashTable *ht) +{ +#ifdef __SSE2__ + char *p = (char*)&HT_HASH(ht, (ht)->nTableMask); + size_t size = HT_HASH_SIZE(ht->nTableMask); + __m128i xmm0 = _mm_setzero_si128(); + xmm0 = _mm_cmpeq_epi8(xmm0, xmm0); + ZEND_ASSERT(size >= 64 && ((size & 0x3f) == 0)); + do { + _mm_storeu_si128((__m128i*)p, xmm0); + _mm_storeu_si128((__m128i*)(p+16), xmm0); + _mm_storeu_si128((__m128i*)(p+32), xmm0); + _mm_storeu_si128((__m128i*)(p+48), xmm0); + p += 64; + size -= 64; + } while (size != 0); +#else + memset(&HT_HASH(ht, ht->nTableMask), HT_INVALID_IDX, HT_HASH_SIZE(ht->nTableMask)); +#endif +} + +static zend_always_inline void HT_HASH_RESET_PACKED(HashTable *ht) +{ + HT_HASH(ht, -2) = HT_INVALID_IDX; + HT_HASH(ht, -1) = HT_INVALID_IDX; +} + +static zend_always_inline Bucket *HT_HASH_TO_BUCKET(HashTable *ht, size_t idx) +{ + return HT_HASH_TO_BUCKET_EX(ht->arData, idx); +} + +static zend_always_inline void HT_SET_DATA_ADDR(HashTable *ht, const void *ptr) +{ + ht->arData = (Bucket*)(((char*)ptr) + HT_HASH_SIZE(ht->nTableMask)); +} + +static zend_always_inline void *HT_GET_DATA_ADDR(HashTable *ht) +{ + return (char*)(ht->arData) - HT_HASH_SIZE(ht->nTableMask); +} + +typedef uint32_t HashPosition; + +typedef struct _HashTableIterator { + HashTable *ht; + HashPosition pos; +} HashTableIterator; + #define HT_FLAGS(ht) (ht)->u.flags -#define HT_INVALIDATE(ht) do { \ - HT_FLAGS(ht) = HASH_FLAG_UNINITIALIZED; \ - } while (0) +static zend_always_inline void HT_INVALIDATE(HashTable *ht) +{ + HT_FLAGS(ht) = HASH_FLAG_UNINITIALIZED;; +} -#define HT_IS_INITIALIZED(ht) \ - ((HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) == 0) +static zend_always_inline bool HT_IS_INITIALIZED(const HashTable *ht) +{ + return (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) == 0; +} -#define HT_IS_PACKED(ht) \ - ((HT_FLAGS(ht) & HASH_FLAG_PACKED) != 0) +static zend_always_inline bool HT_IS_PACKED(const HashTable *ht) +{ + return (HT_FLAGS(ht) & HASH_FLAG_PACKED) != 0; +} -#define HT_IS_WITHOUT_HOLES(ht) \ - ((ht)->nNumUsed == (ht)->nNumOfElements) +static zend_always_inline bool HT_IS_WITHOUT_HOLES(const HashTable *ht) +{ + return ht->nNumUsed == ht->nNumOfElements; +} -#define HT_HAS_STATIC_KEYS_ONLY(ht) \ - ((HT_FLAGS(ht) & (HASH_FLAG_PACKED|HASH_FLAG_STATIC_KEYS)) != 0) +static zend_always_inline bool HT_HAS_STATIC_KEYS_ONLY(const HashTable *ht) +{ + return (HT_FLAGS(ht) & (HASH_FLAG_PACKED|HASH_FLAG_STATIC_KEYS)) != 0; +} +static zend_always_inline void HT_ALLOW_COW_VIOLATION(HashTable *ht) +{ #if ZEND_DEBUG -# define HT_ALLOW_COW_VIOLATION(ht) HT_FLAGS(ht) |= HASH_FLAG_ALLOW_COW_VIOLATION -#else -# define HT_ALLOW_COW_VIOLATION(ht) + HT_FLAGS(ht) |= HASH_FLAG_ALLOW_COW_VIOLATION; #endif +} #define HT_ITERATORS_COUNT(ht) (ht)->u.v.nIteratorsCount -#define HT_ITERATORS_OVERFLOW(ht) (HT_ITERATORS_COUNT(ht) == 0xff) -#define HT_HAS_ITERATORS(ht) (HT_ITERATORS_COUNT(ht) != 0) -#define HT_SET_ITERATORS_COUNT(ht, iters) \ - do { HT_ITERATORS_COUNT(ht) = (iters); } while (0) -#define HT_INC_ITERATORS_COUNT(ht) \ - HT_SET_ITERATORS_COUNT(ht, HT_ITERATORS_COUNT(ht) + 1) -#define HT_DEC_ITERATORS_COUNT(ht) \ - HT_SET_ITERATORS_COUNT(ht, HT_ITERATORS_COUNT(ht) - 1) +static zend_always_inline bool HT_ITERATORS_OVERFLOW(const HashTable *ht) +{ + return HT_ITERATORS_COUNT(ht) == 0xff; +} -extern ZEND_API const HashTable zend_empty_array; +static zend_always_inline bool HT_HAS_ITERATORS(const HashTable *ht) +{ + return HT_ITERATORS_COUNT(ht) != 0; +} -#define ZVAL_EMPTY_ARRAY(z) do { \ - zval *__z = (z); \ - Z_ARR_P(__z) = (zend_array*)&zend_empty_array; \ - Z_TYPE_INFO_P(__z) = IS_ARRAY; \ - } while (0) +static zend_always_inline void HT_SET_ITERATORS_COUNT(HashTable *ht, uint8_t iters) +{ + HT_ITERATORS_COUNT(ht) = iters; +} + +static zend_always_inline void HT_INC_ITERATORS_COUNT(HashTable *ht) +{ + HT_SET_ITERATORS_COUNT(ht, HT_ITERATORS_COUNT(ht) + 1); +} +static zend_always_inline void HT_DEC_ITERATORS_COUNT(HashTable *ht) +{ + HT_SET_ITERATORS_COUNT(ht, HT_ITERATORS_COUNT(ht) - 1); +} + +extern ZEND_API const HashTable zend_empty_array; + +static zend_always_inline void ZVAL_EMPTY_ARRAY(zval *z) +{ + Z_ARR_P(z) = (zend_array*)&zend_empty_array; + Z_TYPE_INFO_P(z) = IS_ARRAY; +} typedef struct _zend_hash_key { zend_ulong h; diff --git a/Zend/zend_sort.c b/Zend/zend_sort.c index 355d2d1bad581..4f3e1d747fecb 100644 --- a/Zend/zend_sort.c +++ b/Zend/zend_sort.c @@ -17,8 +17,8 @@ +----------------------------------------------------------------------+ */ -#include "zend.h" #include "zend_sort.h" + #include static inline void zend_sort_2(void *a, void *b, compare_func_t cmp, swap_func_t swp) /* {{{ */ { diff --git a/Zend/zend_sort.h b/Zend/zend_sort.h index e606935f79029..74e67e1a206c6 100644 --- a/Zend/zend_sort.h +++ b/Zend/zend_sort.h @@ -20,6 +20,9 @@ #ifndef ZEND_SORT_H #define ZEND_SORT_H +#include "zend_func_types.h" +#include "zend_portability.h" + BEGIN_EXTERN_C() ZEND_API void zend_sort(void *base, size_t nmemb, size_t siz, compare_func_t cmp, swap_func_t swp); ZEND_API void zend_insert_sort(void *base, size_t nmemb, size_t siz, compare_func_t cmp, swap_func_t swp); diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 3d900226e54e5..db0b58e127257 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -22,6 +22,7 @@ #ifndef ZEND_TYPES_H #define ZEND_TYPES_H +#include "zend_func_types.h" #include "zend_portability.h" #include "zend_long.h" #include "zend_rc_debug.h" @@ -89,12 +90,7 @@ typedef struct _zend_resource zend_resource; typedef struct _zend_reference zend_reference; typedef struct _zend_ast_ref zend_ast_ref; typedef struct _zend_ast zend_ast; - -typedef int (*compare_func_t)(const void *, const void *); -typedef void (*swap_func_t)(void *, void *); -typedef void (*sort_func_t)(void *, size_t, size_t, compare_func_t, swap_func_t); -typedef void (*dtor_func_t)(zval *pDest); -typedef void (*copy_ctor_func_t)(zval *pElement); +typedef struct _zend_array HashTable; /* * zend_type - is an abstraction layer to represent information about type hint. @@ -331,152 +327,6 @@ struct _zval_struct { } u2; }; -typedef struct _Bucket { - zval val; - zend_ulong h; /* hash value (or numeric index) */ - zend_string *key; /* string key or NULL for numerics */ -} Bucket; - -typedef struct _zend_array HashTable; - -struct _zend_array { - zend_refcounted_h gc; - union { - struct { - ZEND_ENDIAN_LOHI_4( - uint8_t flags, - uint8_t _unused, - uint8_t nIteratorsCount, - uint8_t _unused2) - } v; - uint32_t flags; - } u; - uint32_t nTableMask; - union { - uint32_t *arHash; /* hash table (allocated above this pointer) */ - Bucket *arData; /* array of hash buckets */ - zval *arPacked; /* packed array of zvals */ - }; - uint32_t nNumUsed; - uint32_t nNumOfElements; - uint32_t nTableSize; - uint32_t nInternalPointer; - zend_long nNextFreeElement; - dtor_func_t pDestructor; -}; - -/* - * HashTable Data Layout - * ===================== - * - * +=============================+ - * | HT_HASH(ht, ht->nTableMask) | +=============================+ - * | ... | | HT_INVALID_IDX | - * | HT_HASH(ht, -1) | | HT_INVALID_IDX | - * +-----------------------------+ +-----------------------------+ - * ht->arData ---> | Bucket[0] | ht->arPacked ---> | ZVAL[0] | - * | ... | | ... | - * | Bucket[ht->nTableSize-1] | | ZVAL[ht->nTableSize-1] | - * +=============================+ +=============================+ - */ - -#define HT_INVALID_IDX ((uint32_t) -1) - -#define HT_MIN_MASK ((uint32_t) -2) -#define HT_MIN_SIZE 8 - -/* HT_MAX_SIZE is chosen to satisfy the following constraints: - * - HT_SIZE_TO_MASK(HT_MAX_SIZE) != 0 - * - HT_SIZE_EX(HT_MAX_SIZE, HT_SIZE_TO_MASK(HT_MAX_SIZE)) does not overflow or - * wrapparound, and is <= the addressable space size - * - HT_MAX_SIZE must be a power of two: - * (nTableSizearHash, idx) - -#define HT_SIZE_TO_MASK(nTableSize) \ - ((uint32_t)(-((nTableSize) + (nTableSize)))) -#define HT_HASH_SIZE(nTableMask) \ - (((size_t)-(uint32_t)(nTableMask)) * sizeof(uint32_t)) -#define HT_DATA_SIZE(nTableSize) \ - ((size_t)(nTableSize) * sizeof(Bucket)) -#define HT_SIZE_EX(nTableSize, nTableMask) \ - (HT_DATA_SIZE((nTableSize)) + HT_HASH_SIZE((nTableMask))) -#define HT_SIZE(ht) \ - HT_SIZE_EX((ht)->nTableSize, (ht)->nTableMask) -#define HT_USED_SIZE(ht) \ - (HT_HASH_SIZE((ht)->nTableMask) + ((size_t)(ht)->nNumUsed * sizeof(Bucket))) -#define HT_PACKED_DATA_SIZE(nTableSize) \ - ((size_t)(nTableSize) * sizeof(zval)) -#define HT_PACKED_SIZE_EX(nTableSize, nTableMask) \ - (HT_PACKED_DATA_SIZE((nTableSize)) + HT_HASH_SIZE((nTableMask))) -#define HT_PACKED_SIZE(ht) \ - HT_PACKED_SIZE_EX((ht)->nTableSize, (ht)->nTableMask) -#define HT_PACKED_USED_SIZE(ht) \ - (HT_HASH_SIZE((ht)->nTableMask) + ((size_t)(ht)->nNumUsed * sizeof(zval))) -#ifdef __SSE2__ -# define HT_HASH_RESET(ht) do { \ - char *p = (char*)&HT_HASH(ht, (ht)->nTableMask); \ - size_t size = HT_HASH_SIZE((ht)->nTableMask); \ - __m128i xmm0 = _mm_setzero_si128(); \ - xmm0 = _mm_cmpeq_epi8(xmm0, xmm0); \ - ZEND_ASSERT(size >= 64 && ((size & 0x3f) == 0)); \ - do { \ - _mm_storeu_si128((__m128i*)p, xmm0); \ - _mm_storeu_si128((__m128i*)(p+16), xmm0); \ - _mm_storeu_si128((__m128i*)(p+32), xmm0); \ - _mm_storeu_si128((__m128i*)(p+48), xmm0); \ - p += 64; \ - size -= 64; \ - } while (size != 0); \ - } while (0) -#else -# define HT_HASH_RESET(ht) \ - memset(&HT_HASH(ht, (ht)->nTableMask), HT_INVALID_IDX, HT_HASH_SIZE((ht)->nTableMask)) -#endif -#define HT_HASH_RESET_PACKED(ht) do { \ - HT_HASH(ht, -2) = HT_INVALID_IDX; \ - HT_HASH(ht, -1) = HT_INVALID_IDX; \ - } while (0) -#define HT_HASH_TO_BUCKET(ht, idx) \ - HT_HASH_TO_BUCKET_EX((ht)->arData, idx) - -#define HT_SET_DATA_ADDR(ht, ptr) do { \ - (ht)->arData = (Bucket*)(((char*)(ptr)) + HT_HASH_SIZE((ht)->nTableMask)); \ - } while (0) -#define HT_GET_DATA_ADDR(ht) \ - ((char*)((ht)->arData) - HT_HASH_SIZE((ht)->nTableMask)) - -typedef uint32_t HashPosition; - -typedef struct _HashTableIterator { - HashTable *ht; - HashPosition pos; -} HashTableIterator; - struct _zend_object { zend_refcounted_h gc; uint32_t handle; // TODO: may be removed ??? diff --git a/Zend/zend_variables.h b/Zend/zend_variables.h index ea3fd9c5efcb8..62d0cfb8bd386 100644 --- a/Zend/zend_variables.h +++ b/Zend/zend_variables.h @@ -21,6 +21,7 @@ #ifndef ZEND_VARIABLES_H #define ZEND_VARIABLES_H +#include "zend_hash.h" #include "zend_types.h" #include "zend_gc.h"