@@ -3026,6 +3026,220 @@ PHP_FUNCTION(iterator_to_array)
3026
3026
spl_iterator_apply (obj , use_keys ? spl_iterator_to_array_apply : spl_iterator_to_values_apply , (void * )return_value );
3027
3027
} /* }}} */
3028
3028
3029
+ typedef struct {
3030
+ HashPosition hash_position_or_tag ; /* uses the fact that index UINT32_MAX is not possible for arrays */
3031
+ union {
3032
+ zend_array * array ;
3033
+ zend_object_iterator * obj_iter ;
3034
+ };
3035
+ } spl_zip_iterator_entry ;
3036
+
3037
+ typedef struct {
3038
+ zend_object_iterator intern ;
3039
+ spl_zip_iterator_entry * iterators ;
3040
+ uint32_t iterator_count ;
3041
+ } spl_zip_iterator ;
3042
+
3043
+ static zend_always_inline bool spl_zip_iterator_is_obj_entry (const spl_zip_iterator_entry * entry )
3044
+ {
3045
+ return entry -> hash_position_or_tag == UINT32_MAX ;
3046
+ }
3047
+
3048
+ static void spl_iterator_zip_dtor (zend_object_iterator * iter )
3049
+ {
3050
+ spl_zip_iterator * zip_iterator = (spl_zip_iterator * ) iter ;
3051
+ for (uint32_t i = 0 ; i < zip_iterator -> iterator_count ; i ++ ) {
3052
+ spl_zip_iterator_entry * current = & zip_iterator -> iterators [i ];
3053
+ if (spl_zip_iterator_is_obj_entry (current )) {
3054
+ zend_iterator_dtor (current -> obj_iter );
3055
+ } else {
3056
+ zend_array_release (current -> array );
3057
+ }
3058
+ }
3059
+ zval_ptr_dtor (& iter -> data );
3060
+ efree (zip_iterator -> iterators );
3061
+ }
3062
+
3063
+ static zend_result spl_iterator_zip_valid (zend_object_iterator * iter )
3064
+ {
3065
+ spl_zip_iterator * zip_iterator = (spl_zip_iterator * ) iter ;
3066
+
3067
+ uint32_t i = 0 ;
3068
+ for (; i < zip_iterator -> iterator_count ; i ++ ) {
3069
+ spl_zip_iterator_entry * current = & zip_iterator -> iterators [i ];
3070
+ if (spl_zip_iterator_is_obj_entry (current )) {
3071
+ if (current -> obj_iter -> funcs -> valid (current -> obj_iter ) != SUCCESS ) {
3072
+ return FAILURE ;
3073
+ }
3074
+ } else {
3075
+ current -> hash_position_or_tag = zend_hash_get_current_pos_ex (current -> array , current -> hash_position_or_tag );
3076
+ if (current -> hash_position_or_tag >= current -> array -> nNumUsed ) {
3077
+ return FAILURE ;
3078
+ }
3079
+ }
3080
+ }
3081
+
3082
+ return i > 0 ? SUCCESS : FAILURE ;
3083
+ }
3084
+
3085
+ /* Invariant: returned array is packed and has all UNDEF elements. */
3086
+ static zend_array * spl_iterator_zip_reset_array (spl_zip_iterator * zip_iterator )
3087
+ {
3088
+ zval * array_zv = & zip_iterator -> intern .data ;
3089
+
3090
+ /* Reuse array if it's RC1 */
3091
+ if (!Z_ISUNDEF_P (array_zv ) && Z_REFCOUNT_P (array_zv ) == 1 ) {
3092
+ zend_array * array = Z_ARR_P (array_zv );
3093
+ if (HT_IS_PACKED (array )
3094
+ && array -> nNumUsed == zip_iterator -> iterator_count
3095
+ && array -> nNumOfElements == zip_iterator -> iterator_count ) {
3096
+ array -> nNextFreeElement = zip_iterator -> iterator_count ;
3097
+ for (uint32_t i = 0 ; i < zip_iterator -> iterator_count ; i ++ ) {
3098
+ zval_ptr_dtor (& array -> arPacked [i ]);
3099
+ ZVAL_UNDEF (& array -> arPacked [i ]);
3100
+ }
3101
+ return array ;
3102
+ }
3103
+ }
3104
+
3105
+ zval_ptr_dtor (array_zv );
3106
+
3107
+ /* Create optimized packed array */
3108
+ zend_array * array = zend_new_array (zip_iterator -> iterator_count );
3109
+ zend_hash_real_init_packed (array );
3110
+ array -> nNumUsed = array -> nNumOfElements = array -> nNextFreeElement = zip_iterator -> iterator_count ;
3111
+ ZVAL_ARR (array_zv , array );
3112
+ return array ;
3113
+ }
3114
+
3115
+ zval * spl_iterator_zip_get_current_data (zend_object_iterator * iter )
3116
+ {
3117
+ spl_zip_iterator * zip_iterator = (spl_zip_iterator * ) iter ;
3118
+
3119
+ zend_array * array = spl_iterator_zip_reset_array (zip_iterator );
3120
+
3121
+ for (uint32_t i = 0 ; i < zip_iterator -> iterator_count ; i ++ ) {
3122
+ spl_zip_iterator_entry * current = & zip_iterator -> iterators [i ];
3123
+ zval * data ;
3124
+ if (spl_zip_iterator_is_obj_entry (current )) {
3125
+ data = current -> obj_iter -> funcs -> get_current_data (current -> obj_iter );
3126
+ } else {
3127
+ data = zend_hash_get_current_data_ex (current -> array , & current -> hash_position_or_tag );
3128
+ }
3129
+ if (UNEXPECTED (data == NULL )) {
3130
+ for (uint32_t j = 0 ; j < i ; j ++ ) {
3131
+ zval_ptr_dtor (& array -> arPacked [j ]);
3132
+ ZVAL_UNDEF (& array -> arPacked [j ]);
3133
+ }
3134
+ return NULL ;
3135
+ }
3136
+ ZVAL_COPY (& array -> arPacked [i ], data );
3137
+ }
3138
+
3139
+ return & iter -> data ;
3140
+ }
3141
+
3142
+ void spl_iterator_zip_move_forward (zend_object_iterator * iter )
3143
+ {
3144
+ spl_zip_iterator * zip_iterator = (spl_zip_iterator * ) iter ;
3145
+
3146
+ for (uint32_t i = 0 ; i < zip_iterator -> iterator_count ; i ++ ) {
3147
+ spl_zip_iterator_entry * current = & zip_iterator -> iterators [i ];
3148
+ if (spl_zip_iterator_is_obj_entry (current )) {
3149
+ // TODO: error handling
3150
+ current -> obj_iter -> funcs -> move_forward (current -> obj_iter );
3151
+ } else {
3152
+ // TODO: error handling
3153
+ zend_hash_move_forward_ex (current -> array , & current -> hash_position_or_tag );
3154
+ }
3155
+ }
3156
+ }
3157
+
3158
+ void spl_iterator_zip_rewind (zend_object_iterator * iter )
3159
+ {
3160
+ spl_zip_iterator * zip_iterator = (spl_zip_iterator * ) iter ;
3161
+
3162
+ for (uint32_t i = 0 ; i < zip_iterator -> iterator_count ; i ++ ) {
3163
+ spl_zip_iterator_entry * current = & zip_iterator -> iterators [i ];
3164
+ if (spl_zip_iterator_is_obj_entry (current )) {
3165
+ if (current -> obj_iter -> funcs -> rewind ) {
3166
+ current -> obj_iter -> funcs -> rewind (current -> obj_iter );
3167
+ if (UNEXPECTED (EG (exception ))) {
3168
+ return ;
3169
+ }
3170
+ } else if (iter -> index > 0 ) {
3171
+ zend_throw_error (NULL , "Iterator does not support rewinding because one or more sub iterators do not support rewinding" );
3172
+ return ;
3173
+ }
3174
+ } else {
3175
+ zend_hash_internal_pointer_reset_ex (current -> array , & current -> hash_position_or_tag );
3176
+ }
3177
+ }
3178
+ }
3179
+
3180
+ static const zend_object_iterator_funcs spl_iterator_zip_funcs = {
3181
+ spl_iterator_zip_dtor ,
3182
+ spl_iterator_zip_valid ,
3183
+ spl_iterator_zip_get_current_data ,
3184
+ NULL , /* get_current_key, uses default index implementation */
3185
+ spl_iterator_zip_move_forward ,
3186
+ spl_iterator_zip_rewind , /* rewind */
3187
+ NULL , /* invalidate_current */ // TODO ???
3188
+ NULL , /* get_gc */ // TODO: do we need this? I suppose because it wraps potentially cyclic objects the answer is yes :-(
3189
+ };
3190
+
3191
+ // TODO: by ref support ???
3192
+ PHP_FUNCTION (iterator_zip )
3193
+ {
3194
+ zval * argv ;
3195
+ uint32_t iterator_count ;
3196
+
3197
+ ZEND_PARSE_PARAMETERS_START (0 , -1 )
3198
+ Z_PARAM_VARIADIC ('*' , argv , iterator_count )
3199
+ ZEND_PARSE_PARAMETERS_END ();
3200
+
3201
+ spl_zip_iterator_entry * iterators = safe_emalloc (iterator_count , sizeof (spl_zip_iterator_entry ), 0 );
3202
+
3203
+ for (uint32_t i = 0 ; i < iterator_count ; i ++ ) {
3204
+ if (UNEXPECTED (!zend_is_iterable (& argv [i ]))) {
3205
+ for (uint32_t j = 0 ; j < i ; j ++ ) {
3206
+ spl_zip_iterator_entry * current = & iterators [i ];
3207
+ if (spl_zip_iterator_is_obj_entry (current )) {
3208
+ zend_iterator_dtor (current -> obj_iter );
3209
+ } else {
3210
+ Z_TRY_DELREF_P (& argv [j ]);
3211
+ }
3212
+ }
3213
+ efree (iterators );
3214
+ zend_argument_value_error (i + 1 , "must be of type iterable, %s given" , zend_zval_value_name (& argv [i ]));
3215
+ RETURN_THROWS ();
3216
+ }
3217
+
3218
+ if (Z_TYPE (argv [i ]) == IS_ARRAY ) {
3219
+ iterators [i ].hash_position_or_tag = 0 ;
3220
+ iterators [i ].array = Z_ARR (argv [i ]);
3221
+ Z_TRY_ADDREF (argv [i ]);
3222
+ } else {
3223
+ ZEND_ASSERT (Z_TYPE (argv [i ]) == IS_OBJECT );
3224
+
3225
+ zend_class_entry * ce = Z_OBJCE_P (& argv [i ]);
3226
+ zend_object_iterator * obj_iter = ce -> get_iterator (ce , & argv [i ], false);
3227
+ iterators [i ].hash_position_or_tag = UINT32_MAX ;
3228
+ iterators [i ].obj_iter = obj_iter ;
3229
+ }
3230
+ }
3231
+
3232
+ spl_zip_iterator * iterator = emalloc (sizeof (* iterator ));
3233
+ zend_iterator_init (& iterator -> intern );
3234
+ ZVAL_UNDEF (& iterator -> intern .data );
3235
+
3236
+ iterator -> intern .funcs = & spl_iterator_zip_funcs ;
3237
+ iterator -> iterators = iterators ;
3238
+ iterator -> iterator_count = iterator_count ;
3239
+
3240
+ zend_create_internal_iterator_iter (return_value , & iterator -> intern );
3241
+ }
3242
+
3029
3243
static int spl_iterator_count_apply (zend_object_iterator * iter , void * puser ) /* {{{ */
3030
3244
{
3031
3245
if (UNEXPECTED (* (zend_long * )puser == ZEND_LONG_MAX )) {
0 commit comments