xref: /ext-ds/src/ds/ds_set.c (revision 28e6f6a1)
1 #include "../common.h"
2 
3 #include "../php/iterators/php_set_iterator.h"
4 #include "../php/handlers/php_set_handlers.h"
5 #include "../php/classes/php_set_ce.h"
6 
7 #include "ds_set.h"
8 #include "ds_htable.h"
9 
ds_set_ex(ds_htable_t * table)10 ds_set_t *ds_set_ex(ds_htable_t *table)
11 {
12     ds_set_t *set = ecalloc(1, sizeof(ds_set_t));
13     set->table = table;
14     return set;
15 }
16 
ds_set()17 ds_set_t *ds_set()
18 {
19     return ds_set_ex(ds_htable());
20 }
21 
ds_set_clone(ds_set_t * set)22 ds_set_t *ds_set_clone(ds_set_t *set)
23 {
24     return ds_set_ex(ds_htable_clone(set->table));
25 }
26 
ds_set_allocate(ds_set_t * set,zend_long capacity)27 void ds_set_allocate(ds_set_t *set, zend_long capacity)
28 {
29     ds_htable_ensure_capacity(set->table, capacity);
30 }
31 
ds_set_sort_callback(ds_set_t * set)32 void ds_set_sort_callback(ds_set_t *set)
33 {
34     ds_htable_sort_callback_by_key(set->table);
35 }
36 
ds_set_sort(ds_set_t * set)37 void ds_set_sort(ds_set_t *set)
38 {
39     ds_htable_sort_by_key(set->table);
40 }
41 
ds_set_sorted_callback(ds_set_t * set)42 ds_set_t *ds_set_sorted_callback(ds_set_t *set)
43 {
44     ds_set_t *sorted = ds_set_clone(set);
45     ds_set_sort_callback(sorted);
46     return sorted;
47 }
48 
ds_set_sorted(ds_set_t * set)49 ds_set_t *ds_set_sorted(ds_set_t *set)
50 {
51     ds_set_t *sorted = ds_set_clone(set);
52     ds_set_sort(sorted);
53     return sorted;
54 }
55 
ds_set_add(ds_set_t * set,zval * value)56 void ds_set_add(ds_set_t *set, zval *value)
57 {
58     ds_htable_put(set->table, value, NULL);
59 }
60 
ds_set_add_va(ds_set_t * set,VA_PARAMS)61 void ds_set_add_va(ds_set_t *set, VA_PARAMS)
62 {
63     for (; argc != 0; argc--, argv++) {
64         ds_set_add(set, argv);
65     }
66 }
67 
iterator_add(zend_object_iterator * iterator,void * puser)68 static int iterator_add(zend_object_iterator *iterator, void *puser)
69 {
70     ds_set_add((ds_set_t *) puser, iterator->funcs->get_current_data(iterator));
71     return SUCCESS;
72 }
73 
add_traversable_to_set(ds_set_t * set,zval * obj)74 static inline void add_traversable_to_set(ds_set_t *set, zval *obj)
75 {
76     spl_iterator_apply(obj, iterator_add, set);
77 }
78 
add_array_to_set(ds_set_t * set,HashTable * array)79 static inline void add_array_to_set(ds_set_t *set, HashTable *array)
80 {
81     zval *value;
82     ZEND_HASH_FOREACH_VAL(array, value) {
83         ZVAL_DEREF(value);
84         ds_set_add(set, value);
85     }
86     ZEND_HASH_FOREACH_END();
87 }
88 
ds_set_add_all(ds_set_t * set,zval * values)89 void ds_set_add_all(ds_set_t *set, zval *values)
90 {
91     if (values == NULL) {
92         return;
93     }
94 
95     if (ds_is_array(values)) {
96         add_array_to_set(set, Z_ARRVAL_P(values));
97         return;
98     }
99 
100     if (ds_is_traversable(values)) {
101         add_traversable_to_set(set, values);
102         return;
103     }
104 
105     ARRAY_OR_TRAVERSABLE_REQUIRED();
106 }
107 
ds_set_contains(ds_set_t * set,zval * value)108 bool ds_set_contains(ds_set_t *set, zval *value)
109 {
110     return ds_htable_has_key(set->table, value);
111 }
112 
ds_set_contains_va(ds_set_t * set,VA_PARAMS)113 bool ds_set_contains_va(ds_set_t *set, VA_PARAMS)
114 {
115     return ds_htable_has_keys(set->table, argc, argv);
116 }
117 
ds_set_remove(ds_set_t * set,zval * value)118 static inline void ds_set_remove(ds_set_t *set, zval *value)
119 {
120     ds_htable_remove(set->table, value, NULL);
121 }
122 
ds_set_remove_va(ds_set_t * set,VA_PARAMS)123 void ds_set_remove_va(ds_set_t *set, VA_PARAMS)
124 {
125     while (argc--) {
126         ds_set_remove(set, argv++);
127     }
128 }
129 
ds_set_get(ds_set_t * set,zend_long index)130 zval *ds_set_get(ds_set_t *set, zend_long index)
131 {
132     ds_htable_bucket_t *bucket = ds_htable_lookup_by_position(set->table, index);
133 
134     if (bucket) {
135         return &bucket->key;
136     }
137 
138     INDEX_OUT_OF_RANGE(index, set->table->size);
139     return NULL;
140 }
141 
ds_set_get_first(ds_set_t * set)142 zval *ds_set_get_first(ds_set_t *set)
143 {
144     ds_htable_bucket_t *bucket = ds_htable_lookup_by_position(set->table, 0);
145 
146     if ( ! bucket) {
147         NOT_ALLOWED_WHEN_EMPTY();
148         return NULL;
149     }
150 
151     return &bucket->key;
152 }
153 
ds_set_get_last(ds_set_t * set)154 zval *ds_set_get_last(ds_set_t *set)
155 {
156     ds_htable_bucket_t *bucket = ds_htable_lookup_by_position(set->table, DS_SET_SIZE(set) - 1);
157 
158     if ( ! bucket) {
159         NOT_ALLOWED_WHEN_EMPTY();
160         return NULL;
161     }
162 
163     return &bucket->key;
164 }
165 
ds_set_join(ds_set_t * set,const char * glue,const size_t len,zval * return_value)166 void ds_set_join(ds_set_t *set, const char *glue, const size_t len, zval *return_value)
167 {
168     zend_string *str = ds_htable_join_keys(set->table, glue, len);
169     ZVAL_STR(return_value, str);
170 }
171 
ds_set_slice(ds_set_t * set,zend_long index,zend_long length)172 ds_set_t *ds_set_slice(ds_set_t *set, zend_long index, zend_long length)
173 {
174     return ds_set_ex(ds_htable_slice(set->table, index, length));
175 }
176 
ds_set_diff(ds_set_t * set,ds_set_t * other)177 ds_set_t *ds_set_diff(ds_set_t *set, ds_set_t *other)
178 {
179     return ds_set_ex(ds_htable_diff(set->table, other->table));
180 }
181 
ds_set_assign_diff(ds_set_t * set,ds_set_t * other)182 void ds_set_assign_diff(ds_set_t *set, ds_set_t *other)
183 {
184     zval *value;
185     DS_SET_FOREACH(other, value) {
186         ds_set_remove(set, value);
187     }
188     DS_SET_FOREACH_END();
189 }
190 
ds_set_intersect(ds_set_t * set,ds_set_t * other)191 ds_set_t *ds_set_intersect(ds_set_t *set, ds_set_t *other)
192 {
193     return ds_set_ex(ds_htable_intersect(set->table, other->table));
194 }
195 
ds_set_assign_intersect(ds_set_t * set,ds_set_t * other)196 void ds_set_assign_intersect(ds_set_t *set, ds_set_t *other)
197 {
198     zval *value;
199     DS_SET_FOREACH(set, value) {
200         if ( ! ds_set_contains(other, value)) {
201             ds_set_remove(set, value);
202         }
203     }
204     DS_SET_FOREACH_END();
205 }
206 
207 // Returns a new ds_set_t with buffer in either A or B but not both
ds_set_xor(ds_set_t * set,ds_set_t * other)208 ds_set_t *ds_set_xor(ds_set_t *set, ds_set_t *other)
209 {
210     return ds_set_ex(ds_htable_xor(set->table, other->table));
211 }
212 
213 // Elements in either A or B but not both
ds_set_assign_xor(ds_set_t * set,ds_set_t * other)214 void ds_set_assign_xor(ds_set_t *set, ds_set_t *other)
215 {
216     zval *value;
217 
218     DS_SET_FOREACH(set, value) {
219         if (ds_set_contains(other, value)) {
220             ds_set_remove(set, value);
221         }
222     }
223     DS_SET_FOREACH_END();
224 
225     DS_SET_FOREACH(other, value) {
226         ds_set_remove(set, value);
227     }
228     DS_SET_FOREACH_END();
229 }
230 
ds_set_union(ds_set_t * set,ds_set_t * other)231 ds_set_t *ds_set_union(ds_set_t *set, ds_set_t *other)
232 {
233     return ds_set_ex(ds_htable_merge(set->table, other->table));
234 }
235 
ds_set_merge(ds_set_t * set,zval * values)236 ds_set_t *ds_set_merge(ds_set_t *set, zval *values)
237 {
238     if (values && (ds_is_array(values) || ds_is_traversable(values))) {
239         ds_set_t *merged = ds_set_clone(set);
240         ds_set_add_all(merged, values);
241         return merged;
242     }
243 
244     ARRAY_OR_TRAVERSABLE_REQUIRED();
245     return NULL;
246 }
247 
ds_set_assign_union(ds_set_t * set,ds_set_t * other)248 void ds_set_assign_union(ds_set_t *set, ds_set_t *other)
249 {
250     zval *value;
251     DS_SET_FOREACH(other, value) {
252         ds_set_add(set, value);
253     }
254     DS_SET_FOREACH_END();
255 }
256 
ds_set_clear(ds_set_t * set)257 void ds_set_clear(ds_set_t *set)
258 {
259     ds_htable_clear(set->table);
260 }
261 
ds_set_free(ds_set_t * set)262 void ds_set_free(ds_set_t *set)
263 {
264     ds_htable_free(set->table);
265     efree(set);
266 }
267 
ds_set_reduce(ds_set_t * set,FCI_PARAMS,zval * initial,zval * return_value)268 void ds_set_reduce(ds_set_t *set, FCI_PARAMS, zval *initial, zval *return_value)
269 {
270     zval *value;
271     zval carry;
272     zval params[2];
273 
274     if (initial == NULL) {
275         ZVAL_NULL(&carry);
276     } else {
277         ZVAL_COPY_VALUE(&carry, initial);
278     }
279 
280     DS_SET_FOREACH(set, value) {
281         ZVAL_COPY_VALUE(&params[0], &carry);
282         ZVAL_COPY_VALUE(&params[1], value);
283 
284         fci.param_count = 2;
285         fci.params      = params;
286         fci.retval      = &carry;
287 
288         if (zend_call_function(&fci, &fci_cache) == FAILURE || Z_ISUNDEF(carry)) {
289             ZVAL_NULL(return_value);
290             return;
291         }
292 
293         Z_TRY_DELREF_P(&carry);
294     }
295     DS_SET_FOREACH_END();
296     ZVAL_COPY(return_value, &carry);
297 }
298 
ds_set_map(ds_set_t * set,FCI_PARAMS)299 ds_set_t * ds_set_map(ds_set_t *set, FCI_PARAMS)
300 {
301     ds_set_t *result = ds_set();
302 
303     if (DS_SET_IS_EMPTY(set)) {
304         return result;
305 
306     } else {
307         zval *value;
308 
309         DS_SET_FOREACH(set, value) {
310             zval retval;
311 
312             fci.param_count = 1;
313             fci.params      = value;
314             fci.retval      = &retval;
315 
316             if (zend_call_function(&fci, &fci_cache) == FAILURE || Z_ISUNDEF(retval)) {
317                 ds_set_free(result);
318                 return NULL;
319             }
320 
321             ds_set_add(result, &retval);
322             zval_ptr_dtor(&retval);
323         }
324         DS_SET_FOREACH_END();
325 
326         return result;
327     }
328 }
329 
ds_set_filter_callback(ds_set_t * set,FCI_PARAMS)330 ds_set_t *ds_set_filter_callback(ds_set_t *set, FCI_PARAMS)
331 {
332     ds_set_t *result = ds_set();
333 
334     if (DS_SET_IS_EMPTY(set)) {
335         return result;
336 
337     } else {
338         zval *value;
339 
340         DS_SET_FOREACH(set, value) {
341             zval retval;
342 
343             fci.param_count = 1;
344             fci.params      = value;
345             fci.retval      = &retval;
346 
347             if (zend_call_function(&fci, &fci_cache) == FAILURE || Z_ISUNDEF(retval)) {
348                 ds_set_free(result);
349                 return NULL;
350             }
351 
352             if (EXPECTED_BOOL_IS_TRUE(&retval)) {
353                 ds_set_add(result, value);
354             }
355 
356             zval_ptr_dtor(&retval);
357         }
358         DS_SET_FOREACH_END();
359 
360         return result;
361     }
362 }
363 
ds_set_filter(ds_set_t * set)364 ds_set_t *ds_set_filter(ds_set_t *set)
365 {
366     ds_set_t *result = ds_set();
367 
368     if (DS_SET_IS_EMPTY(set)) {
369         return result;
370 
371     } else {
372         zval *value;
373         DS_SET_FOREACH(set, value) {
374             if (zend_is_true(value)) {
375                 ds_set_add(result, value);
376             }
377         }
378         DS_SET_FOREACH_END();
379 
380         return result;
381     }
382 }
383 
ds_set_reverse(ds_set_t * set)384 void ds_set_reverse(ds_set_t *set)
385 {
386     ds_htable_reverse(set->table);
387 }
388 
ds_set_reversed(ds_set_t * set)389 ds_set_t *ds_set_reversed(ds_set_t *set)
390 {
391     return ds_set_ex(ds_htable_reversed(set->table));
392 }
393 
ds_set_to_array(ds_set_t * set,zval * arr)394 void ds_set_to_array(ds_set_t *set, zval *arr)
395 {
396     zval *value;
397 
398     array_init_size(arr, set->table->size);
399 
400     DS_HTABLE_FOREACH_KEY(set->table, value) {
401         add_next_index_zval(arr, value);
402         Z_TRY_ADDREF_P(value);
403     }
404     DS_HTABLE_FOREACH_END();
405 }
406 
ds_set_sum(ds_set_t * set,zval * return_value)407 void ds_set_sum(ds_set_t *set, zval *return_value)
408 {
409     zval *value;
410     ZVAL_LONG(return_value, 0);
411 
412     DS_SET_FOREACH(set, value) {
413         DS_ADD_TO_SUM(value, return_value);
414     }
415     DS_SET_FOREACH_END();
416 }
417