@@ -496,24 +496,19 @@ pg_base_get_init(PyObject *self, PyObject *_null)
496
496
static int
497
497
pg_IntFromObj (PyObject * obj , int * val )
498
498
{
499
- int tmp_val ;
500
-
501
499
if (PyFloat_Check (obj )) {
502
500
/* Python3.8 complains with deprecation warnings if we pass
503
501
* floats to PyLong_AsLong.
504
502
*/
505
- double dv = PyFloat_AsDouble (obj );
506
- tmp_val = (int )dv ;
503
+ * val = (int )PyFloat_AS_DOUBLE (obj );
507
504
}
508
505
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
+ }
515
511
}
516
- * val = tmp_val ;
517
512
return 1 ;
518
513
}
519
514
@@ -535,25 +530,56 @@ pg_IntFromObjIndex(PyObject *obj, int _index, int *val)
535
530
static int
536
531
pg_TwoIntsFromObj (PyObject * obj , int * val1 , int * val2 )
537
532
{
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 )) {
539
539
return pg_TwoIntsFromObj (PyTuple_GET_ITEM (obj , 0 ), val1 , val2 );
540
540
}
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
542
547
return 0 ;
543
548
}
544
549
545
- // Can use PySequence_ITEM because of the PySequence_Check above.
550
+ // Now we can extract the items, using this macro because we know
551
+ // obj is a PySequence.
546
552
PyObject * item1 = PySequence_ITEM (obj , 0 );
547
553
PyObject * item2 = PySequence_ITEM (obj , 1 );
548
554
555
+ // If either item is NULL lets get out of here
549
556
if (item1 == NULL || item2 == NULL ) {
550
557
Py_XDECREF (item1 );
551
558
Py_XDECREF (item2 );
552
559
PyErr_Clear ();
553
560
return 0 ;
554
561
}
555
562
556
- if (!pg_IntFromObj (item1 , val1 ) || !pg_IntFromObj (item2 , val2 )) {
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 ();
557
583
Py_DECREF (item1 );
558
584
Py_DECREF (item2 );
559
585
return 0 ;
@@ -567,14 +593,16 @@ pg_TwoIntsFromObj(PyObject *obj, int *val1, int *val2)
567
593
static int
568
594
pg_FloatFromObj (PyObject * obj , float * val )
569
595
{
570
- float f = (float )PyFloat_AsDouble (obj );
571
-
572
- if (f == -1 && PyErr_Occurred ()) {
573
- PyErr_Clear ();
574
- 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
+ }
575
605
}
576
-
577
- * val = f ;
578
606
return 1 ;
579
607
}
580
608
@@ -596,25 +624,56 @@ pg_FloatFromObjIndex(PyObject *obj, int _index, float *val)
596
624
static int
597
625
pg_TwoFloatsFromObj (PyObject * obj , float * val1 , float * val2 )
598
626
{
599
- 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 )) {
600
633
return pg_TwoFloatsFromObj (PyTuple_GET_ITEM (obj , 0 ), val1 , val2 );
601
634
}
602
- 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
603
641
return 0 ;
604
642
}
605
643
606
- // Can use PySequence_ITEM because of the PySequence_Check above.
644
+ // Now we can extract the items, using this macro because we know
645
+ // obj is a PySequence.
607
646
PyObject * item1 = PySequence_ITEM (obj , 0 );
608
647
PyObject * item2 = PySequence_ITEM (obj , 1 );
609
648
649
+ // If either item is NULL lets get out of here
610
650
if (item1 == NULL || item2 == NULL ) {
611
651
Py_XDECREF (item1 );
612
652
Py_XDECREF (item2 );
613
653
PyErr_Clear ();
614
654
return 0 ;
615
655
}
616
656
617
- if (!pg_FloatFromObj (item1 , val1 ) || !pg_FloatFromObj (item2 , val2 )) {
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 ();
618
677
Py_DECREF (item1 );
619
678
Py_DECREF (item2 );
620
679
return 0 ;
0 commit comments