Skip to content

Commit c50687e

Browse files
committed
Prototype iterator_zip
1 parent e4ad271 commit c50687e

File tree

6 files changed

+383
-2
lines changed

6 files changed

+383
-2
lines changed

Zend/zend_interfaces.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,12 +509,17 @@ ZEND_API zend_result zend_create_internal_iterator_zval(zval *return_value, zval
509509
return FAILURE;
510510
}
511511

512+
zend_create_internal_iterator_iter(return_value, iter);
513+
return SUCCESS;
514+
}
515+
516+
ZEND_API void zend_create_internal_iterator_iter(zval *return_value, zend_object_iterator *iter)
517+
{
512518
zend_internal_iterator *intern =
513519
(zend_internal_iterator *) zend_internal_iterator_create(zend_ce_internal_iterator);
514520
intern->iter = iter;
515521
intern->iter->index = 0;
516522
ZVAL_OBJ(return_value, &intern->std);
517-
return SUCCESS;
518523
}
519524

520525
static void zend_internal_iterator_free(zend_object *obj) {

Zend/zend_interfaces.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ ZEND_API int zend_user_serialize(zval *object, unsigned char **buffer, size_t *b
7575
ZEND_API int zend_user_unserialize(zval *object, zend_class_entry *ce, const unsigned char *buf, size_t buf_len, zend_unserialize_data *data);
7676

7777
ZEND_API zend_result zend_create_internal_iterator_zval(zval *return_value, zval *obj);
78+
ZEND_API void zend_create_internal_iterator_iter(zval *return_value, zend_object_iterator *iter);
7879

7980
END_EXTERN_C()
8081

ext/spl/php_spl.stub.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,5 @@ function iterator_apply(Traversable $iterator, callable $callback, ?array $args
5151
function iterator_count(iterable $iterator): int {}
5252

5353
function iterator_to_array(iterable $iterator, bool $preserve_keys = true): array {}
54+
55+
function iterator_zip(iterable... $iterators): InternalIterator {}

ext/spl/php_spl_arginfo.h

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/spl/spl_iterators.c

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3026,6 +3026,276 @@ PHP_FUNCTION(iterator_to_array)
30263026
spl_iterator_apply(obj, use_keys ? spl_iterator_to_array_apply : spl_iterator_to_values_apply, (void*)return_value);
30273027
} /* }}} */
30283028

3029+
typedef struct {
3030+
/* To distinguish betwseen arrays and iterator objects we use the fact that UINT32_MAX
3031+
* is not a possible array hash position index. */
3032+
HashPosition hash_position_or_tag;
3033+
union {
3034+
zend_array *array;
3035+
zend_object_iterator *obj_iter;
3036+
};
3037+
} spl_zip_iterator_entry;
3038+
3039+
typedef struct {
3040+
zend_object_iterator intern;
3041+
spl_zip_iterator_entry *iterators;
3042+
zval key_array;
3043+
uint32_t iterator_count;
3044+
} spl_zip_iterator;
3045+
3046+
static zend_always_inline bool spl_zip_iterator_is_obj_entry(const spl_zip_iterator_entry *entry)
3047+
{
3048+
return entry->hash_position_or_tag == UINT32_MAX;
3049+
}
3050+
3051+
static void spl_iterator_zip_dtor(zend_object_iterator *iter)
3052+
{
3053+
spl_zip_iterator *zip_iterator = (spl_zip_iterator *) iter;
3054+
for (uint32_t i = 0; i < zip_iterator->iterator_count; i++) {
3055+
spl_zip_iterator_entry *current = &zip_iterator->iterators[i];
3056+
if (spl_zip_iterator_is_obj_entry(current)) {
3057+
zend_iterator_dtor(current->obj_iter);
3058+
} else {
3059+
zend_array_release(current->array);
3060+
}
3061+
}
3062+
zval_ptr_dtor(&iter->data);
3063+
efree(zip_iterator->iterators);
3064+
}
3065+
3066+
static zend_result spl_iterator_zip_valid(zend_object_iterator *iter)
3067+
{
3068+
spl_zip_iterator *zip_iterator = (spl_zip_iterator *) iter;
3069+
3070+
uint32_t i = 0;
3071+
for (; i < zip_iterator->iterator_count; i++) {
3072+
spl_zip_iterator_entry *current = &zip_iterator->iterators[i];
3073+
if (spl_zip_iterator_is_obj_entry(current)) {
3074+
if (current->obj_iter->funcs->valid(current->obj_iter) != SUCCESS) {
3075+
return FAILURE;
3076+
}
3077+
} else {
3078+
current->hash_position_or_tag = zend_hash_get_current_pos_ex(current->array, current->hash_position_or_tag);
3079+
if (current->hash_position_or_tag >= current->array->nNumUsed) {
3080+
return FAILURE;
3081+
}
3082+
}
3083+
}
3084+
3085+
return i > 0 ? SUCCESS : FAILURE;
3086+
}
3087+
3088+
/* Invariant: returned array is packed and has all UNDEF elements. */
3089+
static zend_array *spl_iterator_zip_reset_array(spl_zip_iterator *zip_iterator, zval *array_zv)
3090+
{
3091+
/* Reuse array if it's RC1 */
3092+
if (!Z_ISUNDEF_P(array_zv) && Z_REFCOUNT_P(array_zv) == 1) {
3093+
zend_array *array = Z_ARR_P(array_zv);
3094+
if (HT_IS_PACKED(array)
3095+
&& array->nNumUsed == zip_iterator->iterator_count
3096+
&& array->nNumOfElements == zip_iterator->iterator_count) {
3097+
array->nNextFreeElement = zip_iterator->iterator_count;
3098+
for (uint32_t i = 0; i < zip_iterator->iterator_count; i++) {
3099+
zval_ptr_dtor(&array->arPacked[i]);
3100+
ZVAL_UNDEF(&array->arPacked[i]);
3101+
}
3102+
return array;
3103+
}
3104+
}
3105+
3106+
zval_ptr_dtor(array_zv);
3107+
3108+
/* Create optimized packed array */
3109+
zend_array *array = zend_new_array(zip_iterator->iterator_count);
3110+
zend_hash_real_init_packed(array);
3111+
array->nNumUsed = array->nNumOfElements = array->nNextFreeElement = zip_iterator->iterator_count;
3112+
ZVAL_ARR(array_zv, array);
3113+
return array;
3114+
}
3115+
3116+
void spl_iterator_zip_get_current_key(zend_object_iterator *iter, zval *key)
3117+
{
3118+
spl_zip_iterator *zip_iterator = (spl_zip_iterator *) iter;
3119+
3120+
zend_array *array = spl_iterator_zip_reset_array(zip_iterator, &zip_iterator->key_array);
3121+
3122+
for (uint32_t i = 0; i < zip_iterator->iterator_count; i++) {
3123+
spl_zip_iterator_entry *current = &zip_iterator->iterators[i];
3124+
if (spl_zip_iterator_is_obj_entry(current)) {
3125+
current->obj_iter->funcs->get_current_key(current->obj_iter, &array->arPacked[i]);
3126+
if (UNEXPECTED(EG(exception))) {
3127+
ZVAL_NULL(key);
3128+
return;
3129+
}
3130+
} else {
3131+
zend_hash_get_current_key_zval_ex(current->array, &array->arPacked[i], &current->hash_position_or_tag);
3132+
}
3133+
}
3134+
3135+
ZVAL_COPY(key, &zip_iterator->key_array);
3136+
}
3137+
3138+
zval *spl_iterator_zip_get_current_data(zend_object_iterator *iter)
3139+
{
3140+
spl_zip_iterator *zip_iterator = (spl_zip_iterator *) iter;
3141+
3142+
zend_array *array = spl_iterator_zip_reset_array(zip_iterator, &zip_iterator->intern.data);
3143+
3144+
for (uint32_t i = 0; i < zip_iterator->iterator_count; i++) {
3145+
spl_zip_iterator_entry *current = &zip_iterator->iterators[i];
3146+
zval *data;
3147+
if (spl_zip_iterator_is_obj_entry(current)) {
3148+
data = current->obj_iter->funcs->get_current_data(current->obj_iter);
3149+
} else {
3150+
data = zend_hash_get_current_data_ex(current->array, &current->hash_position_or_tag);
3151+
}
3152+
if (UNEXPECTED(data == NULL)) {
3153+
for (uint32_t j = 0; j < i; j++) {
3154+
zval_ptr_dtor(&array->arPacked[j]);
3155+
ZVAL_UNDEF(&array->arPacked[j]);
3156+
}
3157+
return NULL;
3158+
}
3159+
ZVAL_COPY(&array->arPacked[i], data);
3160+
}
3161+
3162+
return &iter->data;
3163+
}
3164+
3165+
void spl_iterator_zip_move_forward(zend_object_iterator *iter)
3166+
{
3167+
spl_zip_iterator *zip_iterator = (spl_zip_iterator *) iter;
3168+
3169+
for (uint32_t i = 0; i < zip_iterator->iterator_count; i++) {
3170+
spl_zip_iterator_entry *current = &zip_iterator->iterators[i];
3171+
if (spl_zip_iterator_is_obj_entry(current)) {
3172+
current->obj_iter->funcs->move_forward(current->obj_iter);
3173+
if (UNEXPECTED(EG(exception))) {
3174+
return;
3175+
}
3176+
} else {
3177+
if (UNEXPECTED(zend_hash_move_forward_ex(current->array, &current->hash_position_or_tag) != SUCCESS)) {
3178+
return;
3179+
}
3180+
}
3181+
}
3182+
}
3183+
3184+
void spl_iterator_zip_rewind(zend_object_iterator *iter)
3185+
{
3186+
spl_zip_iterator *zip_iterator = (spl_zip_iterator *) iter;
3187+
3188+
for (uint32_t i = 0; i < zip_iterator->iterator_count; i++) {
3189+
spl_zip_iterator_entry *current = &zip_iterator->iterators[i];
3190+
if (spl_zip_iterator_is_obj_entry(current)) {
3191+
if (current->obj_iter->funcs->rewind) {
3192+
current->obj_iter->funcs->rewind(current->obj_iter);
3193+
if (UNEXPECTED(EG(exception))) {
3194+
return;
3195+
}
3196+
} else if (iter->index > 0) {
3197+
zend_throw_error(NULL, "Iterator does not support rewinding because one or more sub iterators do not support rewinding");
3198+
return;
3199+
}
3200+
} else {
3201+
zend_hash_internal_pointer_reset_ex(current->array, &current->hash_position_or_tag);
3202+
}
3203+
}
3204+
}
3205+
3206+
static HashTable *spl_iterator_zip_get_gc(zend_object_iterator *iter, zval **table, int *n)
3207+
{
3208+
spl_zip_iterator *zip_iterator = (spl_zip_iterator *) iter;
3209+
3210+
HashTable *ht_slot = NULL;
3211+
3212+
// TODO: there can only be one gc_buffer active at a time
3213+
3214+
for (uint32_t i = 0; i < zip_iterator->iterator_count; i++) {
3215+
// TODO: array ????
3216+
spl_zip_iterator_entry *current = &zip_iterator->iterators[i];
3217+
if (spl_zip_iterator_is_obj_entry(current)) {
3218+
if (current->obj_iter->funcs->get_gc) {
3219+
//HashTable *ht = current->obj_iter->funcs->get_gc(current->obj_iter, tmp_table, tmp_n);
3220+
if (ht_slot) {
3221+
3222+
} else {
3223+
//ht_slot = ht;
3224+
}
3225+
}
3226+
}
3227+
}
3228+
3229+
*table = NULL;
3230+
*n = 0;
3231+
3232+
return ht_slot;
3233+
}
3234+
3235+
static const zend_object_iterator_funcs spl_iterator_zip_funcs = {
3236+
spl_iterator_zip_dtor,
3237+
spl_iterator_zip_valid,
3238+
spl_iterator_zip_get_current_data,
3239+
spl_iterator_zip_get_current_key,
3240+
spl_iterator_zip_move_forward,
3241+
spl_iterator_zip_rewind,
3242+
NULL, /* invalidate_current */ // TODO ???
3243+
spl_iterator_zip_get_gc, /* get_gc */
3244+
};
3245+
3246+
// TODO: by ref support ??? (what happens now when we have a ref-returning generator?)
3247+
PHP_FUNCTION(iterator_zip)
3248+
{
3249+
zval *argv;
3250+
uint32_t iterator_count;
3251+
3252+
ZEND_PARSE_PARAMETERS_START(0, -1)
3253+
Z_PARAM_VARIADIC('*', argv, iterator_count)
3254+
ZEND_PARSE_PARAMETERS_END();
3255+
3256+
spl_zip_iterator_entry *iterators = safe_emalloc(iterator_count, sizeof(spl_zip_iterator_entry), 0);
3257+
3258+
for (uint32_t i = 0; i < iterator_count; i++) {
3259+
if (UNEXPECTED(!zend_is_iterable(&argv[i]))) {
3260+
for (uint32_t j = 0; j < i; j++) {
3261+
spl_zip_iterator_entry *current = &iterators[i];
3262+
if (spl_zip_iterator_is_obj_entry(current)) {
3263+
zend_iterator_dtor(current->obj_iter);
3264+
} else {
3265+
zval_ptr_dtor(&argv[j]);
3266+
}
3267+
}
3268+
efree(iterators);
3269+
zend_argument_value_error(i + 1, "must be of type iterable, %s given", zend_zval_value_name(&argv[i]));
3270+
RETURN_THROWS();
3271+
}
3272+
3273+
if (Z_TYPE(argv[i]) == IS_ARRAY) {
3274+
iterators[i].hash_position_or_tag = 0;
3275+
iterators[i].array = Z_ARR(argv[i]);
3276+
Z_TRY_ADDREF(argv[i]);
3277+
} else {
3278+
ZEND_ASSERT(Z_TYPE(argv[i]) == IS_OBJECT);
3279+
3280+
zend_class_entry *ce = Z_OBJCE_P(&argv[i]);
3281+
zend_object_iterator *obj_iter = ce->get_iterator(ce, &argv[i], false);
3282+
iterators[i].hash_position_or_tag = UINT32_MAX;
3283+
iterators[i].obj_iter = obj_iter;
3284+
}
3285+
}
3286+
3287+
spl_zip_iterator *iterator = emalloc(sizeof(*iterator));
3288+
zend_iterator_init(&iterator->intern);
3289+
ZVAL_UNDEF(&iterator->intern.data);
3290+
ZVAL_UNDEF(&iterator->key_array);
3291+
3292+
iterator->intern.funcs = &spl_iterator_zip_funcs;
3293+
iterator->iterators = iterators;
3294+
iterator->iterator_count = iterator_count;
3295+
3296+
zend_create_internal_iterator_iter(return_value, &iterator->intern);
3297+
}
3298+
30293299
static int spl_iterator_count_apply(zend_object_iterator *iter, void *puser) /* {{{ */
30303300
{
30313301
if (UNEXPECTED(*(zend_long*)puser == ZEND_LONG_MAX)) {

0 commit comments

Comments
 (0)