Skip to content

Commit 6026d1e

Browse files
authored
Merge pull request #3214 from Starbuck5/optimize-pg-two-things-from-obj
Optimize pg_Two(Ints/Floats)FromObj
2 parents 5dace5c + 480a472 commit 6026d1e

File tree

1 file changed

+117
-26
lines changed

1 file changed

+117
-26
lines changed

src_c/base.c

Lines changed: 117 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -496,24 +496,19 @@ pg_base_get_init(PyObject *self, PyObject *_null)
496496
static int
497497
pg_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)
535530
static int
536531
pg_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

551593
static int
552594
pg_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)
580624
static int
581625
pg_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

Comments
 (0)