@@ -3026,6 +3026,220 @@ 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+ 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+
30293243static int spl_iterator_count_apply (zend_object_iterator * iter , void * puser ) /* {{{ */
30303244{
30313245 if (UNEXPECTED (* (zend_long * )puser == ZEND_LONG_MAX )) {
0 commit comments