-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathhash.h
More file actions
326 lines (276 loc) · 10.1 KB
/
hash.h
File metadata and controls
326 lines (276 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
#ifndef _HASH_H
#define _HASH_H
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <php.h>
#include <zend_types.h>
typedef unsigned long int ulong;
typedef struct string_t {
unsigned int len;
char str[1];
} string_t;
typedef enum {
NULL_T=0,
BOOL_T,
LONG_T,
DOUBLE_T,
STR_T,
HT_T,
SERI_T,
TS_HT_T
} type_t;
typedef struct value_t {
type_t type;
int expire;
union {
zend_bool b;
long int l;
double d;
string_t *str;
void *ptr;
};
} value_t;
typedef struct bucket_t {
ulong h; /* Used for numeric indexing */
value_t value;
struct bucket_t *pListNext;
struct bucket_t *pListLast;
struct bucket_t *pNext;
struct bucket_t *pLast;
uint nKeyLength;
char arKey[1];
} bucket_t;
typedef void (*hash_dtor_func_t)(value_t *pDest);
typedef struct hash_table_t {
uint nTableSize;
uint nTableMask;
uint nNumOfElements;
ulong nNextFreeElement;
bucket_t *pListHead;
bucket_t *pListTail;
bucket_t **arBuckets;
hash_dtor_func_t pDestructor;
} hash_table_t;
void hash_table_value_free(value_t *value);
/* startup/shutdown */
int _hash_table_init(hash_table_t *ht, uint nSize, hash_dtor_func_t pDestructor);
void hash_table_destroy(hash_table_t *ht);
void hash_table_clean(hash_table_t *ht);
#define hash_table_init(ht, nSize) _hash_table_init((ht), (nSize), hash_table_value_free)
#define hash_table_init_ex(ht, nSize, pDestructor) _hash_table_init((ht), (nSize), (pDestructor))
#define HASH_TABLE_ADD 1
#define HASH_TABLE_UPDATE 2
#define HASH_TABLE_NEXT_INSERT 4
/* additions/updates/changes */
int _hash_table_add_or_update(hash_table_t *ht, const char *arKey, uint nKeyLength, value_t *pData, int flag);
#define hash_table_update(ht, arKey, nKeyLength, pData, pDest) \
_hash_table_add_or_update(ht, arKey, nKeyLength, pData, HASH_TABLE_UPDATE)
#define hash_table_add(ht, arKey, nKeyLength, pData, pDest) \
_hash_table_add_or_update(ht, arKey, nKeyLength, pData, HASH_TABLE_ADD)
int _hash_table_quick_add_or_update(hash_table_t *ht, const char *arKey, uint nKeyLength, ulong h, value_t *pData, int flag);
#define hash_table_quick_update(ht, arKey, nKeyLength, h, pData, pDest) \
_hash_table_quick_add_or_update(ht, arKey, nKeyLength, h, pData, HASH_TABLE_UPDATE)
#define hash_table_quick_add(ht, arKey, nKeyLength, h, pData, pDest) \
_hash_table_quick_add_or_update(ht, arKey, nKeyLength, h, pData, HASH_TABLE_ADD)
int _hash_table_index_update_or_next_insert(hash_table_t *ht, ulong h, value_t *pData, int flag);
#define hash_table_index_update(ht, h, pData, pDest) \
_hash_table_index_update_or_next_insert(ht, h, pData, HASH_TABLE_UPDATE)
#define hash_table_next_index_insert(ht, pData, pDest) \
_hash_table_index_update_or_next_insert(ht, 0, pData, HASH_TABLE_NEXT_INSERT)
#define HASH_TABLE_APPLY_KEEP 1
#define HASH_TABLE_APPLY_REMOVE 2
#define HASH_TABLE_APPLY_STOP 4
typedef int (*hash_apply_func_t)(bucket_t *pDest);
typedef int (*hash_apply_func_arg_t)(bucket_t *pDest, void *argument);
typedef int (*hash_apply_func_args_t)(bucket_t *pDest, int num_args, va_list args);
void hash_table_apply(hash_table_t *ht, hash_apply_func_t apply_func);
void hash_table_apply_with_argument(hash_table_t *ht, hash_apply_func_arg_t apply_func, void *);
void hash_table_apply_with_arguments(hash_table_t *ht, hash_apply_func_args_t apply_func, int, ...);
#define HASH_TABLE_DEL_KEY 1
#define HASH_TABLE_DEL_KEY_QUICK 2
#define HASH_TABLE_DEL_INDEX 4
/* Deletes */
int hash_table_del_key_or_index(hash_table_t *ht, const char *arKey, uint nKeyLength, ulong h, int flag);
#define hash_table_del(ht, arKey, nKeyLength) \
hash_table_del_key_or_index(ht, arKey, nKeyLength, 0, HASH_TABLE_DEL_KEY)
#define hash_table_quick_del(ht, arKey, nKeyLength, h) \
hash_table_del_key_or_index(ht, arKey, nKeyLength, h, HASH_TABLE_DEL_KEY_QUICK)
#define hash_table_index_del(ht, h) \
hash_table_del_key_or_index(ht, NULL, 0, h, HASH_TABLE_DEL_INDEX)
#define zend_get_hash_value(s,l) hash_table_func(s,l)
/* Data retreival */
int hash_table_find(const hash_table_t *ht, const char *arKey, uint nKeyLength, value_t *pData);
int hash_table_quick_find(const hash_table_t *ht, const char *arKey, uint nKeyLength, ulong h, value_t *pData);
int hash_table_index_find(const hash_table_t *ht, ulong h, value_t *pData);
/* Misc */
int hash_table_exists(const hash_table_t *ht, const char *arKey, uint nKeyLength);
int hash_table_quick_exists(const hash_table_t *ht, const char *arKey, uint nKeyLength, ulong h);
int hash_table_index_exists(const hash_table_t *ht, ulong h);
ulong hash_table_next_free_element(const hash_table_t *ht);
int hash_table_num_elements(const hash_table_t *ht);
void hash_table_reindex(hash_table_t *ht, zend_bool only_integer_keys);
ulong hash_table_func(const char *arKey, uint nKeyLength);
static zend_always_inline void hash_table_bucket_delete(hash_table_t *ht, bucket_t *p) {
if (p->pLast) {
p->pLast->pNext = p->pNext;
} else {
ht->arBuckets[p->h & ht->nTableMask] = p->pNext;
}
if (p->pNext) {
p->pNext->pLast = p->pLast;
}
if (p->pListLast != NULL) {
p->pListLast->pListNext = p->pListNext;
} else {
/* Deleting the head of the list */
ht->pListHead = p->pListNext;
}
if (p->pListNext != NULL) {
p->pListNext->pListLast = p->pListLast;
} else {
/* Deleting the tail of the list */
ht->pListTail = p->pListLast;
}
ht->nNumOfElements--;
if (ht->pDestructor) {
ht->pDestructor(&p->value);
}
free(p);
}
typedef int (*hash_compare_func_t)(const bucket_t *a, const bucket_t *b);
int compare_key(const bucket_t *a, const bucket_t *b);
int compare_value(const bucket_t *a, const bucket_t *b);
int hash_table_minmax(const hash_table_t *ht, hash_compare_func_t compar, int flag, bucket_t **ret);
int hash_table_sort(hash_table_t *ht, hash_compare_func_t compar, int reverse);
// ===========================================================================================================
#include <pthread.h>
#include <time.h>
#include <zend_exceptions.h>
#if defined(LOCK_TIMEOUT) && LOCK_TIMEOUT <= 0
#undef LOCK_TIMEOUT
#endif
#ifdef LOCK_TIMEOUT
typedef struct _tskey_hash_table_t {
hash_table_t ht;
pthread_mutex_t lock;
} tskey_hash_table_t;
#endif
typedef struct ts_hash_table_t {
hash_table_t ht;
int expire;
volatile unsigned short rd_count;
volatile unsigned short ref_count; // reference count
pthread_mutex_t wlock;
pthread_mutex_t rlock;
int fds[2];
#ifdef LOCK_TIMEOUT
tskey_hash_table_t *tsht;
ulong h;
#endif
} ts_hash_table_t;
static zend_always_inline int _ts_hash_table_init(ts_hash_table_t *ts_ht, uint nSize, hash_dtor_func_t pDestructor) {
ts_ht->rd_count = 0;
ts_ht->ref_count = 1;
ts_ht->fds[0] = 0;
ts_ht->fds[1] = 0;
ts_ht->expire = 0;
#ifdef LOCK_TIMEOUT
ts_ht->tsht = NULL;
ts_ht->h = hash_table_func((const char*) &ts_ht, sizeof(void*));
#endif
pthread_mutex_init(&ts_ht->rlock, NULL);
pthread_mutex_init(&ts_ht->wlock, NULL);
return _hash_table_init(&ts_ht->ht, nSize, pDestructor);
}
#define ts_hash_table_init(ht, nSize) _ts_hash_table_init((ht), (nSize), hash_table_value_free)
#define ts_hash_table_init_ex(ht, nSize, pDestructor) _ts_hash_table_init((ht), (nSize), (pDestructor))
static zend_always_inline void ts_hash_table_lock(ts_hash_table_t *ts_ht) {
pthread_mutex_lock(&ts_ht->rlock);
}
static zend_always_inline void ts_hash_table_unlock(ts_hash_table_t *ts_ht) {
pthread_mutex_unlock(&ts_ht->rlock);
}
#ifdef LOCK_TIMEOUT
extern pthread_key_t tskey;
void ts_table_table_tid_destroy(void *hh);
long int ts_table_table_tid_inc(ts_hash_table_t *hh);
long int ts_table_table_tid_dec_ex(tskey_hash_table_t *tsht, ts_hash_table_t *hh);
#define ts_table_table_tid_dec(ht) ts_table_table_tid_dec_ex(pthread_getspecific(tskey), ht)
const char *gettimeofstr();
void ts_hash_table_deadlock(const char *msg);
#endif
static zend_always_inline void ts_hash_table_wr_lock(ts_hash_table_t *ts_ht) {
#ifndef LOCK_TIMEOUT
pthread_mutex_lock(&ts_ht->wlock);
#else
// printf("%p lock\n", ts_ht);
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += LOCK_TIMEOUT;
if(pthread_mutex_timedlock(&ts_ht->wlock, &ts)) {
if(ts_table_table_tid_inc(ts_ht) == 1) {
ts_hash_table_deadlock("Deadlock caused by shared variables");
} else {
ts_hash_table_deadlock("Repeated locking of shared variables");
}
pthread_mutex_lock(&ts_ht->wlock);
} else {
ts_table_table_tid_inc(ts_ht);
}
ts_ht->tsht = pthread_getspecific(tskey);
#endif
}
static zend_always_inline void ts_hash_table_wr_unlock(ts_hash_table_t *ts_ht) {
#ifndef LOCK_TIMEOUT
pthread_mutex_unlock(&ts_ht->wlock);
#else
long int i = ts_table_table_tid_dec(ts_ht);
if(i == 0) {
ts_ht->tsht = NULL;
// printf("%p unlock\n", ts_ht);
pthread_mutex_unlock(&ts_ht->wlock);
} else if(i < 0) {
i = ts_table_table_tid_dec_ex(ts_ht->tsht, ts_ht);
if(i == 0) {
ts_ht->tsht = NULL;
// printf("%p unlock\n", ts_ht);
pthread_mutex_unlock(&ts_ht->wlock);
} else if(i < 0) {
ts_hash_table_deadlock("Shared variable unlocking exception");
}
}
#endif
}
static zend_always_inline void ts_hash_table_rd_lock(ts_hash_table_t *ts_ht) {
pthread_mutex_lock(&ts_ht->rlock);
if((++ts_ht->rd_count) == 1) ts_hash_table_wr_lock(ts_ht);
pthread_mutex_unlock(&ts_ht->rlock);
}
static zend_always_inline void ts_hash_table_rd_unlock(ts_hash_table_t *ts_ht) {
pthread_mutex_lock(&ts_ht->rlock);
if((--ts_ht->rd_count) == 0) ts_hash_table_wr_unlock(ts_ht);
pthread_mutex_unlock(&ts_ht->rlock);
}
static zend_always_inline void ts_hash_table_ref(ts_hash_table_t *ts_ht) {
ts_hash_table_lock(ts_ht);
ts_ht->ref_count++;
ts_hash_table_unlock(ts_ht);
}
#define ts_hash_table_unref(ts_ht) ts_hash_table_destroy(ts_ht)
static zend_always_inline void ts_hash_table_destroy_ex(ts_hash_table_t *ts_ht, int is_free) {
ts_hash_table_lock(ts_ht);
if(--ts_ht->ref_count == 0) {
ts_hash_table_unlock(ts_ht);
if(ts_ht->fds[0] > 0) close(ts_ht->fds[0]);
if(ts_ht->fds[1] > 0) close(ts_ht->fds[1]);
pthread_mutex_destroy(&ts_ht->rlock);
pthread_mutex_destroy(&ts_ht->wlock);
hash_table_destroy(&ts_ht->ht);
if(is_free) free(ts_ht);
} else ts_hash_table_unlock(ts_ht);
}
#define ts_hash_table_destroy(ts_ht) ts_hash_table_destroy_ex(ts_ht, 1)
#endif /* _HASH_H */