xref: /ext-ds/src/common.c (revision 34885a6f)
1 #include "common.h"
2 
ds_allocate_zval_buffer(zend_long length)3 zval *ds_allocate_zval_buffer(zend_long length)
4 {
5     return ecalloc(length, sizeof(zval));
6 }
7 
ds_next_power_of_2(uint32_t n,uint32_t min)8 uint32_t ds_next_power_of_2(uint32_t n, uint32_t min)
9 {
10     if (n < min) return min;
11 
12     n--;
13     n |= n >>  1;
14     n |= n >>  2;
15     n |= n >>  4;
16     n |= n >>  8;
17     n |= n >> 16;
18     n++;
19 
20     return n;
21 }
22 
ds_reallocate_zval_buffer(zval * buffer,zend_long length,zend_long current,zend_long used)23 zval *ds_reallocate_zval_buffer(
24     zval *buffer,
25     zend_long length,
26     zend_long current,
27     zend_long used
28 ) {
29     if (length == current) {
30         return buffer;
31     }
32 
33     // Destruct zvals if we're truncating the buffer.
34     if (length < used) {
35         zend_long i;
36 
37         for (i = length; i < used; i++) {
38             DTOR_AND_UNDEF(&buffer[i]);
39         }
40     }
41 
42     buffer = erealloc(buffer, length * sizeof(zval));
43 
44     // Clear out any new memory that was allocated.
45     if (length > current) {
46         memset(buffer + current, 0, (length - current) * sizeof(zval));
47     }
48 
49     return buffer;
50 }
51 
ds_zval_user_compare_func(const void * a,const void * b)52 static int ds_zval_user_compare_func(const void *a, const void *b)
53 {
54     zval params[2];
55     zval retval;
56 
57     zval *x = (zval*) a;
58     zval *y = (zval*) b;
59 
60     ZVAL_COPY_VALUE(&params[0], x);
61     ZVAL_COPY_VALUE(&params[1], y);
62 
63     DSG(user_compare_fci).param_count = 2;
64     DSG(user_compare_fci).params      = params;
65     DSG(user_compare_fci).retval      = &retval;
66 
67     if (zend_call_function(
68             &DSG(user_compare_fci),
69             &DSG(user_compare_fci_cache)) == SUCCESS) {
70 
71         return (int) zval_get_long(&retval);
72     }
73 
74     return 0;
75 }
76 
ds_zval_compare_func(const void * a,const void * b)77 static int ds_zval_compare_func(const void *a, const void *b)
78 {
79     zval retval;
80 
81     zval *x = (zval*) a;
82     zval *y = (zval*) b;
83 
84     if (compare_function(&retval, x, y) == SUCCESS) {
85         return (int) zval_get_long(&retval);
86     }
87 
88     return 0;
89 }
90 
ds_sort_zval_buffer(zval * buffer,zend_long size)91 void ds_sort_zval_buffer(zval *buffer, zend_long size)
92 {
93     qsort(buffer, size, sizeof(zval), ds_zval_compare_func);
94 }
95 
ds_user_sort_zval_buffer(zval * buffer,zend_long size)96 void ds_user_sort_zval_buffer(zval *buffer, zend_long size)
97 {
98     qsort(buffer, size, sizeof(zval), ds_zval_user_compare_func);
99 }
100 
ds_zval_isset(zval * value,int check_empty)101 int ds_zval_isset(zval *value, int check_empty)
102 {
103     if (value == NULL) {
104         return 0;
105     }
106 
107     if ( ! check_empty) {
108         return Z_TYPE_P(value) != IS_NULL;
109     }
110 
111     return zend_is_true(value);
112 }
113 
ds_normalize_slice_args(zend_long * offset,zend_long * length,zend_long size)114 void ds_normalize_slice_args(
115     zend_long *offset,
116     zend_long *length,
117     zend_long size
118 ) {
119     zend_long idx = *offset;
120     zend_long len = *length;
121 
122     // If the offset is beyond the end or the length is zero, it's an empty slice.
123     if (size == 0 || idx >= size) {
124         *offset = 0;
125         *length = 0;
126 
127     } else {
128 
129         // If index is negative, start that far from the end.
130         if (idx < 0) {
131             idx = MAX(0, size + idx);
132         }
133 
134         // If length is given and negative, stop that far from the end.
135         if (len < 0) {
136             len = MAX(0, (size + len) - idx);
137         }
138 
139         // If the length extends beyond the end, only go up to the end.
140         if ((idx + len) > size) {
141             len = MAX(0, size - idx);
142         }
143 
144         *offset = idx;
145         *length = len;
146     }
147 }
148 
smart_str_appendz(smart_str * buffer,zval * value)149 void smart_str_appendz(smart_str *buffer, zval *value)
150 {
151     switch (Z_TYPE_P(value)) {
152         case IS_STRING:
153             smart_str_append(buffer, Z_STR_P(value));
154             return;
155         case IS_LONG:
156             smart_str_append_long(buffer, Z_LVAL_P(value));
157             return;
158     }
159 
160     zend_string *str = zval_get_string(value);
161     smart_str_append(buffer, str);
162     zend_string_free(str);
163 }
164 
ds_join_zval_buffer(zval * buffer,zend_long size,char * glue,size_t len)165 zend_string *ds_join_zval_buffer(
166     zval        *buffer,
167     zend_long    size,
168     char        *glue,
169     size_t       len
170 ) {
171     smart_str str = {0};
172 
173     if (size <= 0) {
174         return ZSTR_EMPTY_ALLOC();
175     }
176 
177     if (size == 1) {
178         return zval_get_string(buffer);
179     }
180 
181     // Glue is optional, will use empty string by default if NULL
182     if (glue && len) {
183         zval *pos = buffer;
184         zval *end = buffer + size - 1; // Exclude last value
185 
186         // Append each part and the glue right up to the last value.
187         do {
188             smart_str_appendz(&str, pos);
189             smart_str_appendl(&str, glue, len);
190         } while (++pos != end);
191 
192         // Append last value
193         smart_str_appendz(&str, pos);
194 
195     } else {
196         zval *pos = buffer;
197         zval *end = buffer + size;
198 
199         // Append each part including the last, without glue.
200         do {
201             smart_str_appendz(&str, pos);
202         } while (++pos != end);
203     }
204 
205     smart_str_0(&str);
206     return str.s;
207 }
208 
ds_is_traversable(zval * value)209 bool ds_is_traversable(zval *value)
210 {
211     return Z_TYPE_P(value) == IS_OBJECT &&
212         instanceof_function(Z_OBJCE_P(value), zend_ce_traversable);
213 }
214 
ds_is_array(zval * value)215 bool ds_is_array(zval *value)
216 {
217     return Z_TYPE_P(value) == IS_ARRAY;
218 }
219 
ds_php_array_uses_keys(HashTable * ht)220 bool ds_php_array_uses_keys(HashTable *ht)
221 {
222     zend_string     *key;
223     zend_long       index;
224     zend_long       expected = 0;
225 
226     ZEND_HASH_FOREACH_KEY(ht, index, key) {
227         if (key || index != expected++) {
228             return true;
229         }
230     }
231 
232     ZEND_HASH_FOREACH_END();
233     return false;
234 }
235 
ds_reverse_zval_range(zval * x,zval * y)236 void ds_reverse_zval_range(zval *x, zval *y)
237 {
238     for (; x < --y; ++x) SWAP_ZVAL(*x, *y);
239 }
240 
ds_throw_exception(zend_class_entry * ce,const char * format,...)241 void ds_throw_exception(zend_class_entry *ce, const char *format, ...)
242 {
243     va_list ap;
244     zend_string *str;
245 
246     va_start(ap, format);
247     str = vstrpprintf(0, format, ap);
248     va_end(ap);
249 
250     zend_throw_exception(ce, str->val, 0);
251     zend_string_free(str);
252 }
253