Skip to content

Split headers part 3: zend_func_types.h and zend_hash.h #10717

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions Zend/zend_func_types.h
Original file line number Diff line number Diff line change
@@ -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 |
| [email protected] so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/

#ifndef ZEND_FUNC_TYPES_H
#define ZEND_FUNC_TYPES_H

#include <stddef.h>

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 */
1 change: 1 addition & 0 deletions Zend/zend_hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
+----------------------------------------------------------------------+
*/

#include "zend_hash.h"
#include "zend.h"
#include "zend_globals.h"
#include "zend_variables.h"
Expand Down
271 changes: 242 additions & 29 deletions Zend/zend_hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
* (nTableSize<HT_MAX_SIZE ? nTableSize+nTableSize : nTableSize) <= HT_MAX_SIZE
*/
#if SIZEOF_SIZE_T == 4
# define HT_MAX_SIZE 0x02000000
# define HT_HASH_TO_BUCKET_EX(data, idx) \
((Bucket*)((char*)(data) + (idx)))
# define HT_IDX_TO_HASH(idx) \
((idx) * sizeof(Bucket))
# define HT_HASH_TO_IDX(idx) \
((idx) / sizeof(Bucket))
#elif SIZEOF_SIZE_T == 8
# define HT_MAX_SIZE 0x40000000
# define HT_HASH_TO_BUCKET_EX(data, idx) \
((data) + (idx))
# define HT_IDX_TO_HASH(idx) \
(idx)
# define HT_HASH_TO_IDX(idx) \
(idx)
#else
# error "Unknown SIZEOF_SIZE_T"
#endif

#define HT_HASH_EX(data, idx) \
((uint32_t*)(data))[(int32_t)(idx)]
#define HT_HASH(ht, idx) \
HT_HASH_EX((ht)->arHash, 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;
Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_sort.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
+----------------------------------------------------------------------+
*/

#include "zend.h"
#include "zend_sort.h"

#include <limits.h>

static inline void zend_sort_2(void *a, void *b, compare_func_t cmp, swap_func_t swp) /* {{{ */ {
Expand Down
3 changes: 3 additions & 0 deletions Zend/zend_sort.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading