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 : 5 : get_bisect_state(PyObject *module)
22 : : {
23 : 5 : void *state = PyModule_GetState(module);
24 : : assert(state != NULL);
25 : 5 : return (bisect_state *)state;
26 : : }
27 : :
28 : : static ssizeargfunc
29 : 0 : get_sq_item(PyObject *s)
30 : : {
31 : : // The parts of PySequence_GetItem that we only need to do once
32 : 0 : PyTypeObject *tp = Py_TYPE(s);
33 : 0 : PySequenceMethods *m = tp->tp_as_sequence;
34 [ # # # # ]: 0 : if (m && m->sq_item) {
35 : 0 : return m->sq_item;
36 : : }
37 : : const char *msg;
38 [ # # # # ]: 0 : if (tp->tp_as_mapping && tp->tp_as_mapping->mp_subscript) {
39 : 0 : msg = "%.200s is not a sequence";
40 : : }
41 : : else {
42 : 0 : msg = "'%.200s' object does not support indexing";
43 : : }
44 : 0 : PyErr_Format(PyExc_TypeError, msg, tp->tp_name);
45 : 0 : return NULL;
46 : : }
47 : :
48 : : static inline Py_ssize_t
49 : 0 : internal_bisect_right(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi,
50 : : PyObject* key)
51 : : {
52 : : PyObject *litem;
53 : : Py_ssize_t mid;
54 : : int res;
55 : :
56 [ # # ]: 0 : if (lo < 0) {
57 : 0 : PyErr_SetString(PyExc_ValueError, "lo must be non-negative");
58 : 0 : return -1;
59 : : }
60 [ # # ]: 0 : if (hi == -1) {
61 : 0 : hi = PySequence_Size(list);
62 [ # # ]: 0 : if (hi < 0)
63 : 0 : return -1;
64 : : }
65 : 0 : ssizeargfunc sq_item = get_sq_item(list);
66 [ # # ]: 0 : if (sq_item == NULL) {
67 : 0 : return -1;
68 : : }
69 [ # # ]: 0 : if (Py_EnterRecursiveCall("in _bisect.bisect_right")) {
70 : 0 : return -1;
71 : : }
72 : 0 : PyTypeObject *tp = Py_TYPE(item);
73 : 0 : richcmpfunc compare = tp->tp_richcompare;
74 [ # # ]: 0 : while (lo < hi) {
75 : : /* The (size_t)cast ensures that the addition and subsequent division
76 : : are performed as unsigned operations, avoiding difficulties from
77 : : signed overflow. (See issue 13496.) */
78 : 0 : mid = ((size_t)lo + hi) / 2;
79 : : assert(mid >= 0);
80 : : // PySequence_GetItem, but we already checked the types.
81 : 0 : litem = sq_item(list, mid);
82 : : assert((PyErr_Occurred() == NULL) ^ (litem == NULL));
83 [ # # ]: 0 : if (litem == NULL) {
84 : 0 : goto error;
85 : : }
86 [ # # ]: 0 : if (key != Py_None) {
87 : 0 : PyObject *newitem = PyObject_CallOneArg(key, litem);
88 [ # # ]: 0 : if (newitem == NULL) {
89 : 0 : goto error;
90 : : }
91 : 0 : Py_SETREF(litem, newitem);
92 : : }
93 : : /* if item < key(list[mid]):
94 : : * hi = mid
95 : : * else:
96 : : * lo = mid + 1
97 : : */
98 [ # # # # ]: 0 : if (compare != NULL && Py_IS_TYPE(litem, tp)) {
99 : : // A fast path for comparing objects of the same type
100 : 0 : PyObject *res_obj = compare(item, litem, Py_LT);
101 [ # # ]: 0 : if (res_obj == Py_True) {
102 : 0 : Py_DECREF(res_obj);
103 : 0 : Py_DECREF(litem);
104 : 0 : hi = mid;
105 : 0 : continue;
106 : : }
107 [ # # ]: 0 : if (res_obj == Py_False) {
108 : 0 : Py_DECREF(res_obj);
109 : 0 : Py_DECREF(litem);
110 : 0 : lo = mid + 1;
111 : 0 : continue;
112 : : }
113 [ # # ]: 0 : if (res_obj == NULL) {
114 : 0 : goto error;
115 : : }
116 [ # # ]: 0 : if (res_obj == Py_NotImplemented) {
117 : 0 : Py_DECREF(res_obj);
118 : 0 : compare = NULL;
119 : 0 : res = PyObject_RichCompareBool(item, litem, Py_LT);
120 : : }
121 : : else {
122 : 0 : res = PyObject_IsTrue(res_obj);
123 : 0 : Py_DECREF(res_obj);
124 : : }
125 : : }
126 : : else {
127 : : // A default path for comparing arbitrary objects
128 : 0 : res = PyObject_RichCompareBool(item, litem, Py_LT);
129 : : }
130 [ # # ]: 0 : if (res < 0) {
131 : 0 : goto error;
132 : : }
133 : 0 : Py_DECREF(litem);
134 [ # # ]: 0 : if (res)
135 : 0 : hi = mid;
136 : : else
137 : 0 : lo = mid + 1;
138 : : }
139 : 0 : Py_LeaveRecursiveCall();
140 : 0 : return lo;
141 : 0 : error:
142 : 0 : Py_LeaveRecursiveCall();
143 : 0 : Py_XDECREF(litem);
144 : 0 : return -1;
145 : : }
146 : :
147 : : /*[clinic input]
148 : : _bisect.bisect_right -> Py_ssize_t
149 : :
150 : : a: object
151 : : x: object
152 : : lo: Py_ssize_t = 0
153 : : hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None
154 : : *
155 : : key: object = None
156 : :
157 : : Return the index where to insert item x in list a, assuming a is sorted.
158 : :
159 : : The return value i is such that all e in a[:i] have e <= x, and all e in
160 : : a[i:] have e > x. So if x already appears in the list, a.insert(i, x) will
161 : : insert just after the rightmost x already there.
162 : :
163 : : Optional args lo (default 0) and hi (default len(a)) bound the
164 : : slice of a to be searched.
165 : : [clinic start generated code]*/
166 : :
167 : : static Py_ssize_t
168 : 0 : _bisect_bisect_right_impl(PyObject *module, PyObject *a, PyObject *x,
169 : : Py_ssize_t lo, Py_ssize_t hi, PyObject *key)
170 : : /*[clinic end generated code: output=3a4bc09cc7c8a73d input=40fcc5afa06ae593]*/
171 : : {
172 : 0 : return internal_bisect_right(a, x, lo, hi, key);
173 : : }
174 : :
175 : : /*[clinic input]
176 : : _bisect.insort_right
177 : :
178 : : a: object
179 : : x: object
180 : : lo: Py_ssize_t = 0
181 : : hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None
182 : : *
183 : : key: object = None
184 : :
185 : : Insert item x in list a, and keep it sorted assuming a is sorted.
186 : :
187 : : If x is already in a, insert it to the right of the rightmost x.
188 : :
189 : : Optional args lo (default 0) and hi (default len(a)) bound the
190 : : slice of a to be searched.
191 : : [clinic start generated code]*/
192 : :
193 : : static PyObject *
194 : 0 : _bisect_insort_right_impl(PyObject *module, PyObject *a, PyObject *x,
195 : : Py_ssize_t lo, Py_ssize_t hi, PyObject *key)
196 : : /*[clinic end generated code: output=ac3bf26d07aedda2 input=44e1708e26b7b802]*/
197 : : {
198 : : PyObject *result, *key_x;
199 : : Py_ssize_t index;
200 : :
201 [ # # ]: 0 : if (key == Py_None) {
202 : 0 : index = internal_bisect_right(a, x, lo, hi, key);
203 : : } else {
204 : 0 : key_x = PyObject_CallOneArg(key, x);
205 [ # # ]: 0 : if (key_x == NULL) {
206 : 0 : return NULL;
207 : : }
208 : 0 : index = internal_bisect_right(a, key_x, lo, hi, key);
209 : 0 : Py_DECREF(key_x);
210 : : }
211 [ # # ]: 0 : if (index < 0)
212 : 0 : return NULL;
213 [ # # ]: 0 : if (PyList_CheckExact(a)) {
214 [ # # ]: 0 : if (PyList_Insert(a, index, x) < 0)
215 : 0 : return NULL;
216 : : }
217 : : else {
218 : 0 : bisect_state *state = get_bisect_state(module);
219 : 0 : result = _PyObject_CallMethod(a, state->str_insert, "nO", index, x);
220 [ # # ]: 0 : if (result == NULL)
221 : 0 : return NULL;
222 : 0 : Py_DECREF(result);
223 : : }
224 : :
225 : 0 : Py_RETURN_NONE;
226 : : }
227 : :
228 : : static inline Py_ssize_t
229 : 0 : internal_bisect_left(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t hi,
230 : : PyObject *key)
231 : : {
232 : : PyObject *litem;
233 : : Py_ssize_t mid;
234 : : int res;
235 : :
236 [ # # ]: 0 : if (lo < 0) {
237 : 0 : PyErr_SetString(PyExc_ValueError, "lo must be non-negative");
238 : 0 : return -1;
239 : : }
240 [ # # ]: 0 : if (hi == -1) {
241 : 0 : hi = PySequence_Size(list);
242 [ # # ]: 0 : if (hi < 0)
243 : 0 : return -1;
244 : : }
245 : 0 : ssizeargfunc sq_item = get_sq_item(list);
246 [ # # ]: 0 : if (sq_item == NULL) {
247 : 0 : return -1;
248 : : }
249 [ # # ]: 0 : if (Py_EnterRecursiveCall("in _bisect.bisect_left")) {
250 : 0 : return -1;
251 : : }
252 : 0 : PyTypeObject *tp = Py_TYPE(item);
253 : 0 : richcmpfunc compare = tp->tp_richcompare;
254 [ # # ]: 0 : while (lo < hi) {
255 : : /* The (size_t)cast ensures that the addition and subsequent division
256 : : are performed as unsigned operations, avoiding difficulties from
257 : : signed overflow. (See issue 13496.) */
258 : 0 : mid = ((size_t)lo + hi) / 2;
259 : : assert(mid >= 0);
260 : : // PySequence_GetItem, but we already checked the types.
261 : 0 : litem = sq_item(list, mid);
262 : : assert((PyErr_Occurred() == NULL) ^ (litem == NULL));
263 [ # # ]: 0 : if (litem == NULL) {
264 : 0 : goto error;
265 : : }
266 [ # # ]: 0 : if (key != Py_None) {
267 : 0 : PyObject *newitem = PyObject_CallOneArg(key, litem);
268 [ # # ]: 0 : if (newitem == NULL) {
269 : 0 : goto error;
270 : : }
271 : 0 : Py_SETREF(litem, newitem);
272 : : }
273 : : /* if key(list[mid]) < item:
274 : : * lo = mid + 1
275 : : * else:
276 : : * hi = mid
277 : : */
278 [ # # # # ]: 0 : if (compare != NULL && Py_IS_TYPE(litem, tp)) {
279 : : // A fast path for comparing objects of the same type
280 : 0 : PyObject *res_obj = compare(litem, item, Py_LT);
281 [ # # ]: 0 : if (res_obj == Py_True) {
282 : 0 : Py_DECREF(res_obj);
283 : 0 : Py_DECREF(litem);
284 : 0 : lo = mid + 1;
285 : 0 : continue;
286 : : }
287 [ # # ]: 0 : if (res_obj == Py_False) {
288 : 0 : Py_DECREF(res_obj);
289 : 0 : Py_DECREF(litem);
290 : 0 : hi = mid;
291 : 0 : continue;
292 : : }
293 [ # # ]: 0 : if (res_obj == NULL) {
294 : 0 : goto error;
295 : : }
296 [ # # ]: 0 : if (res_obj == Py_NotImplemented) {
297 : 0 : Py_DECREF(res_obj);
298 : 0 : compare = NULL;
299 : 0 : res = PyObject_RichCompareBool(litem, item, Py_LT);
300 : : }
301 : : else {
302 : 0 : res = PyObject_IsTrue(res_obj);
303 : 0 : Py_DECREF(res_obj);
304 : : }
305 : : }
306 : : else {
307 : : // A default path for comparing arbitrary objects
308 : 0 : res = PyObject_RichCompareBool(litem, item, Py_LT);
309 : : }
310 [ # # ]: 0 : if (res < 0) {
311 : 0 : goto error;
312 : : }
313 : 0 : Py_DECREF(litem);
314 [ # # ]: 0 : if (res)
315 : 0 : lo = mid + 1;
316 : : else
317 : 0 : hi = mid;
318 : : }
319 : 0 : Py_LeaveRecursiveCall();
320 : 0 : return lo;
321 : 0 : error:
322 : 0 : Py_LeaveRecursiveCall();
323 : 0 : Py_XDECREF(litem);
324 : 0 : return -1;
325 : : }
326 : :
327 : :
328 : : /*[clinic input]
329 : : _bisect.bisect_left -> Py_ssize_t
330 : :
331 : : a: object
332 : : x: object
333 : : lo: Py_ssize_t = 0
334 : : hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None
335 : : *
336 : : key: object = None
337 : :
338 : : Return the index where to insert item x in list a, assuming a is sorted.
339 : :
340 : : The return value i is such that all e in a[:i] have e < x, and all e in
341 : : a[i:] have e >= x. So if x already appears in the list, a.insert(i, x) will
342 : : insert just before the leftmost x already there.
343 : :
344 : : Optional args lo (default 0) and hi (default len(a)) bound the
345 : : slice of a to be searched.
346 : : [clinic start generated code]*/
347 : :
348 : : static Py_ssize_t
349 : 0 : _bisect_bisect_left_impl(PyObject *module, PyObject *a, PyObject *x,
350 : : Py_ssize_t lo, Py_ssize_t hi, PyObject *key)
351 : : /*[clinic end generated code: output=70749d6e5cae9284 input=90dd35b50ceb05e3]*/
352 : : {
353 : 0 : return internal_bisect_left(a, x, lo, hi, key);
354 : : }
355 : :
356 : :
357 : : /*[clinic input]
358 : : _bisect.insort_left
359 : :
360 : : a: object
361 : : x: object
362 : : lo: Py_ssize_t = 0
363 : : hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None
364 : : *
365 : : key: object = None
366 : :
367 : : Insert item x in list a, and keep it sorted assuming a is sorted.
368 : :
369 : : If x is already in a, insert it to the left of the leftmost x.
370 : :
371 : : Optional args lo (default 0) and hi (default len(a)) bound the
372 : : slice of a to be searched.
373 : : [clinic start generated code]*/
374 : :
375 : : static PyObject *
376 : 0 : _bisect_insort_left_impl(PyObject *module, PyObject *a, PyObject *x,
377 : : Py_ssize_t lo, Py_ssize_t hi, PyObject *key)
378 : : /*[clinic end generated code: output=b1d33e5e7ffff11e input=3ab65d8784f585b1]*/
379 : : {
380 : : PyObject *result, *key_x;
381 : : Py_ssize_t index;
382 : :
383 [ # # ]: 0 : if (key == Py_None) {
384 : 0 : index = internal_bisect_left(a, x, lo, hi, key);
385 : : } else {
386 : 0 : key_x = PyObject_CallOneArg(key, x);
387 [ # # ]: 0 : if (key_x == NULL) {
388 : 0 : return NULL;
389 : : }
390 : 0 : index = internal_bisect_left(a, key_x, lo, hi, key);
391 : 0 : Py_DECREF(key_x);
392 : : }
393 [ # # ]: 0 : if (index < 0)
394 : 0 : return NULL;
395 [ # # ]: 0 : if (PyList_CheckExact(a)) {
396 [ # # ]: 0 : if (PyList_Insert(a, index, x) < 0)
397 : 0 : return NULL;
398 : : } else {
399 : 0 : bisect_state *state = get_bisect_state(module);
400 : 0 : result = _PyObject_CallMethod(a, state->str_insert, "nO", index, x);
401 [ # # ]: 0 : if (result == NULL)
402 : 0 : return NULL;
403 : 0 : Py_DECREF(result);
404 : : }
405 : :
406 : 0 : Py_RETURN_NONE;
407 : : }
408 : :
409 : : static PyMethodDef bisect_methods[] = {
410 : : _BISECT_BISECT_RIGHT_METHODDEF
411 : : _BISECT_INSORT_RIGHT_METHODDEF
412 : : _BISECT_BISECT_LEFT_METHODDEF
413 : : _BISECT_INSORT_LEFT_METHODDEF
414 : : {NULL, NULL} /* sentinel */
415 : : };
416 : :
417 : : PyDoc_STRVAR(module_doc,
418 : : "Bisection algorithms.\n\
419 : : \n\
420 : : This module provides support for maintaining a list in sorted order without\n\
421 : : having to sort the list after each insertion. For long lists of items with\n\
422 : : expensive comparison operations, this can be an improvement over the more\n\
423 : : common approach.\n");
424 : :
425 : : static int
426 : 3 : bisect_clear(PyObject *module)
427 : : {
428 : 3 : bisect_state *state = get_bisect_state(module);
429 [ + + ]: 3 : Py_CLEAR(state->str_insert);
430 : 3 : return 0;
431 : : }
432 : :
433 : : static void
434 : 2 : bisect_free(void *module)
435 : : {
436 : 2 : bisect_clear((PyObject *)module);
437 : 2 : }
438 : :
439 : : static int
440 : 2 : bisect_modexec(PyObject *m)
441 : : {
442 : 2 : bisect_state *state = get_bisect_state(m);
443 : 2 : state->str_insert = PyUnicode_InternFromString("insert");
444 [ - + ]: 2 : if (state->str_insert == NULL) {
445 : 0 : return -1;
446 : : }
447 : 2 : return 0;
448 : : }
449 : :
450 : : static PyModuleDef_Slot bisect_slots[] = {
451 : : {Py_mod_exec, bisect_modexec},
452 : : {0, NULL}
453 : : };
454 : :
455 : : static struct PyModuleDef _bisectmodule = {
456 : : PyModuleDef_HEAD_INIT,
457 : : .m_name = "_bisect",
458 : : .m_size = sizeof(bisect_state),
459 : : .m_doc = module_doc,
460 : : .m_methods = bisect_methods,
461 : : .m_slots = bisect_slots,
462 : : .m_clear = bisect_clear,
463 : : .m_free = bisect_free,
464 : : };
465 : :
466 : : PyMODINIT_FUNC
467 : 2 : PyInit__bisect(void)
468 : : {
469 : 2 : return PyModuleDef_Init(&_bisectmodule);
470 : : }
|