@@ -496,24 +496,19 @@ pg_base_get_init(PyObject *self, PyObject *_null)
496496static int
497497pg_IntFromObj (PyObject * obj , int * val )
498498{
499- int tmp_val ;
500-
501499 if (PyFloat_Check (obj )) {
502500 /* Python3.8 complains with deprecation warnings if we pass
503501 * floats to PyLong_AsLong.
504502 */
505- double dv = PyFloat_AsDouble (obj );
506- tmp_val = (int )dv ;
503+ * val = (int )PyFloat_AS_DOUBLE (obj );
507504 }
508505 else {
509- tmp_val = PyLong_AsLong (obj );
510- }
511-
512- if (tmp_val == -1 && PyErr_Occurred ()) {
513- PyErr_Clear ();
514- return 0 ;
506+ * val = PyLong_AsLong (obj );
507+ if (* val == -1 && PyErr_Occurred ()) {
508+ PyErr_Clear ();
509+ return 0 ;
510+ }
515511 }
516- * val = tmp_val ;
517512 return 1 ;
518513}
519514
@@ -535,30 +530,79 @@ pg_IntFromObjIndex(PyObject *obj, int _index, int *val)
535530static int
536531pg_TwoIntsFromObj (PyObject * obj , int * val1 , int * val2 )
537532{
538- if (PyTuple_Check (obj ) && PyTuple_Size (obj ) == 1 ) {
533+ // First, lets check the size. This returns -1 if invalid and may set an
534+ // error.
535+ Py_ssize_t obj_size = PySequence_Size (obj );
536+
537+ // If the object is a tuple of one element, try that one element.
538+ if (obj_size == 1 && PyTuple_Check (obj )) {
539539 return pg_TwoIntsFromObj (PyTuple_GET_ITEM (obj , 0 ), val1 , val2 );
540540 }
541- if (!PySequence_Check (obj ) || PySequence_Length (obj ) != 2 ) {
541+
542+ // Otherwise lets make sure this is a legit sequence and has two elements.
543+ // Some objects can passing PySequence_Size but fail PySequence_Check
544+ // (like sets)
545+ if (obj_size != 2 || !PySequence_Check (obj )) {
546+ PyErr_Clear (); // Clear the potential error from PySequence_Size
547+ return 0 ;
548+ }
549+
550+ // Now we can extract the items, using this macro because we know
551+ // obj is a PySequence.
552+ PyObject * item1 = PySequence_ITEM (obj , 0 );
553+ PyObject * item2 = PySequence_ITEM (obj , 1 );
554+
555+ // If either item is NULL lets get out of here
556+ if (item1 == NULL || item2 == NULL ) {
557+ Py_XDECREF (item1 );
558+ Py_XDECREF (item2 );
559+ PyErr_Clear ();
542560 return 0 ;
543561 }
544- if (!pg_IntFromObjIndex (obj , 0 , val1 ) ||
545- !pg_IntFromObjIndex (obj , 1 , val2 )) {
562+
563+ // Fastest way to extract numbers I tested (in Python 3.13) is to extract
564+ // Python floats as doubles with the below macro, and get everything else
565+ // through PyLong_AsLong, using C casting to turn into the final type.
566+ if (PyFloat_Check (item1 )) {
567+ * val1 = (int )PyFloat_AS_DOUBLE (item1 );
568+ }
569+ else {
570+ * val1 = PyLong_AsLong (item1 );
571+ }
572+
573+ if (PyFloat_Check (item2 )) {
574+ * val2 = (int )PyFloat_AS_DOUBLE (item2 );
575+ }
576+ else {
577+ * val2 = PyLong_AsLong (item2 );
578+ }
579+
580+ // This catches the case where either of the PyLong_AsLong's failed
581+ if ((* val1 == -1 || * val2 == -1 ) && PyErr_Occurred ()) {
582+ PyErr_Clear ();
583+ Py_DECREF (item1 );
584+ Py_DECREF (item2 );
546585 return 0 ;
547586 }
587+
588+ Py_DECREF (item1 );
589+ Py_DECREF (item2 );
548590 return 1 ;
549591}
550592
551593static int
552594pg_FloatFromObj (PyObject * obj , float * val )
553595{
554- float f = (float )PyFloat_AsDouble (obj );
555-
556- if (f == -1 && PyErr_Occurred ()) {
557- PyErr_Clear ();
558- return 0 ;
596+ if (PyFloat_Check (obj )) {
597+ * val = (float )PyFloat_AS_DOUBLE (obj );
598+ }
599+ else {
600+ * val = (float )PyLong_AsLong (obj );
601+ if (* val == -1.0f && PyErr_Occurred ()) {
602+ PyErr_Clear ();
603+ return 0 ;
604+ }
559605 }
560-
561- * val = f ;
562606 return 1 ;
563607}
564608
@@ -580,16 +624,63 @@ pg_FloatFromObjIndex(PyObject *obj, int _index, float *val)
580624static int
581625pg_TwoFloatsFromObj (PyObject * obj , float * val1 , float * val2 )
582626{
583- if (PyTuple_Check (obj ) && PyTuple_Size (obj ) == 1 ) {
627+ // First, lets check the size. This returns -1 if invalid and may set an
628+ // error.
629+ Py_ssize_t obj_size = PySequence_Size (obj );
630+
631+ // If the object is a tuple of one element, try that one element.
632+ if (obj_size == 1 && PyTuple_Check (obj )) {
584633 return pg_TwoFloatsFromObj (PyTuple_GET_ITEM (obj , 0 ), val1 , val2 );
585634 }
586- if (!PySequence_Check (obj ) || PySequence_Length (obj ) != 2 ) {
635+
636+ // Otherwise lets make sure this is a legit sequence and has two elements.
637+ // Some objects can passing PySequence_Size but fail PySequence_Check
638+ // (like sets)
639+ if (obj_size != 2 || !PySequence_Check (obj )) {
640+ PyErr_Clear (); // Clear the potential error from PySequence_Size
641+ return 0 ;
642+ }
643+
644+ // Now we can extract the items, using this macro because we know
645+ // obj is a PySequence.
646+ PyObject * item1 = PySequence_ITEM (obj , 0 );
647+ PyObject * item2 = PySequence_ITEM (obj , 1 );
648+
649+ // If either item is NULL lets get out of here
650+ if (item1 == NULL || item2 == NULL ) {
651+ Py_XDECREF (item1 );
652+ Py_XDECREF (item2 );
653+ PyErr_Clear ();
587654 return 0 ;
588655 }
589- if (!pg_FloatFromObjIndex (obj , 0 , val1 ) ||
590- !pg_FloatFromObjIndex (obj , 1 , val2 )) {
656+
657+ // Fastest way to extract numbers I tested (in Python 3.13) is to extract
658+ // Python floats as doubles with the below macro, and get everything else
659+ // through PyLong_AsLong, using C casting to turn into the final type.
660+ if (PyFloat_Check (item1 )) {
661+ * val1 = (float )PyFloat_AS_DOUBLE (item1 );
662+ }
663+ else {
664+ * val1 = (float )PyLong_AsLong (item1 );
665+ }
666+
667+ if (PyFloat_Check (item2 )) {
668+ * val2 = (float )PyFloat_AS_DOUBLE (item2 );
669+ }
670+ else {
671+ * val2 = (float )PyLong_AsLong (item2 );
672+ }
673+
674+ // This catches the case where either of the PyLong_AsLong's failed
675+ if ((* val1 == -1.0f || * val2 == -1.0f ) && PyErr_Occurred ()) {
676+ PyErr_Clear ();
677+ Py_DECREF (item1 );
678+ Py_DECREF (item2 );
591679 return 0 ;
592680 }
681+
682+ Py_DECREF (item1 );
683+ Py_DECREF (item2 );
593684 return 1 ;
594685}
595686
0 commit comments