@@ -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