xref: /ext-ds/src/ds/ds_htable.h (revision 9904fca6)
1 #ifndef DS_HTABLE_H
2 #define DS_HTABLE_H
3 
4 #include "../common.h"
5 
6 #define DS_HTABLE_MIN_CAPACITY  8  // Must be a power of 2
7 
8 /**
9  * Marker to indicate an invalid index in the buffer.
10  */
11 #define DS_HTABLE_INVALID_INDEX ((uint32_t) -1)
12 
13 /**
14  * Determines the calculated hash of a bucket, before mod. We're using the key's
15  * zval uint32_t "next" to store this value so that we don't need another member
16  * in the bucket struct.
17  */
18 #define DS_HTABLE_BUCKET_HASH(_bucket) (Z_NEXT((_bucket)->key))
19 
20 /**
21  * Determines the buffer index of the next bucket in the collision chain.
22  * An invalid index indicates that it's the last bucket in the chain.
23  */
24 #define DS_HTABLE_BUCKET_NEXT(_bucket) (Z_NEXT((_bucket)->value))
25 
26 /**
27  * Determines if a bucket has been deleted.
28  */
29 #define DS_HTABLE_BUCKET_DELETED(_bucket) (Z_ISUNDEF((_bucket)->key))
30 
31 /**
32  * Finds the start of the collision chain for a given hash.
33  * An invalid index indicates that a chain doesn't exist.
34  */
35 #define DS_HTABLE_BUCKET_LOOKUP(t, h) ((t)->lookup[h & ((t)->capacity - 1)])
36 
37 /**
38  * Determines if a table is packed, ie. doesn't have deleted buckets.
39  */
40 #define DS_HTABLE_IS_PACKED(t) ((t)->size == (t)->next)
41 
42 /**
43  * Rehashes a bucket into a table.
44  *
45  * 1. Determine where the bucket's chain would start.
46  * 2. Set the bucket's next bucket to be the start of the chain.
47  * 3. Set the start of the chain to the bucket's position in the buffer.
48  *
49  * This means that the next bucket can come before another in the buffer,
50  * because a rehash unshifts the bucket into the chain.
51  */
52 #define DS_HTABLE_BUCKET_REHASH(table, bucket, mask, idx)   \
53 do {                                                        \
54     ds_htable_bucket_t *_bucket = bucket;                   \
55     ds_htable_t        *_table  = table;                    \
56                                                             \
57     uint32_t  hash = DS_HTABLE_BUCKET_HASH(_bucket);        \
58     uint32_t *head = &_table->lookup[hash & (mask)];        \
59                                                             \
60     DS_HTABLE_BUCKET_NEXT(_bucket) = *head;                 \
61     *head = idx;                                            \
62 } while (0)
63 
64 /**
65  * Copies a bucket's state into another, including: key, value, hash and next.
66  */
67 #define DS_HTABLE_BUCKET_COPY(dst, src)                         \
68 do {                                                            \
69     ds_htable_bucket_t *_src = src;                             \
70     ds_htable_bucket_t *_dst = dst;                             \
71     ZVAL_COPY(&_dst->key, &_src->key);                          \
72     ZVAL_COPY(&_dst->value, &_src->value);                      \
73     DS_HTABLE_BUCKET_NEXT(_dst) = DS_HTABLE_BUCKET_NEXT(_src);  \
74     DS_HTABLE_BUCKET_HASH(_dst) = DS_HTABLE_BUCKET_HASH(_src);  \
75 } while (0)
76 
77 /**
78  * Marks a bucket as deleted, destructing both the key and the value.
79  */
80 #define DS_HTABLE_BUCKET_DELETE(b)                          \
81     DTOR_AND_UNDEF(&(b)->value);                            \
82     DTOR_AND_UNDEF(&(b)->key);                              \
83     DS_HTABLE_BUCKET_NEXT((b)) = DS_HTABLE_INVALID_INDEX
84 
85 #define DS_HTABLE_FOREACH_BUCKET(h, b)                  \
86 do {                                                    \
87     ds_htable_t *_h = h;                                \
88     ds_htable_bucket_t *_x = _h->buckets;               \
89     ds_htable_bucket_t *_y = _h->buckets + _h->next;    \
90     for (; _x < _y; ++_x) {                             \
91         if (DS_HTABLE_BUCKET_DELETED(_x)) continue;     \
92         b = _x;
93 
94 #define DS_HTABLE_FOREACH_BUCKET_REVERSED(h, b)         \
95 do {                                                    \
96     ds_htable_t *_h  = h;                               \
97     ds_htable_bucket_t *_x = _h->buckets;               \
98     ds_htable_bucket_t *_y = _x + _h->next - 1;         \
99     for (; _y >= _x; --_y) {                            \
100         if (DS_HTABLE_BUCKET_DELETED(_y)) continue;     \
101         b = _y;
102 
103 #define DS_HTABLE_FOREACH(h, i, k, v)                   \
104 do {                                                    \
105     uint32_t _i;                                        \
106     uint32_t _n = (h)->size;                            \
107     ds_htable_bucket_t *_b = (h)->buckets;              \
108                                                         \
109     for (_i = 0; _i < _n; ++_b) {                       \
110         if (DS_HTABLE_BUCKET_DELETED(_b)) continue;     \
111         k = &_b->key;                                   \
112         v = &_b->value;                                 \
113         i = _i++;
114 
115 #define DS_HTABLE_FOREACH_KEY(h, k)                     \
116 do {                                                    \
117     ds_htable_t *_h = h;                                \
118     ds_htable_bucket_t *_x = _h->buckets;               \
119     ds_htable_bucket_t *_y = _h->buckets + _h->next;    \
120     for (; _x < _y; ++_x) {                             \
121         if (DS_HTABLE_BUCKET_DELETED(_x)) continue;     \
122         k = &_x->key;
123 
124 #define DS_HTABLE_FOREACH_VALUE(h, v)                   \
125 do {                                                    \
126     ds_htable_t *_h = h;                                \
127     ds_htable_bucket_t *_x = _h->buckets;               \
128     ds_htable_bucket_t *_y = _h->buckets + _h->next;    \
129     for (; _x < _y; ++_x) {                             \
130         if (DS_HTABLE_BUCKET_DELETED(_x)) continue;     \
131         v = &_x->value;
132 
133 #define DS_HTABLE_FOREACH_KEY_VALUE(h, k, v)    \
134 do {                                                    \
135     ds_htable_t *_h = h;                                \
136     ds_htable_bucket_t *_x = _h->buckets;               \
137     ds_htable_bucket_t *_y = _h->buckets + _h->next;    \
138     for (; _x < _y; ++_x) {                             \
139         if (DS_HTABLE_BUCKET_DELETED(_x)) continue;     \
140         k = &_x->key;                                   \
141         v = &_x->value;
142 
143 #define DS_HTABLE_FOREACH_END() \
144     }                           \
145 } while (0)
146 
147 typedef struct _ds_htable_bucket_t {
148     zval key;
149     zval value;
150 } ds_htable_bucket_t;
151 
152 typedef struct _ds_htable_t {
153     ds_htable_bucket_t  *buckets;       // Buffer for the buckets
154     uint32_t            *lookup;        // Separated hash lookup table
155     uint32_t             next;          // Next open index in the bucket buffer
156     uint32_t             size;          // Number of active buckets in the table
157     uint32_t             capacity;      // Length of the bucket buffer
158     uint32_t             min_deleted;   // First deleted bucket buffer index
159 } ds_htable_t;
160 
161 ds_htable_t *ds_htable();
162 zval *ds_htable_values(ds_htable_t *table);
163 
164 void ds_htable_ensure_capacity(ds_htable_t *table, uint32_t capacity);
165 
166 void ds_htable_sort(ds_htable_t *table, compare_func_t compare_func);
167 void ds_htable_sort_by_key(ds_htable_t *table);
168 void ds_htable_sort_by_value(ds_htable_t *table);
169 void ds_htable_sort_by_pair(ds_htable_t *table);
170 void ds_htable_sort_callback_by_key(ds_htable_t *table);
171 void ds_htable_sort_callback_by_value(ds_htable_t *table);
172 
173 ds_htable_bucket_t *ds_htable_lookup_by_value(ds_htable_t *h, zval *key);
174 ds_htable_bucket_t *ds_htable_lookup_by_key(ds_htable_t *h, zval *key);
175 ds_htable_bucket_t *ds_htable_lookup_by_position(ds_htable_t *table, uint32_t position);
176 
177 bool ds_htable_lookup_or_next(ds_htable_t *table, zval *key, ds_htable_bucket_t **return_value);
178 bool ds_htable_has_keys(ds_htable_t *h, VA_PARAMS);
179 bool ds_htable_has_key(ds_htable_t *table, zval *key);
180 bool ds_htable_has_values(ds_htable_t *h, VA_PARAMS);
181 bool ds_htable_has_value(ds_htable_t *h, zval *value);
182 int  ds_htable_remove(ds_htable_t *h, zval *key, zval *return_value);
183 void ds_htable_put(ds_htable_t *h, zval *key, zval *value);
184 void ds_htable_to_array(ds_htable_t *h, zval *arr);
185 void ds_htable_free(ds_htable_t *h);
186 zval *ds_htable_get(ds_htable_t *h, zval *key);
187 ds_htable_t *ds_htable_slice(ds_htable_t *table, zend_long index, zend_long length);
188 
189 void ds_htable_clear(ds_htable_t *h);
190 bool ds_htable_isset(ds_htable_t *h, zval *key, bool check_empty);
191 ds_htable_t *ds_htable_clone(ds_htable_t *source);
192 
193 zend_string *ds_htable_join_keys(ds_htable_t *table, const char* glue, const size_t len);
194 
195 void ds_htable_reverse(ds_htable_t *table);
196 ds_htable_t *ds_htable_reversed(ds_htable_t *table);
197 
198 ds_htable_bucket_t *ds_htable_first(ds_htable_t *table);
199 ds_htable_bucket_t *ds_htable_last(ds_htable_t *table);
200 
201 ds_htable_t *ds_htable_map(ds_htable_t *table, FCI_PARAMS);
202 ds_htable_t *ds_htable_filter(ds_htable_t *table);
203 ds_htable_t *ds_htable_filter_callback(ds_htable_t *table, FCI_PARAMS);
204 
205 void ds_htable_apply(ds_htable_t *table, FCI_PARAMS);
206 void ds_htable_reduce(ds_htable_t *table, FCI_PARAMS, zval *initial, zval *return_value);
207 
208 ds_htable_t *ds_htable_xor(ds_htable_t *table, ds_htable_t *other);
209 ds_htable_t *ds_htable_diff(ds_htable_t *table, ds_htable_t *other);
210 ds_htable_t *ds_htable_intersect(ds_htable_t *table, ds_htable_t *other);
211 ds_htable_t *ds_htable_merge(ds_htable_t *table, ds_htable_t *other);
212 
213 int ds_htable_serialize(ds_htable_t *table, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data);
214 int ds_htable_unserialize(ds_htable_t *table, const unsigned char *buffer, size_t length, zend_unserialize_data *data);
215 
216 #endif
217