#include "../php/iterators/php_vector_iterator.h" #include "../php/handlers/php_vector_handlers.h" #include "../php/classes/php_vector_ce.h" #include "ds_vector.h" static inline bool index_out_of_range(zend_long index, zend_long max) { if (index < 0 || index >= max) { INDEX_OUT_OF_RANGE(index, max); return true; } return false; } static inline void ds_vector_reallocate(ds_vector_t *vector, zend_long capacity) { vector->buffer = ds_reallocate_zval_buffer(vector->buffer, capacity, vector->capacity, vector->size); vector->capacity = capacity; } ds_vector_t *ds_vector_ex(zend_long capacity) { ds_vector_t *vector = ecalloc(1, sizeof(ds_vector_t)); // Make sure that capacity is valid. capacity = MAX(capacity, DS_VECTOR_MIN_CAPACITY); vector->buffer = ds_allocate_zval_buffer(capacity); vector->capacity = capacity; vector->size = 0; return vector; } ds_vector_t *ds_vector() { return ds_vector_ex(DS_VECTOR_MIN_CAPACITY); } ds_vector_t *ds_vector_clone(ds_vector_t *vector) { if (DS_VECTOR_IS_EMPTY(vector)) { return ds_vector(); } else { ds_vector_t *clone = ecalloc(1, sizeof(ds_vector_t)); clone->buffer = ds_allocate_zval_buffer(vector->capacity); clone->capacity = vector->capacity; clone->size = vector->size; COPY_ZVAL_BUFFER(clone->buffer, vector->buffer, vector->size); return clone; } } ds_vector_t *ds_vector_from_buffer(zval *buffer, zend_long capacity, zend_long size) { ds_vector_t *vector = ecalloc(1, sizeof(ds_vector_t)); // Make sure that the buffer is at least the minimum length. if (capacity < DS_VECTOR_MIN_CAPACITY) { buffer = ds_reallocate_zval_buffer(buffer, DS_VECTOR_MIN_CAPACITY, capacity, size); capacity = DS_VECTOR_MIN_CAPACITY; } vector->buffer = buffer; vector->capacity = capacity; vector->size = size; return vector; } void ds_vector_allocate(ds_vector_t *vector, zend_long capacity) { if (capacity > vector->capacity) { ds_vector_reallocate(vector, capacity); } } static inline void ds_vector_increase_capacity(ds_vector_t *vector) { ds_vector_reallocate(vector, vector->capacity + (vector->capacity >> 1)); } static inline void ds_vector_ensure_capacity(ds_vector_t *vector, zend_long capacity) { if (capacity > vector->capacity) { zend_long boundary = vector->capacity + (vector->capacity >> 1); ds_vector_reallocate(vector, MAX(capacity, boundary)); } } static inline void ds_vector_auto_truncate(ds_vector_t *vector) { const zend_long c = vector->capacity; const zend_long n = vector->size; if (n <= c / 4 && c / 2 >= DS_VECTOR_MIN_CAPACITY) { ds_vector_reallocate(vector, c / 2); } } void ds_vector_remove(ds_vector_t *vector, zend_long index, zval *return_value) { if (index_out_of_range(index, vector->size)) { return; } if (index == vector->size - 1) { ds_vector_pop(vector, return_value); } else { zval *pos = vector->buffer + index; if (return_value) { ZVAL_COPY(return_value, pos); } if ( ! Z_ISUNDEF_P(pos)) { zval_ptr_dtor(pos); } memmove(pos, pos + 1, sizeof(zval) * (vector->size - index)); vector->size--; ds_vector_auto_truncate(vector); } } zval *ds_vector_get(ds_vector_t *vector, zend_long index) { if (index_out_of_range(index, vector->size)) { return NULL; } return vector->buffer + index; } static inline void increase_capacity_if_full(ds_vector_t *vector) { if (vector->size == vector->capacity) { ds_vector_increase_capacity(vector); } } static inline void ds_vector_clear_buffer(ds_vector_t *vector) { zval *pos = vector->buffer; zval *end = vector->buffer + vector->size; for (; pos != end; ++pos) { DTOR_AND_UNDEF(pos); } vector->size = 0; } void ds_vector_clear(ds_vector_t *vector) { if (vector->size > 0) { ds_vector_clear_buffer(vector); if (vector->capacity > DS_VECTOR_MIN_CAPACITY) { ds_vector_reallocate(vector, DS_VECTOR_MIN_CAPACITY); } } } void ds_vector_set(ds_vector_t *vector, zend_long index, zval *value) { if ( ! index_out_of_range(index, vector->size)) { zval *ptr = vector->buffer + index; zval_ptr_dtor(ptr); ZVAL_COPY(ptr, value); } } void ds_vector_to_array(ds_vector_t *vector, zval *return_value) { zend_long size = vector->size; if (size == 0) { array_init(return_value); } else { zval *pos = vector->buffer; zval *end = pos + size; array_init_size(return_value, size); for (; pos != end; ++pos) { add_next_index_zval(return_value, pos); Z_TRY_ADDREF_P(pos); } } } static inline zend_long ds_vector_find_index(ds_vector_t *vector, zval *value) { zval *pos = vector->buffer; zval *end = vector->buffer + vector->size; for (; pos != end; ++pos) { if (zend_is_identical(value, pos)) { return pos - vector->buffer; } } return FAILURE; } void ds_vector_find(ds_vector_t *vector, zval *value, zval *return_value) { zend_long index = ds_vector_find_index(vector, value); if (index >= 0) { ZVAL_LONG(return_value, index); return; } ZVAL_FALSE(return_value); } bool ds_vector_contains(ds_vector_t *vector, zval *value) { return ds_vector_find_index(vector, value) != FAILURE; } bool ds_vector_contains_va(ds_vector_t *vector, VA_PARAMS) { while (argc-- > 0) { if ( ! ds_vector_contains(vector, argv++)) { return false; } } return true; } void ds_vector_join(ds_vector_t *vector, char *str, size_t len, zval *return_value) { zend_string *s; s = ds_join_zval_buffer(vector->buffer, DS_VECTOR_SIZE(vector), str, len); ZVAL_STR(return_value, s); } void ds_vector_insert_va(ds_vector_t *vector, zend_long index, VA_PARAMS) { if ( ! index_out_of_range(index, vector->size + 1) && argc > 0) { zend_long len; zval *src; zval *dst; zval *end; ds_vector_ensure_capacity(vector, vector->size + argc); src = argv; dst = vector->buffer + index; end = dst + argc; len = vector->size - index; if (len > 0) { memmove(end, dst, (vector->size - index) * sizeof(zval)); } for (; dst != end; ++dst, ++src) { ZVAL_COPY(dst, src); } vector->size += argc; } } void ds_vector_insert(ds_vector_t *vector, zend_long index, zval *value) { ds_vector_insert_va(vector, index, 1, value); } void ds_vector_push(ds_vector_t *vector, zval *value) { increase_capacity_if_full(vector); ZVAL_COPY(&vector->buffer[vector->size++], value); } void ds_vector_push_va(ds_vector_t *vector, VA_PARAMS) { if (argc == 1) { ds_vector_push(vector, argv); return; } if (argc > 0) { zval *src, *dst, *end; ds_vector_ensure_capacity(vector, vector->size + argc); src = argv; dst = &vector->buffer[vector->size]; end = dst + argc; while (dst != end) { ZVAL_COPY(dst++, src++); } vector->size += argc; } } void ds_vector_unshift(ds_vector_t *vector, zval *value) { ds_vector_insert(vector, 0, value); } void ds_vector_unshift_va(ds_vector_t *vector, VA_PARAMS) { if (argc == 1) { ds_vector_unshift(vector, argv); return; } if (argc > 0) { zval *dst, *src, *end; ds_vector_ensure_capacity(vector, vector->size + argc); src = argv; dst = vector->buffer; end = dst + argc; memmove(end, dst, vector->size * sizeof(zval)); while (dst != end) { ZVAL_COPY(dst++, src++); } vector->size += argc; } } void ds_vector_sort_callback(ds_vector_t *vector) { ds_user_sort_zval_buffer(vector->buffer, vector->size); } void ds_vector_sort(ds_vector_t *vector) { ds_sort_zval_buffer(vector->buffer, vector->size); } bool ds_vector_isset(ds_vector_t *vector, zend_long index, int check_empty) { if (index < 0 || index >= vector->size) { return 0; } return ds_zval_isset(vector->buffer + index, check_empty); } bool ds_vector_index_exists(ds_vector_t *vector, zend_long index) { return index >= 0 && index < vector->size; } static int iterator_add(zend_object_iterator *iterator, void *puser) { ds_vector_push((ds_vector_t *) puser, iterator->funcs->get_current_data(iterator)); return ZEND_HASH_APPLY_KEEP; } static inline void add_traversable_to_vector(ds_vector_t *vector, zval *obj) { spl_iterator_apply(obj, iterator_add, (void*) vector); } static inline void add_array_to_vector(ds_vector_t *vector, HashTable *array) { zval *value; ds_vector_ensure_capacity(vector, vector->size + array->nNumOfElements); ZEND_HASH_FOREACH_VAL(array, value) { ds_vector_push(vector, value); } ZEND_HASH_FOREACH_END(); } void ds_vector_rotate(ds_vector_t *vector, zend_long r) { zval *a, *b, *c; zend_long n = vector->size; if (n < 2) { return; } // Negative rotation should rotate in the opposite direction if (r < 0) r = n - (llabs(r) % n); else if (r > n) r = r % n; // There's no need to rotate if the sequence won't be affected. if (r == 0 || r == n) return; a = vector->buffer; // Start of buffer b = a + r; // Pivot c = a + n; // End of buffer // [a..b....c] ds_reverse_zval_range(a, b); ds_reverse_zval_range(b, c); ds_reverse_zval_range(a, c); } void ds_vector_push_all(ds_vector_t *vector, zval *values) { if ( ! values) { return; } if (ds_is_array(values)) { add_array_to_vector(vector, Z_ARRVAL_P(values)); return; } if (ds_is_traversable(values)) { add_traversable_to_vector(vector, values); return; } ARRAY_OR_TRAVERSABLE_REQUIRED(); } ds_vector_t *ds_vector_merge(ds_vector_t *vector, zval *values) { if (values && (ds_is_array(values) || ds_is_traversable(values))) { ds_vector_t *merged = ds_vector_clone(vector); ds_vector_push_all(merged, values); return merged; } ARRAY_OR_TRAVERSABLE_REQUIRED(); return NULL; } void ds_vector_pop(ds_vector_t *vector, zval *return_value) { SET_AS_RETURN_AND_UNDEF(&vector->buffer[--vector->size]); ds_vector_auto_truncate(vector); } void ds_vector_pop_throw(ds_vector_t *vector, zval *return_value) { if (DS_VECTOR_IS_EMPTY(vector)) { NOT_ALLOWED_WHEN_EMPTY(); return; } ds_vector_pop(vector, return_value); } void ds_vector_shift(ds_vector_t *vector, zval *return_value) { zval *first = vector->buffer; SET_AS_RETURN_AND_UNDEF(first); vector->size--; memmove(first, first + 1, vector->size * sizeof(zval)); ds_vector_auto_truncate(vector); } void ds_vector_shift_throw(ds_vector_t *vector, zval *return_value) { if (DS_VECTOR_IS_EMPTY(vector)) { NOT_ALLOWED_WHEN_EMPTY(); return; } ds_vector_shift(vector, return_value); } zval *ds_vector_get_last(ds_vector_t *vector) { return &vector->buffer[vector->size - 1]; } zval *ds_vector_get_last_throw(ds_vector_t *vector) { if (DS_VECTOR_IS_EMPTY(vector)) { NOT_ALLOWED_WHEN_EMPTY(); return NULL; } return ds_vector_get_last(vector); } zval *ds_vector_get_first(ds_vector_t *vector) { return &vector->buffer[0]; } zval *ds_vector_get_first_throw(ds_vector_t *vector) { if (DS_VECTOR_IS_EMPTY(vector)) { NOT_ALLOWED_WHEN_EMPTY(); return NULL; } return ds_vector_get_first(vector); } void ds_vector_reverse(ds_vector_t *vector) { ds_reverse_zval_range(vector->buffer, vector->buffer + vector->size); } ds_vector_t *ds_vector_reversed(ds_vector_t *vector) { zval *value; zval *buffer = ds_allocate_zval_buffer(vector->capacity); zval *target = &buffer[vector->size - 1]; DS_VECTOR_FOREACH(vector, value) { ZVAL_COPY(target--, value); } DS_VECTOR_FOREACH_END(); return ds_vector_from_buffer(buffer, vector->capacity, vector->size); } void ds_vector_apply(ds_vector_t *vector, FCI_PARAMS) { zval retval; zval *value; DS_VECTOR_FOREACH(vector, value) { fci.param_count = 1; fci.params = value; fci.retval = &retval; if (zend_call_function(&fci, &fci_cache) == FAILURE || Z_ISUNDEF(retval)) { return; } zval_ptr_dtor(value); ZVAL_COPY_VALUE(value, &retval); } DS_VECTOR_FOREACH_END(); } ds_vector_t *ds_vector_map(ds_vector_t *vector, FCI_PARAMS) { zval retval; zval *value; zval *buffer = ds_allocate_zval_buffer(vector->capacity); zval *target = buffer; DS_VECTOR_FOREACH(vector, value) { fci.param_count = 1; fci.params = value; fci.retval = &retval; if (zend_call_function(&fci, &fci_cache) == FAILURE || Z_ISUNDEF(retval)) { // Release the values copied into the buffer on failure. for (; target > buffer; target--) { zval_ptr_dtor(target - 1); } zval_ptr_dtor(&retval); efree(buffer); return NULL; } ZVAL_COPY(target++, &retval); zval_ptr_dtor(&retval); } DS_VECTOR_FOREACH_END(); return ds_vector_from_buffer(buffer, vector->capacity, vector->size); } ds_vector_t *ds_vector_filter(ds_vector_t *vector) { if (DS_VECTOR_IS_EMPTY(vector)) { return ds_vector(); } else { zval *value; zval *buffer = ds_allocate_zval_buffer(vector->size); zval *target = buffer; DS_VECTOR_FOREACH(vector, value) { if (zend_is_true(value)) { ZVAL_COPY(target++, value); } } DS_VECTOR_FOREACH_END(); return ds_vector_from_buffer(buffer, vector->size, (target - buffer)); } } ds_vector_t *ds_vector_filter_callback(ds_vector_t *vector, FCI_PARAMS) { if (DS_VECTOR_IS_EMPTY(vector)) { return ds_vector(); } else { zval retval; zval *value; zval *buffer = ds_allocate_zval_buffer(vector->size); zval *target = buffer; DS_VECTOR_FOREACH(vector, value) { fci.param_count = 1; fci.params = value; fci.retval = &retval; // Catch potential exceptions or other errors during comparison. if (zend_call_function(&fci, &fci_cache) == FAILURE || Z_ISUNDEF(retval)) { // Release the values copied into the buffer on failure. for (; target > buffer; target--) { zval_ptr_dtor(target - 1); } zval_ptr_dtor(&retval); efree(buffer); return NULL; } // Copy the value into the buffer if the callback returned true. if (EXPECTED_BOOL_IS_TRUE(&retval)) { ZVAL_COPY(target++, value); } zval_ptr_dtor(&retval); } DS_VECTOR_FOREACH_END(); return ds_vector_from_buffer(buffer, vector->size, (target - buffer)); } } void ds_vector_reduce(ds_vector_t *vector, zval *initial, zval *return_value, FCI_PARAMS) { zval *value; zval carry; zval params[2]; if (initial == NULL) { ZVAL_NULL(&carry); } else { ZVAL_COPY_VALUE(&carry, initial); } DS_VECTOR_FOREACH(vector, value) { ZVAL_COPY_VALUE(¶ms[0], &carry); ZVAL_COPY_VALUE(¶ms[1], value); fci.param_count = 2; fci.params = params; fci.retval = &carry; if (zend_call_function(&fci, &fci_cache) == FAILURE || Z_ISUNDEF(carry)) { zval_ptr_dtor(&carry); ZVAL_NULL(return_value); return; } Z_TRY_DELREF_P(&carry); } DS_VECTOR_FOREACH_END(); ZVAL_COPY(return_value, &carry); } ds_vector_t *ds_vector_slice(ds_vector_t *vector, zend_long index, zend_long length) { ds_normalize_slice_args(&index, &length, vector->size); if (length == 0) { return ds_vector(); } else { zend_long capacity = MAX(length, DS_VECTOR_MIN_CAPACITY); zval *buf = ds_allocate_zval_buffer(capacity); zval *src = vector->buffer + index; zval *end = vector->buffer + index + length; zval *dst = buf; while (src < end) { ZVAL_COPY(dst++, src++); } return ds_vector_from_buffer(buf, capacity, length); } } void ds_vector_sum(ds_vector_t *vector, zval *return_value) { zval *value; ZVAL_LONG(return_value, 0); DS_VECTOR_FOREACH(vector, value) { DS_ADD_TO_SUM(value, return_value); } DS_VECTOR_FOREACH_END(); } void ds_vector_free(ds_vector_t *vector) { ds_vector_clear_buffer(vector); efree(vector->buffer); efree(vector); }