@@ -376,11 +376,15 @@ partial_vectorcall(partialobject *pto, PyObject *const *args,
376376        return  NULL ;
377377    }
378378
379+     /* Total sizes */ 
380+     Py_ssize_t  tot_nargs  =  pto_nargs  +  nargs  -  pto_phcount ;
381+     Py_ssize_t  tot_nkwds ;
382+     Py_ssize_t  tot_nargskw ;
383+ 
379384    PyObject  * * pto_args  =  _PyTuple_ITEMS (pto -> args );
380-     PyObject  * ret ;
381385
382386    /* Special cases */ 
383-     if  (pto_nkwds   ==   0 ) {
387+     if  (! pto_nkwds ) {
384388        /* Fast path if we're called without arguments */ 
385389        if  (nargskw  ==  0 ) {
386390            return  _PyObject_VectorcallTstate (tstate , pto -> fn , pto_args ,
@@ -394,175 +398,115 @@ partial_vectorcall(partialobject *pto, PyObject *const *args,
394398            PyObject  * * newargs  =  (PyObject  * * )args  -  1 ;
395399            PyObject  * tmp  =  newargs [0 ];
396400            newargs [0 ] =  pto_args [0 ];
397-             ret  =  _PyObject_VectorcallTstate (tstate , pto -> fn , newargs ,
398-                                              nargs  +  1 , kwnames );
401+             PyObject   * ret  =  _PyObject_VectorcallTstate (tstate , pto -> fn , newargs ,
402+                                                         nargs  +  1 , kwnames );
399403            newargs [0 ] =  tmp ;
400404            return  ret ;
401405        }
402- 
403406    }
404407
405-     /* Size variables */ 
406-     Py_ssize_t  tot_nargs  =  pto_nargs  +  nargs  -  pto_phcount ;
407-     Py_ssize_t  tot_nkwds ;
408-     Py_ssize_t  tot_nargskw ;
409- 
410-     /* Divergence on whether pto has kwds */ 
411-     if  (pto_nkwds  ==  0 ) {
412-         tot_nkwds  =  nkwds ;
413-         tot_nargskw  =  tot_nargs  +  tot_nkwds ;
408+     tot_nkwds  =  pto_nkwds  +  nkwds ;
409+     tot_nargskw  =  tot_nargs  +  tot_nkwds ;
410+     PyObject  * * stack , * small_stack [_PY_FASTCALL_SMALL_STACK ];
411+     PyObject  * tot_kwnames  =  kwnames ;
414412
413+     /* Initialize stack & copy keywords to stack */ 
414+     if  (!pto_nkwds ) {
415415        /* Allocate Stack */ 
416-         PyObject  * * stack , * small_stack [_PY_FASTCALL_SMALL_STACK ];
416+         tot_nkwds  =  pto_nkwds  +  nkwds ;
417+         tot_nargskw  =  tot_nargs  +  tot_nkwds ;
417418        ALLOCATE_STACK (PyObject , tot_nargskw , small_stack , stack );
418419
419-         /* Copy Positionals to new stack */ 
420-         if  (pto_phcount ) {
421-             Py_ssize_t  j  =  0 ;       // New args index 
422-             for  (Py_ssize_t  i  =  0 ; i  <  pto_nargs ; i ++ ) {
423-                 if  (pto_args [i ] ==  pto -> placeholder ){
424-                     stack [i ] =  args [j ];
425-                     j  +=  1 ;
426-                 }
427-                 else  {
428-                     stack [i ] =  pto_args [i ];
429-                 }
430-             }
431-             assert (j  ==  pto_phcount );
432-             /* Add remaining args from new_args */ 
433-             if  (nargs  >  pto_phcount ) {
434-                 memcpy (stack  +  pto_nargs , args  +  j , (nargskw  -  j ) *  sizeof (PyObject * ));
435-             }
436-         }
437-         else  {
438-             memcpy (stack , pto_args , pto_nargs  *  sizeof (PyObject * ));
439-             memcpy (stack  +  pto_nargs , args , nargskw  *  sizeof (PyObject * ));
420+         if  (nkwds ) {
421+             /* if !pto_nkwds & nkwds, then simply append kw */ 
422+             memcpy (stack  +  tot_nargs , args  +  nargs , nkwds  *  sizeof (PyObject * ));
440423        }
441- 
442-         /* Call / Maintenance / Return */ 
443-         ret  =  _PyObject_VectorcallTstate (tstate , pto -> fn , stack , tot_nargs , kwnames );
444-         DEALLOCATE_STACK (small_stack , stack );
445-         return  ret ;
446424    }
447425    else  {
448426        PyObject  * key , * val ;
449- 
450-         PyObject  * * pto_kwkeys , * small_pto_kwkeys [_PY_FASTCALL_SMALL_STACK ];
451-         PyObject  * * pto_kwvals , * small_pto_kwvals [_PY_FASTCALL_SMALL_STACK ];
452-         ALLOCATE_STACK (PyObject , pto_nkwds , small_pto_kwkeys , pto_kwkeys );
453-         ALLOCATE_STACK (PyObject , pto_nkwds , small_pto_kwvals , pto_kwvals );
454-         Py_ssize_t  pos  =  0 , i  =  0 ;
455-         while  (PyDict_Next (pto -> kw , & pos , & key , & val )) {
456-             pto_kwkeys [i ] =  key ;
457-             pto_kwvals [i ] =  val ;
458-             i ++ ;
459-         }
460- 
461-         /* Calculate total kw size and positions of items to override */ 
462-         PyObject  * tot_kwnames ;
463-         PyObject  * * stack , * small_stack [_PY_FASTCALL_SMALL_STACK ];
464-         tot_nkwds  =  pto_nkwds  +  nkwds ;
465-         if  (nkwds ) {
466-             Py_ssize_t  * positions , small_positions [_PY_FASTCALL_SMALL_STACK ];
467-             ALLOCATE_STACK (Py_ssize_t , nkwds , small_positions , positions );
468-             for  (Py_ssize_t  i = 0 ; i  <  nkwds ; i ++ ) {
469-                 positions [i ] =  -1 ;
470-                 key  =  PyTuple_GET_ITEM (kwnames , i );
471-                 if  (PyDict_Contains (pto -> kw , key )) {
472-                     tot_nkwds -- ;
473-                     for  (Py_ssize_t  j = 0 ; j  <  pto_nkwds ; j ++ ) {
474-                         if  (PyObject_RichCompareBool (key , pto_kwkeys [j ], Py_EQ )) {
475-                             positions [i ] =  j ;
476-                             break ;
477-                         }
478-                     }
427+         PyObject  * pto_kw  =  NULL ;  // pto_kw with duplicates merged (if any) 
428+ 
429+         /* Temporary stack for keywords that are not in pto->kw */ 
430+         PyObject  * * kwtail , * small_kwtail [_PY_FASTCALL_SMALL_STACK  *  2 ];
431+         ALLOCATE_STACK (PyObject , nkwds  *  2 , small_kwtail , kwtail );
432+ 
433+         /* Merge kw to pto_kw or add to tail (if not duplicate) */ 
434+         Py_ssize_t  n_tail  =  0 ;
435+         for  (Py_ssize_t  i  =  0 ; i  <  nkwds ; ++ i ) {
436+             key  =  PyTuple_GET_ITEM (kwnames , i );
437+             val  =  args [nargs  +  i ];
438+             if  (PyDict_Contains (pto -> kw , key )) {
439+                 if  (pto_kw  ==  NULL ) {
440+                     pto_kw  =  PyDict_Copy (pto -> kw );
479441                }
442+                 PyDict_SetItem (pto_kw , key , val );
480443            }
481-             tot_nargskw  =  tot_nargs  +  tot_nkwds ;
482- 
483-             /* Allocate Stack */ 
484-             ALLOCATE_STACK (PyObject , tot_nargskw , small_stack , stack );
485- 
486-             /* Copy Pto Keywords to stack */ 
487-             memcpy (stack  +  tot_nargs , pto_kwvals , pto_nkwds  *  sizeof (PyObject * ));
488-             DEALLOCATE_STACK (small_pto_kwvals , pto_kwvals );
489- 
490-             /* Copy New Keywords to stack */ 
491-             tot_kwnames  =  PyTuple_New (tot_nkwds );
492-             for  (Py_ssize_t  i = 0 ; i  <  pto_nkwds ; i ++ ) {
493-                 key  =  pto_kwkeys [i ];
494-                 PyTuple_SET_ITEM (tot_kwnames , i , key );
495-                 Py_INCREF (key );
496-             }
497-             DEALLOCATE_STACK (small_pto_kwkeys , pto_kwkeys );
498- 
499-             Py_ssize_t  k  =  0 ;
500-             Py_ssize_t  j ;
501-             for  (Py_ssize_t  i = 0 ; i  <  nkwds ; i ++ ) {
502-                 key  =  PyTuple_GET_ITEM (kwnames , i );
503-                 val  =  args [nargs  +  i ];
504-                 j  =  positions [i ];
505-                 if  (j  !=  -1 ) {
506-                     stack [tot_nargs  +  j ] =  val ;
507-                 }
508-                 else  {
509-                     PyTuple_SET_ITEM (tot_kwnames , pto_nkwds  +  k , key );
510-                     Py_INCREF (key );
511-                     stack [tot_nargs  +  pto_nkwds  +  k ] =  val ;
512-                     k ++ ;
513-                 }
444+             else  {
445+                 kwtail [n_tail ] =  key ;
446+                 kwtail [n_tail  +  nkwds ] =  val ;
447+                 n_tail ++ ;
514448            }
515-             DEALLOCATE_STACK (small_positions , positions );
516449        }
517-         else  {
518-             tot_nargskw  =  tot_nargs  +  tot_nkwds ;
519450
520-              /* Allocate Stack */ 
521-              ALLOCATE_STACK ( PyObject ,  tot_nargskw ,  small_stack ,  stack ) ;
522- 
523-              /* Copy Pto Keywords to stack */ 
524-              memcpy ( stack   +   tot_nargs ,  pto_kwvals ,  pto_nkwds   *   sizeof ( PyObject * ) );
525-              DEALLOCATE_STACK ( small_pto_kwvals ,  pto_kwvals );
451+         /* Allocate Stack */ 
452+         Py_ssize_t   n_merges   =   nkwds   -   n_tail ;
453+          tot_nkwds   =   pto_nkwds   +   nkwds   -   n_merges ; 
454+         tot_nargskw   =   tot_nargs   +   tot_nkwds ; 
455+         ALLOCATE_STACK ( PyObject ,  tot_nargskw ,  small_stack ,  stack );
456+         tot_kwnames   =   PyTuple_New ( tot_nkwds );
526457
527-             /* Copy New Keywords to stack */ 
528-             tot_kwnames  =  PyTuple_New (tot_nkwds );
529-             for  (Py_ssize_t  i = 0 ; i  <  pto_nkwds ; i ++ ) {
530-                 key  =  pto_kwkeys [i ];
531-                 PyTuple_SET_ITEM (tot_kwnames , i , key );
532-                 Py_INCREF (key );
533-             }
534-             DEALLOCATE_STACK (small_pto_kwkeys , pto_kwkeys );
458+         /* Copy pto_kw to stack */ 
459+         Py_ssize_t  pos  =  0 , i  =  0 ;
460+         while  (PyDict_Next (n_merges  ? pto_kw  : pto -> kw , & pos , & key , & val )) {
461+             PyTuple_SET_ITEM (tot_kwnames , i , key );
462+             Py_INCREF (key );
463+             stack [tot_nargs  +  i ] =  val ;
464+             i ++ ;
535465        }
466+         Py_XDECREF (pto_kw );
467+ 
468+         /* Copy kw tail to stack */ 
469+         for  (Py_ssize_t  i  =  0 ; i  <  n_tail ; ++ i ) {
470+             key  =  kwtail [i ];
471+             val  =  kwtail [i  +  nkwds ];
472+             PyTuple_SET_ITEM (tot_kwnames , pto_nkwds  +  i , key );
473+             Py_INCREF (key );
474+             stack [tot_nargs  +  pto_nkwds  +  i ] =  val ;
475+         }
476+         DEALLOCATE_STACK (small_kwtail , kwtail );
477+     }
536478
537-         /* Copy Positionals to stack */ 
538-         if  (pto_phcount ) {
539-             Py_ssize_t  j  =  0 ;       // New args index 
540-             for  (Py_ssize_t  i  =  0 ; i  <  pto_nargs ; i ++ ) {
541-                 if  (pto_args [i ] ==  pto -> placeholder ){
542-                     stack [i ] =  args [j ];
543-                     j  +=  1 ;
544-                 }
545-                 else  {
546-                     stack [i ] =  pto_args [i ];
547-                 }
479+     /* Copy Positionals to stack */ 
480+     if  (pto_phcount ) {
481+         Py_ssize_t  j  =  0 ;       // New args index 
482+         for  (Py_ssize_t  i  =  0 ; i  <  pto_nargs ; i ++ ) {
483+             if  (pto_args [i ] ==  pto -> placeholder ){
484+                 stack [i ] =  args [j ];
485+                 j  +=  1 ;
548486            }
549-             assert (j  ==  pto_phcount );
550-             /* Add remaining args from new_args */ 
551-             if  (nargs  >  pto_phcount ) {
552-                 memcpy (stack  +  pto_nargs , args  +  j , (nargs  -  j ) *  sizeof (PyObject * ));
487+             else  {
488+                 stack [i ] =  pto_args [i ];
553489            }
554490        }
555-         else  {
556-             memcpy (stack , pto_args , pto_nargs  *  sizeof (PyObject * ));
557-             memcpy (stack  +  pto_nargs , args , nargs  *  sizeof (PyObject * ));
491+         assert (j  ==  pto_phcount );
492+         /* Add remaining args from new_args */ 
493+         if  (nargs  >  pto_phcount ) {
494+             memcpy (stack  +  pto_nargs , args  +  j , (nargs  -  j ) *  sizeof (PyObject * ));
558495        }
496+     }
497+     else  {
498+         memcpy (stack , pto_args , pto_nargs  *  sizeof (PyObject * ));
499+         memcpy (stack  +  pto_nargs , args , nargs  *  sizeof (PyObject * ));
500+     }
559501
560-         /* Call / Maintenance / Return */ 
561-         ret  =  _PyObject_VectorcallTstate (tstate , pto -> fn , stack , tot_nargs , tot_kwnames );
562-         DEALLOCATE_STACK (small_stack , stack );
502+     /* Call / Maintenance / Return */ 
503+     PyObject  * ret  =  _PyObject_VectorcallTstate (tstate , pto -> fn , stack ,
504+                                                tot_nargs , tot_kwnames );
505+     DEALLOCATE_STACK (small_stack , stack );
506+     if  (pto_nkwds ) {
563507        Py_DECREF (tot_kwnames );
564-         return  ret ;
565508    }
509+     return  ret ;
566510}
567511
568512/* Set pto->vectorcall depending on the parameters of the partial object */ 
0 commit comments