Skip to content

Commit 480a472

Browse files
committed
Another pg_Int/Float helper optimization approach.
1 parent 4de5466 commit 480a472

File tree

1 file changed

+85
-26
lines changed

1 file changed

+85
-26
lines changed

src_c/base.c

Lines changed: 85 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,25 +530,56 @@ 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
542547
return 0;
543548
}
544549

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.
546552
PyObject *item1 = PySequence_ITEM(obj, 0);
547553
PyObject *item2 = PySequence_ITEM(obj, 1);
548554

555+
// If either item is NULL lets get out of here
549556
if (item1 == NULL || item2 == NULL) {
550557
Py_XDECREF(item1);
551558
Py_XDECREF(item2);
552559
PyErr_Clear();
553560
return 0;
554561
}
555562

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();
557583
Py_DECREF(item1);
558584
Py_DECREF(item2);
559585
return 0;
@@ -567,14 +593,16 @@ pg_TwoIntsFromObj(PyObject *obj, int *val1, int *val2)
567593
static int
568594
pg_FloatFromObj(PyObject *obj, float *val)
569595
{
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+
}
575605
}
576-
577-
*val = f;
578606
return 1;
579607
}
580608

@@ -596,25 +624,56 @@ pg_FloatFromObjIndex(PyObject *obj, int _index, float *val)
596624
static int
597625
pg_TwoFloatsFromObj(PyObject *obj, float *val1, float *val2)
598626
{
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)) {
600633
return pg_TwoFloatsFromObj(PyTuple_GET_ITEM(obj, 0), val1, val2);
601634
}
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
603641
return 0;
604642
}
605643

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.
607646
PyObject *item1 = PySequence_ITEM(obj, 0);
608647
PyObject *item2 = PySequence_ITEM(obj, 1);
609648

649+
// If either item is NULL lets get out of here
610650
if (item1 == NULL || item2 == NULL) {
611651
Py_XDECREF(item1);
612652
Py_XDECREF(item2);
613653
PyErr_Clear();
614654
return 0;
615655
}
616656

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();
618677
Py_DECREF(item1);
619678
Py_DECREF(item2);
620679
return 0;

0 commit comments

Comments
 (0)