@@ -3026,6 +3026,202 @@ 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+ for (uint32_t i = 0 ; i < zip_iterator -> iterator_count ; i ++ ) {
3068+ spl_zip_iterator_entry * current = & zip_iterator -> iterators [i ];
3069+ if (spl_zip_iterator_is_obj_entry (current )) {
3070+ if (current -> obj_iter -> funcs -> valid (current -> obj_iter ) != SUCCESS ) {
3071+ return FAILURE ;
3072+ }
3073+ } else {
3074+ current -> hash_position_or_tag = zend_hash_get_current_pos_ex (current -> array , current -> hash_position_or_tag );
3075+ if (current -> hash_position_or_tag >= current -> array -> nNumUsed ) {
3076+ return FAILURE ;
3077+ }
3078+ }
3079+ }
3080+
3081+ return SUCCESS ;
3082+ }
3083+
3084+ static zend_array * spl_iterator_zip_reset_array (spl_zip_iterator * zip_iterator )
3085+ {
3086+ zval * array_zv = & zip_iterator -> intern .data ;
3087+
3088+ // TODO: optimize: reuse array if RC1
3089+
3090+ zval_ptr_dtor (array_zv );
3091+
3092+ /* Create optimized packed array */
3093+ zend_array * array = zend_new_array (zip_iterator -> iterator_count );
3094+ zend_hash_real_init_packed (array );
3095+ array -> nNumUsed = array -> nNumOfElements = array -> nNextFreeElement = zip_iterator -> iterator_count ;
3096+ ZVAL_ARR (array_zv , array );
3097+ return array ;
3098+ }
3099+
3100+ zval * spl_iterator_zip_get_current_data (zend_object_iterator * iter )
3101+ {
3102+ spl_zip_iterator * zip_iterator = (spl_zip_iterator * ) iter ;
3103+
3104+ zend_array * array = spl_iterator_zip_reset_array (zip_iterator );
3105+
3106+ for (uint32_t i = 0 ; i < zip_iterator -> iterator_count ; i ++ ) {
3107+ spl_zip_iterator_entry * current = & zip_iterator -> iterators [i ];
3108+ zval * data ;
3109+ if (spl_zip_iterator_is_obj_entry (current )) {
3110+ data = current -> obj_iter -> funcs -> get_current_data (current -> obj_iter );
3111+ } else {
3112+ data = zend_hash_get_current_data_ex (current -> array , & current -> hash_position_or_tag );
3113+ }
3114+ if (UNEXPECTED (data == NULL )) {
3115+ // TODO: cleanup the work we already did (and make sure no memory is uninit)
3116+ return NULL ;
3117+ }
3118+ ZVAL_COPY (& array -> arPacked [i ], data );
3119+ }
3120+
3121+ return & iter -> data ;
3122+ }
3123+
3124+ void spl_iterator_zip_move_forward (zend_object_iterator * iter )
3125+ {
3126+ spl_zip_iterator * zip_iterator = (spl_zip_iterator * ) iter ;
3127+
3128+ for (uint32_t i = 0 ; i < zip_iterator -> iterator_count ; i ++ ) {
3129+ spl_zip_iterator_entry * current = & zip_iterator -> iterators [i ];
3130+ if (spl_zip_iterator_is_obj_entry (current )) {
3131+ // TODO: error handling
3132+ current -> obj_iter -> funcs -> move_forward (current -> obj_iter );
3133+ } else {
3134+ // TODO: error handling
3135+ zend_hash_move_forward_ex (current -> array , & current -> hash_position_or_tag );
3136+ }
3137+ }
3138+ }
3139+
3140+ void spl_iterator_zip_rewind (zend_object_iterator * iter )
3141+ {
3142+ spl_zip_iterator * zip_iterator = (spl_zip_iterator * ) iter ;
3143+
3144+ for (uint32_t i = 0 ; i < zip_iterator -> iterator_count ; i ++ ) {
3145+ spl_zip_iterator_entry * current = & zip_iterator -> iterators [i ];
3146+ if (spl_zip_iterator_is_obj_entry (current )) {
3147+ if (current -> obj_iter -> funcs -> rewind ) {
3148+ current -> obj_iter -> funcs -> rewind (current -> obj_iter );
3149+ if (UNEXPECTED (EG (exception ))) {
3150+ return ;
3151+ }
3152+ } else if (iter -> index > 0 ) {
3153+ zend_throw_error (NULL , "Iterator does not support rewinding because one or more sub iterators do not support rewinding" );
3154+ return ;
3155+ }
3156+ } else {
3157+ zend_hash_internal_pointer_reset_ex (current -> array , & current -> hash_position_or_tag );
3158+ }
3159+ }
3160+ }
3161+
3162+ static const zend_object_iterator_funcs spl_iterator_zip_funcs = {
3163+ spl_iterator_zip_dtor ,
3164+ spl_iterator_zip_valid ,
3165+ spl_iterator_zip_get_current_data ,
3166+ NULL , /* get_current_key, uses default index implementation */
3167+ spl_iterator_zip_move_forward ,
3168+ spl_iterator_zip_rewind , /* rewind */
3169+ NULL , /* invalidate_current */ // TODO ???
3170+ NULL , /* get_gc */ // TODO: do we need this? I suppose because it wraps potentially cyclic objects the answer is yes :-(
3171+ };
3172+
3173+ // TODO: by ref support ???
3174+ PHP_FUNCTION (iterator_zip )
3175+ {
3176+ zval * argv ;
3177+ uint32_t iterator_count ;
3178+
3179+ ZEND_PARSE_PARAMETERS_START (0 , -1 )
3180+ Z_PARAM_VARIADIC ('*' , argv , iterator_count )
3181+ ZEND_PARSE_PARAMETERS_END ();
3182+
3183+ spl_zip_iterator_entry * iterators = safe_emalloc (iterator_count , sizeof (spl_zip_iterator_entry ), 0 );
3184+
3185+ for (uint32_t i = 0 ; i < iterator_count ; i ++ ) {
3186+ if (UNEXPECTED (!zend_is_iterable (& argv [i ]))) {
3187+ for (uint32_t j = 0 ; j < i ; j ++ ) {
3188+ spl_zip_iterator_entry * current = & iterators [i ];
3189+ if (current -> hash_position_or_tag == UINT32_MAX ) {
3190+ zend_iterator_dtor (current -> obj_iter );
3191+ } else {
3192+ Z_TRY_DELREF_P (& argv [j ]);
3193+ }
3194+ }
3195+ efree (iterators );
3196+ zend_argument_value_error (i + 1 , "must be of type iterable, %s given" , zend_zval_value_name (& argv [i ]));
3197+ RETURN_THROWS ();
3198+ }
3199+
3200+ if (Z_TYPE (argv [i ]) == IS_ARRAY ) {
3201+ iterators [i ].hash_position_or_tag = 0 ;
3202+ iterators [i ].array = Z_ARR (argv [i ]);
3203+ Z_TRY_ADDREF (argv [i ]);
3204+ } else {
3205+ ZEND_ASSERT (Z_TYPE (argv [i ]) == IS_OBJECT );
3206+
3207+ zend_class_entry * ce = Z_OBJCE_P (& argv [i ]);
3208+ zend_object_iterator * obj_iter = ce -> get_iterator (ce , & argv [i ], false);
3209+ iterators [i ].hash_position_or_tag = UINT32_MAX ;
3210+ iterators [i ].obj_iter = obj_iter ;
3211+ }
3212+ }
3213+
3214+ spl_zip_iterator * iterator = emalloc (sizeof (* iterator ));
3215+ zend_iterator_init (& iterator -> intern );
3216+ ZVAL_UNDEF (& iterator -> intern .data );
3217+
3218+ iterator -> intern .funcs = & spl_iterator_zip_funcs ;
3219+ iterator -> iterators = iterators ;
3220+ iterator -> iterator_count = iterator_count ;
3221+
3222+ zend_create_internal_iterator_iter (return_value , & iterator -> intern );
3223+ }
3224+
30293225static int spl_iterator_count_apply (zend_object_iterator * iter , void * puser ) /* {{{ */
30303226{
30313227 if (UNEXPECTED (* (zend_long * )puser == ZEND_LONG_MAX )) {
0 commit comments