LCOV - code coverage report
Current view: top level - Modules - _bisectmodule.c (source / functions) Hit Total Coverage
Test: CPython 3.12 LCOV report [commit acb105a7c1f] Lines: 105 114 92.1 %
Date: 2022-07-20 13:12:14 Functions: 11 11 100.0 %
Branches: 57 64 89.1 %

           Branch data     Line data    Source code
       1                 :            : /* Bisection algorithms. Drop in replacement for bisect.py
       2                 :            : 
       3                 :            : Converted to C by Dmitry Vasiliev (dima at hlabs.spb.ru).
       4                 :            : */
       5                 :            : 
       6                 :            : #define PY_SSIZE_T_CLEAN
       7                 :            : #include "Python.h"
       8                 :            : 
       9                 :            : /*[clinic input]
      10                 :            : module _bisect
      11                 :            : [clinic start generated code]*/
      12                 :            : /*[clinic end generated code: output=da39a3ee5e6b4b0d input=4d56a2b2033b462b]*/
      13                 :            : 
      14                 :            : #include "clinic/_bisectmodule.c.h"
      15                 :            : 
      16                 :            : typedef struct {
      17                 :            :     PyObject *str_insert;
      18                 :            : } bisect_state;
      19                 :            : 
      20                 :            : static inline bisect_state*
      21                 :       2724 : get_bisect_state(PyObject *module)
      22                 :            : {
      23                 :       2724 :     void *state = PyModule_GetState(module);
      24                 :            :     assert(state != NULL);
      25                 :       2724 :     return (bisect_state *)state;
      26                 :            : }
      27                 :            : 
      28                 :            : static inline Py_ssize_t
      29                 :     180166 : internal_bisect_right(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi,
      30                 :            :                       PyObject* key)
      31                 :            : {
      32                 :            :     PyObject *litem;
      33                 :            :     Py_ssize_t mid;
      34                 :            :     int res;
      35                 :            : 
      36         [ +  + ]:     180166 :     if (lo < 0) {
      37                 :          2 :         PyErr_SetString(PyExc_ValueError, "lo must be non-negative");
      38                 :          2 :         return -1;
      39                 :            :     }
      40         [ +  + ]:     180164 :     if (hi == -1) {
      41                 :     129269 :         hi = PySequence_Size(list);
      42         [ +  + ]:     129269 :         if (hi < 0)
      43                 :          4 :             return -1;
      44                 :            :     }
      45         [ +  + ]:    1429380 :     while (lo < hi) {
      46                 :            :         /* The (size_t)cast ensures that the addition and subsequent division
      47                 :            :            are performed as unsigned operations, avoiding difficulties from
      48                 :            :            signed overflow.  (See issue 13496.) */
      49                 :    1249224 :         mid = ((size_t)lo + hi) / 2;
      50                 :    1249224 :         litem = PySequence_GetItem(list, mid);
      51         [ +  + ]:    1249224 :         if (litem == NULL)
      52                 :          2 :             return -1;
      53         [ +  + ]:    1249222 :         if (key != Py_None) {
      54                 :        236 :             PyObject *newitem = PyObject_CallOneArg(key, litem);
      55         [ -  + ]:        236 :             if (newitem == NULL) {
      56                 :          0 :                 Py_DECREF(litem);
      57                 :          0 :                 return -1;
      58                 :            :             }
      59                 :        236 :             Py_SETREF(litem, newitem);
      60                 :            :         }
      61                 :    1249222 :         res = PyObject_RichCompareBool(item, litem, Py_LT);
      62                 :    1249222 :         Py_DECREF(litem);
      63         [ +  + ]:    1249222 :         if (res < 0)
      64                 :          2 :             return -1;
      65         [ +  + ]:    1249220 :         if (res)
      66                 :     683287 :             hi = mid;
      67                 :            :         else
      68                 :     565933 :             lo = mid + 1;
      69                 :            :     }
      70                 :     180156 :     return lo;
      71                 :            : }
      72                 :            : 
      73                 :            : /*[clinic input]
      74                 :            : _bisect.bisect_right -> Py_ssize_t
      75                 :            : 
      76                 :            :     a: object
      77                 :            :     x: object
      78                 :            :     lo: Py_ssize_t = 0
      79                 :            :     hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None
      80                 :            :     *
      81                 :            :     key: object = None
      82                 :            : 
      83                 :            : Return the index where to insert item x in list a, assuming a is sorted.
      84                 :            : 
      85                 :            : The return value i is such that all e in a[:i] have e <= x, and all e in
      86                 :            : a[i:] have e > x.  So if x already appears in the list, a.insert(i, x) will
      87                 :            : insert just after the rightmost x already there.
      88                 :            : 
      89                 :            : Optional args lo (default 0) and hi (default len(a)) bound the
      90                 :            : slice of a to be searched.
      91                 :            : [clinic start generated code]*/
      92                 :            : 
      93                 :            : static Py_ssize_t
      94                 :     111650 : _bisect_bisect_right_impl(PyObject *module, PyObject *a, PyObject *x,
      95                 :            :                           Py_ssize_t lo, Py_ssize_t hi, PyObject *key)
      96                 :            : /*[clinic end generated code: output=3a4bc09cc7c8a73d input=40fcc5afa06ae593]*/
      97                 :            : {
      98                 :     111650 :     return internal_bisect_right(a, x, lo, hi, key);
      99                 :            : }
     100                 :            : 
     101                 :            : /*[clinic input]
     102                 :            : _bisect.insort_right
     103                 :            : 
     104                 :            :     a: object
     105                 :            :     x: object
     106                 :            :     lo: Py_ssize_t = 0
     107                 :            :     hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None
     108                 :            :     *
     109                 :            :     key: object = None
     110                 :            : 
     111                 :            : Insert item x in list a, and keep it sorted assuming a is sorted.
     112                 :            : 
     113                 :            : If x is already in a, insert it to the right of the rightmost x.
     114                 :            : 
     115                 :            : Optional args lo (default 0) and hi (default len(a)) bound the
     116                 :            : slice of a to be searched.
     117                 :            : [clinic start generated code]*/
     118                 :            : 
     119                 :            : static PyObject *
     120                 :      68517 : _bisect_insort_right_impl(PyObject *module, PyObject *a, PyObject *x,
     121                 :            :                           Py_ssize_t lo, Py_ssize_t hi, PyObject *key)
     122                 :            : /*[clinic end generated code: output=ac3bf26d07aedda2 input=44e1708e26b7b802]*/
     123                 :            : {
     124                 :            :     PyObject *result, *key_x;
     125                 :            :     Py_ssize_t index;
     126                 :            : 
     127         [ +  + ]:      68517 :     if (key == Py_None) {
     128                 :      68475 :         index = internal_bisect_right(a, x, lo, hi, key);
     129                 :            :     } else {
     130                 :         42 :         key_x = PyObject_CallOneArg(key, x);
     131         [ +  + ]:         42 :         if (key_x == NULL) {
     132                 :          1 :             return NULL;
     133                 :            :         }
     134                 :         41 :         index = internal_bisect_right(a, key_x, lo, hi, key);
     135                 :         41 :         Py_DECREF(key_x);
     136                 :            :     }
     137         [ +  + ]:      68516 :     if (index < 0)
     138                 :          5 :         return NULL;
     139         [ +  + ]:      68511 :     if (PyList_CheckExact(a)) {
     140         [ -  + ]:      68266 :         if (PyList_Insert(a, index, x) < 0)
     141                 :          0 :             return NULL;
     142                 :            :     }
     143                 :            :     else {
     144                 :        245 :         bisect_state *state = get_bisect_state(module);
     145                 :        245 :         result = _PyObject_CallMethod(a, state->str_insert, "nO", index, x);
     146         [ -  + ]:        245 :         if (result == NULL)
     147                 :          0 :             return NULL;
     148                 :        245 :         Py_DECREF(result);
     149                 :            :     }
     150                 :            : 
     151                 :      68511 :     Py_RETURN_NONE;
     152                 :            : }
     153                 :            : 
     154                 :            : static inline Py_ssize_t
     155                 :      46837 : internal_bisect_left(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi,
     156                 :            :                      PyObject *key)
     157                 :            : {
     158                 :            :     PyObject *litem;
     159                 :            :     Py_ssize_t mid;
     160                 :            :     int res;
     161                 :            : 
     162         [ +  + ]:      46837 :     if (lo < 0) {
     163                 :          2 :         PyErr_SetString(PyExc_ValueError, "lo must be non-negative");
     164                 :          2 :         return -1;
     165                 :            :     }
     166         [ +  + ]:      46835 :     if (hi == -1) {
     167                 :      46050 :         hi = PySequence_Size(list);
     168         [ +  + ]:      46050 :         if (hi < 0)
     169                 :          4 :             return -1;
     170                 :            :     }
     171         [ +  + ]:     195846 :     while (lo < hi) {
     172                 :            :         /* The (size_t)cast ensures that the addition and subsequent division
     173                 :            :            are performed as unsigned operations, avoiding difficulties from
     174                 :            :            signed overflow.  (See issue 13496.) */
     175                 :     149019 :         mid = ((size_t)lo + hi) / 2;
     176                 :     149019 :         litem = PySequence_GetItem(list, mid);
     177         [ +  + ]:     149019 :         if (litem == NULL)
     178                 :          2 :             return -1;
     179         [ +  + ]:     149017 :         if (key != Py_None) {
     180                 :        242 :             PyObject *newitem = PyObject_CallOneArg(key, litem);
     181         [ -  + ]:        242 :             if (newitem == NULL) {
     182                 :          0 :                 Py_DECREF(litem);
     183                 :          0 :                 return -1;
     184                 :            :             }
     185                 :        242 :             Py_SETREF(litem, newitem);
     186                 :            :         }
     187                 :     149017 :         res = PyObject_RichCompareBool(litem, item, Py_LT);
     188                 :     149017 :         Py_DECREF(litem);
     189         [ +  + ]:     149017 :         if (res < 0)
     190                 :          2 :             return -1;
     191         [ +  + ]:     149015 :         if (res)
     192                 :      38200 :             lo = mid + 1;
     193                 :            :         else
     194                 :     110815 :             hi = mid;
     195                 :            :     }
     196                 :      46827 :     return lo;
     197                 :            : }
     198                 :            : 
     199                 :            : 
     200                 :            : /*[clinic input]
     201                 :            : _bisect.bisect_left -> Py_ssize_t
     202                 :            : 
     203                 :            :     a: object
     204                 :            :     x: object
     205                 :            :     lo: Py_ssize_t = 0
     206                 :            :     hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None
     207                 :            :     *
     208                 :            :     key: object = None
     209                 :            : 
     210                 :            : Return the index where to insert item x in list a, assuming a is sorted.
     211                 :            : 
     212                 :            : The return value i is such that all e in a[:i] have e < x, and all e in
     213                 :            : a[i:] have e >= x.  So if x already appears in the list, a.insert(i, x) will
     214                 :            : insert just before the leftmost x already there.
     215                 :            : 
     216                 :            : Optional args lo (default 0) and hi (default len(a)) bound the
     217                 :            : slice of a to be searched.
     218                 :            : [clinic start generated code]*/
     219                 :            : 
     220                 :            : static Py_ssize_t
     221                 :      46276 : _bisect_bisect_left_impl(PyObject *module, PyObject *a, PyObject *x,
     222                 :            :                          Py_ssize_t lo, Py_ssize_t hi, PyObject *key)
     223                 :            : /*[clinic end generated code: output=70749d6e5cae9284 input=90dd35b50ceb05e3]*/
     224                 :            : {
     225                 :      46276 :     return internal_bisect_left(a, x, lo, hi, key);
     226                 :            : }
     227                 :            : 
     228                 :            : 
     229                 :            : /*[clinic input]
     230                 :            : _bisect.insort_left
     231                 :            : 
     232                 :            :     a: object
     233                 :            :     x: object
     234                 :            :     lo: Py_ssize_t = 0
     235                 :            :     hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None
     236                 :            :     *
     237                 :            :     key: object = None
     238                 :            : 
     239                 :            : Insert item x in list a, and keep it sorted assuming a is sorted.
     240                 :            : 
     241                 :            : If x is already in a, insert it to the left of the leftmost x.
     242                 :            : 
     243                 :            : Optional args lo (default 0) and hi (default len(a)) bound the
     244                 :            : slice of a to be searched.
     245                 :            : [clinic start generated code]*/
     246                 :            : 
     247                 :            : static PyObject *
     248                 :        562 : _bisect_insort_left_impl(PyObject *module, PyObject *a, PyObject *x,
     249                 :            :                          Py_ssize_t lo, Py_ssize_t hi, PyObject *key)
     250                 :            : /*[clinic end generated code: output=b1d33e5e7ffff11e input=3ab65d8784f585b1]*/
     251                 :            : {
     252                 :            :     PyObject *result, *key_x;
     253                 :            :     Py_ssize_t index;
     254                 :            : 
     255         [ +  + ]:        562 :     if (key == Py_None) {
     256                 :        520 :         index = internal_bisect_left(a, x, lo, hi, key);
     257                 :            :     } else {
     258                 :         42 :         key_x = PyObject_CallOneArg(key, x);
     259         [ +  + ]:         42 :         if (key_x == NULL) {
     260                 :          1 :             return NULL;
     261                 :            :         }
     262                 :         41 :         index = internal_bisect_left(a, key_x, lo, hi, key);
     263                 :         41 :         Py_DECREF(key_x);
     264                 :            :     }
     265         [ +  + ]:        561 :     if (index < 0)
     266                 :          5 :         return NULL;
     267         [ +  + ]:        556 :     if (PyList_CheckExact(a)) {
     268         [ -  + ]:        297 :         if (PyList_Insert(a, index, x) < 0)
     269                 :          0 :             return NULL;
     270                 :            :     } else {
     271                 :        259 :         bisect_state *state = get_bisect_state(module);
     272                 :        259 :         result = _PyObject_CallMethod(a, state->str_insert, "nO", index, x);
     273         [ -  + ]:        259 :         if (result == NULL)
     274                 :          0 :             return NULL;
     275                 :        259 :         Py_DECREF(result);
     276                 :            :     }
     277                 :            : 
     278                 :        556 :     Py_RETURN_NONE;
     279                 :            : }
     280                 :            : 
     281                 :            : static PyMethodDef bisect_methods[] = {
     282                 :            :     _BISECT_BISECT_RIGHT_METHODDEF
     283                 :            :     _BISECT_INSORT_RIGHT_METHODDEF
     284                 :            :     _BISECT_BISECT_LEFT_METHODDEF
     285                 :            :     _BISECT_INSORT_LEFT_METHODDEF
     286                 :            :     {NULL, NULL} /* sentinel */
     287                 :            : };
     288                 :            : 
     289                 :            : PyDoc_STRVAR(module_doc,
     290                 :            : "Bisection algorithms.\n\
     291                 :            : \n\
     292                 :            : This module provides support for maintaining a list in sorted order without\n\
     293                 :            : having to sort the list after each insertion. For long lists of items with\n\
     294                 :            : expensive comparison operations, this can be an improvement over the more\n\
     295                 :            : common approach.\n");
     296                 :            : 
     297                 :            : static int
     298                 :       1114 : bisect_clear(PyObject *module)
     299                 :            : {
     300                 :       1114 :     bisect_state *state = get_bisect_state(module);
     301         [ +  + ]:       1114 :     Py_CLEAR(state->str_insert);
     302                 :       1114 :     return 0;
     303                 :            : }
     304                 :            : 
     305                 :            : static void
     306                 :       1106 : bisect_free(void *module)
     307                 :            : {
     308                 :       1106 :     bisect_clear((PyObject *)module);
     309                 :       1106 : }
     310                 :            : 
     311                 :            : static int
     312                 :       1106 : bisect_modexec(PyObject *m)
     313                 :            : {
     314                 :       1106 :     bisect_state *state = get_bisect_state(m);
     315                 :       1106 :     state->str_insert = PyUnicode_InternFromString("insert");
     316         [ -  + ]:       1106 :     if (state->str_insert == NULL) {
     317                 :          0 :         return -1;
     318                 :            :     }
     319                 :       1106 :     return 0;
     320                 :            : }
     321                 :            : 
     322                 :            : static PyModuleDef_Slot bisect_slots[] = {
     323                 :            :     {Py_mod_exec, bisect_modexec},
     324                 :            :     {0, NULL}
     325                 :            : };
     326                 :            : 
     327                 :            : static struct PyModuleDef _bisectmodule = {
     328                 :            :     PyModuleDef_HEAD_INIT,
     329                 :            :     .m_name = "_bisect",
     330                 :            :     .m_size = sizeof(bisect_state),
     331                 :            :     .m_doc = module_doc,
     332                 :            :     .m_methods = bisect_methods,
     333                 :            :     .m_slots = bisect_slots,
     334                 :            :     .m_clear = bisect_clear,
     335                 :            :     .m_free = bisect_free,
     336                 :            : };
     337                 :            : 
     338                 :            : PyMODINIT_FUNC
     339                 :       1106 : PyInit__bisect(void)
     340                 :            : {
     341                 :       1106 :     return PyModuleDef_Init(&_bisectmodule);
     342                 :            : }

Generated by: LCOV version 1.14