1 #include "../../common.h"
2 
3 #include "../../ds/ds_htable.h"
4 #include "php_htable_iterator.h"
5 
find_starting_bucket(ds_htable_t * table)6 static ds_htable_bucket_t *find_starting_bucket(ds_htable_t *table)
7 {
8     ds_htable_bucket_t *bucket = table->buckets;
9 
10     if (table->size != 0) {
11         ds_htable_bucket_t *last = table->buckets + table->capacity;
12 
13         while (bucket != last && DS_HTABLE_BUCKET_DELETED(bucket)) {
14             ++bucket;
15         }
16     }
17 
18     return bucket;
19 }
20 
php_ds_htable_iterator_dtor(zend_object_iterator * iter)21 static void php_ds_htable_iterator_dtor(zend_object_iterator *iter)
22 {
23     ds_htable_iterator_t *iterator = (ds_htable_iterator_t *) iter;
24 
25     OBJ_RELEASE(iterator->obj);
26     DTOR_AND_UNDEF(&iterator->intern.data);
27 }
28 
php_ds_htable_iterator_valid(zend_object_iterator * iter)29 static int php_ds_htable_iterator_valid(zend_object_iterator *iter)
30 {
31     ds_htable_iterator_t *iterator = (ds_htable_iterator_t *) iter;
32     uint32_t size                  = iterator->table->size;
33     uint32_t position              = iterator->position;
34 
35     return position < size ? SUCCESS : FAILURE;
36 }
37 
php_ds_htable_iterator_get_current_value(zend_object_iterator * iter)38 static zval *php_ds_htable_iterator_get_current_value(zend_object_iterator *iter)
39 {
40     ds_htable_iterator_t *iterator = (ds_htable_iterator_t *) iter;
41     ds_htable_bucket_t   *bucket   = iterator->bucket;
42 
43     if ( ! DS_HTABLE_BUCKET_DELETED(bucket)) {
44         return &bucket->value;
45     }
46 
47     return NULL;
48 }
49 
php_ds_htable_iterator_get_current_keyval(zend_object_iterator * iter)50 static zval *php_ds_htable_iterator_get_current_keyval(zend_object_iterator *iter)
51 {
52     ds_htable_iterator_t *iterator = (ds_htable_iterator_t *) iter;
53     ds_htable_bucket_t   *bucket   = iterator->bucket;
54 
55     if ( ! DS_HTABLE_BUCKET_DELETED(bucket)) {
56         return &bucket->key;
57     }
58 
59     return NULL;
60 }
61 
php_ds_htable_iterator_get_current_key(zend_object_iterator * iter,zval * key)62 static void php_ds_htable_iterator_get_current_key(zend_object_iterator *iter, zval *key)
63 {
64     ds_htable_iterator_t *iterator = (ds_htable_iterator_t *) iter;
65     ds_htable_bucket_t   *bucket   = iterator->bucket;
66 
67     if ( ! DS_HTABLE_BUCKET_DELETED(bucket)) {
68         ZVAL_COPY(key, &bucket->key);
69     }
70 }
71 
php_ds_htable_iterator_get_current_pair(zend_object_iterator * iter)72 static zval *php_ds_htable_iterator_get_current_pair(zend_object_iterator *iter)
73 {
74     ds_htable_iterator_t *iterator = (ds_htable_iterator_t *) iter;
75     ds_htable_bucket_t   *bucket   = iterator->bucket;
76 
77     if ( ! DS_HTABLE_BUCKET_DELETED(bucket)) {
78 
79         zval *key = &bucket->key;
80         zval *val = &bucket->value;
81 
82         zval *arr = &iterator->intern.data;
83 
84         Z_TRY_ADDREF_P(key);
85         Z_TRY_ADDREF_P(val);
86 
87         array_init_size(arr, 2);
88 
89         add_next_index_zval(arr, key);
90         add_next_index_zval(arr, val);
91 
92         return arr;
93     }
94 
95     return NULL;
96 }
97 
php_ds_htable_iterator_get_current_pos(zend_object_iterator * iter,zval * key)98 static void php_ds_htable_iterator_get_current_pos(zend_object_iterator *iter, zval *key)
99 {
100     ZVAL_LONG(key, ((ds_htable_iterator_t *) iter)->position);
101 }
102 
php_ds_htable_iterator_move_forward(zend_object_iterator * iter)103 static void php_ds_htable_iterator_move_forward(zend_object_iterator *iter)
104 {
105     ds_htable_iterator_t *iterator = (ds_htable_iterator_t *) iter;
106 
107     if (++iterator->position < iterator->table->size) {
108         do {
109             ++iterator->bucket;
110         } while (DS_HTABLE_BUCKET_DELETED(iterator->bucket));
111     }
112 }
113 
php_ds_htable_iterator_rewind(zend_object_iterator * iter)114 static void php_ds_htable_iterator_rewind(zend_object_iterator *iter)
115 {
116     ds_htable_iterator_t *iterator = (ds_htable_iterator_t *) iter;
117 
118     iterator->position = 0;
119     iterator->bucket   = find_starting_bucket(iterator->table);
120 }
121 
122 static zend_object_iterator_funcs php_ds_htable_get_value_iterator_funcs = {
123     php_ds_htable_iterator_dtor,
124     php_ds_htable_iterator_valid,
125     php_ds_htable_iterator_get_current_value, // value
126     php_ds_htable_iterator_get_current_pos,   // key
127     php_ds_htable_iterator_move_forward,
128     php_ds_htable_iterator_rewind
129 };
130 
131 static zend_object_iterator_funcs php_ds_htable_get_key_iterator_funcs = {
132     php_ds_htable_iterator_dtor,
133     php_ds_htable_iterator_valid,
134     php_ds_htable_iterator_get_current_keyval, // value
135     php_ds_htable_iterator_get_current_pos,    // key
136     php_ds_htable_iterator_move_forward,
137     php_ds_htable_iterator_rewind
138 };
139 
140 static zend_object_iterator_funcs php_ds_htable_get_pair_iterator_funcs = {
141     php_ds_htable_iterator_dtor,
142     php_ds_htable_iterator_valid,
143     php_ds_htable_iterator_get_current_pair, // value
144     php_ds_htable_iterator_get_current_pos,  // key
145     php_ds_htable_iterator_move_forward,
146     php_ds_htable_iterator_rewind
147 };
148 
149 static zend_object_iterator_funcs php_ds_htable_get_assoc_iterator_funcs = {
150     php_ds_htable_iterator_dtor,
151     php_ds_htable_iterator_valid,
152     php_ds_htable_iterator_get_current_value, // value
153     php_ds_htable_iterator_get_current_key,   // key
154     php_ds_htable_iterator_move_forward,
155     php_ds_htable_iterator_rewind
156 };
157 
158 
php_ds_htable_create_htable_iterator(zval * obj,ds_htable_t * table,zend_object_iterator_funcs * funcs,int by_ref)159 static zend_object_iterator *php_ds_htable_create_htable_iterator(
160     zval *obj,
161     ds_htable_t *table,
162     zend_object_iterator_funcs *funcs,
163     int by_ref
164 ) {
165     ds_htable_iterator_t *iterator;
166 
167     if (by_ref) {
168         ITERATION_BY_REF_NOT_SUPPORTED();
169         return NULL;
170     }
171 
172     iterator = ecalloc(1, sizeof(ds_htable_iterator_t));
173 
174     zend_iterator_init((zend_object_iterator*) iterator);
175 
176     ZVAL_UNDEF(&iterator->intern.data);
177 
178     iterator->intern.funcs  = funcs;
179     iterator->table         = table;
180     iterator->obj           = Z_OBJ_P(obj);
181 
182     // Add a reference to the object so that it doesn't get collected when
183     // the iterated object is implict, eg. foreach ($obj->getInstance() as $value){ ... }
184 #if PHP_VERSION_ID >= 70300
185     GC_ADDREF(iterator->obj);
186 #else
187     ++GC_REFCOUNT(iterator->obj);
188 #endif
189 
190     return (zend_object_iterator *) iterator;
191 }
192 
php_ds_htable_get_value_iterator_ex(zend_class_entry * ce,zval * obj,int by_ref,ds_htable_t * table)193 zend_object_iterator *php_ds_htable_get_value_iterator_ex(
194     zend_class_entry *ce,
195     zval *obj,
196     int by_ref,
197     ds_htable_t *table
198 ){
199     return php_ds_htable_create_htable_iterator(
200         obj, table, &php_ds_htable_get_value_iterator_funcs, by_ref);
201 }
202 
php_ds_htable_get_key_iterator_ex(zend_class_entry * ce,zval * obj,int by_ref,ds_htable_t * table)203 zend_object_iterator *php_ds_htable_get_key_iterator_ex(
204     zend_class_entry *ce,
205     zval *obj,
206     int by_ref,
207     ds_htable_t *table
208 ){
209     return php_ds_htable_create_htable_iterator(
210         obj, table, &php_ds_htable_get_key_iterator_funcs, by_ref);
211 }
212 
php_ds_htable_get_pair_iterator_ex(zend_class_entry * ce,zval * obj,int by_ref,ds_htable_t * table)213 zend_object_iterator *php_ds_htable_get_pair_iterator_ex(
214     zend_class_entry *ce,
215     zval *obj,
216     int by_ref,
217     ds_htable_t *table
218 ){
219     return php_ds_htable_create_htable_iterator(
220         obj, table, &php_ds_htable_get_pair_iterator_funcs, by_ref);
221 }
222 
php_ds_htable_get_assoc_iterator_ex(zend_class_entry * ce,zval * obj,int by_ref,ds_htable_t * table)223 zend_object_iterator *php_ds_htable_get_assoc_iterator_ex(
224     zend_class_entry *ce,
225     zval *obj,
226     int by_ref,
227     ds_htable_t *table
228 ){
229     return php_ds_htable_create_htable_iterator(
230         obj, table, &php_ds_htable_get_assoc_iterator_funcs, by_ref);
231 }
232