@@ -25,6 +25,26 @@ get_bisect_state(PyObject *module)
2525 return (bisect_state * )state ;
2626}
2727
28+ static ssizeargfunc
29+ get_sq_item (PyObject * s )
30+ {
31+ // The parts of PySequence_GetItem that we only need to do once
32+ PyTypeObject * tp = Py_TYPE (s );
33+ PySequenceMethods * m = tp -> tp_as_sequence ;
34+ if (m && m -> sq_item ) {
35+ return m -> sq_item ;
36+ }
37+ const char * msg ;
38+ if (tp -> tp_as_mapping && tp -> tp_as_mapping -> mp_subscript ) {
39+ msg = "%.200s is not a sequence" ;
40+ }
41+ else {
42+ msg = "'%.200s' object does not support indexing" ;
43+ }
44+ PyErr_Format (PyExc_TypeError , msg , tp -> tp_name );
45+ return NULL ;
46+ }
47+
2848static inline Py_ssize_t
2949internal_bisect_right (PyObject * list , PyObject * item , Py_ssize_t lo , Py_ssize_t hi ,
3050 PyObject * key )
@@ -42,32 +62,85 @@ internal_bisect_right(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t
4262 if (hi < 0 )
4363 return -1 ;
4464 }
65+ ssizeargfunc sq_item = get_sq_item (list );
66+ if (sq_item == NULL ) {
67+ return -1 ;
68+ }
69+ if (Py_EnterRecursiveCall ("in _bisect.bisect_right" ) < 0 ) {
70+ return -1 ;
71+ }
72+ PyTypeObject * tp = Py_TYPE (item );
73+ richcmpfunc compare = tp -> tp_richcompare ;
4574 while (lo < hi ) {
4675 /* The (size_t)cast ensures that the addition and subsequent division
4776 are performed as unsigned operations, avoiding difficulties from
4877 signed overflow. (See issue 13496.) */
4978 mid = ((size_t )lo + hi ) / 2 ;
50- litem = PySequence_GetItem (list , mid );
51- if (litem == NULL )
52- return -1 ;
79+ assert (mid >= 0 );
80+ // PySequence_GetItem, but we already checked the types.
81+ litem = sq_item (list , mid );
82+ assert ((PyErr_Occurred () == NULL ) ^ (litem == NULL ));
83+ if (litem == NULL ) {
84+ goto error ;
85+ }
5386 if (key != Py_None ) {
5487 PyObject * newitem = PyObject_CallOneArg (key , litem );
5588 if (newitem == NULL ) {
56- Py_DECREF (litem );
57- return -1 ;
89+ goto error ;
5890 }
5991 Py_SETREF (litem , newitem );
6092 }
61- res = PyObject_RichCompareBool (item , litem , Py_LT );
93+ /* if item < key(list[mid]):
94+ * hi = mid
95+ * else:
96+ * lo = mid + 1
97+ */
98+ if (compare != NULL && Py_IS_TYPE (litem , tp )) {
99+ // A fast path for comparing objects of the same type
100+ PyObject * res_obj = compare (item , litem , Py_LT );
101+ if (res_obj == Py_True ) {
102+ Py_DECREF (res_obj );
103+ Py_DECREF (litem );
104+ hi = mid ;
105+ continue ;
106+ }
107+ if (res_obj == Py_False ) {
108+ Py_DECREF (res_obj );
109+ Py_DECREF (litem );
110+ lo = mid + 1 ;
111+ continue ;
112+ }
113+ if (res_obj == NULL ) {
114+ goto error ;
115+ }
116+ if (res_obj == Py_NotImplemented ) {
117+ Py_DECREF (res_obj );
118+ compare = NULL ;
119+ res = PyObject_RichCompareBool (item , litem , Py_LT );
120+ }
121+ else {
122+ res = PyObject_IsTrue (res_obj );
123+ }
124+ }
125+ else {
126+ // A default path for comparing arbitrary objects
127+ res = PyObject_RichCompareBool (item , litem , Py_LT );
128+ }
129+ if (res < 0 ) {
130+ goto error ;
131+ }
62132 Py_DECREF (litem );
63- if (res < 0 )
64- return -1 ;
65133 if (res )
66134 hi = mid ;
67135 else
68136 lo = mid + 1 ;
69137 }
138+ Py_LeaveRecursiveCall ();
70139 return lo ;
140+ error :
141+ Py_LeaveRecursiveCall ();
142+ Py_XDECREF (litem );
143+ return -1 ;
71144}
72145
73146/*[clinic input]
@@ -168,32 +241,85 @@ internal_bisect_left(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t h
168241 if (hi < 0 )
169242 return -1 ;
170243 }
244+ ssizeargfunc sq_item = get_sq_item (list );
245+ if (sq_item == NULL ) {
246+ return -1 ;
247+ }
248+ if (Py_EnterRecursiveCall ("in _bisect.bisect_left" ) < 0 ) {
249+ return -1 ;
250+ }
251+ PyTypeObject * tp = Py_TYPE (item );
252+ richcmpfunc compare = tp -> tp_richcompare ;
171253 while (lo < hi ) {
172254 /* The (size_t)cast ensures that the addition and subsequent division
173255 are performed as unsigned operations, avoiding difficulties from
174256 signed overflow. (See issue 13496.) */
175257 mid = ((size_t )lo + hi ) / 2 ;
176- litem = PySequence_GetItem (list , mid );
177- if (litem == NULL )
178- return -1 ;
258+ assert (mid >= 0 );
259+ // PySequence_GetItem, but we already checked the types.
260+ litem = sq_item (list , mid );
261+ assert ((PyErr_Occurred () == NULL ) ^ (litem == NULL ));
262+ if (litem == NULL ) {
263+ goto error ;
264+ }
179265 if (key != Py_None ) {
180266 PyObject * newitem = PyObject_CallOneArg (key , litem );
181267 if (newitem == NULL ) {
182- Py_DECREF (litem );
183- return -1 ;
268+ goto error ;
184269 }
185270 Py_SETREF (litem , newitem );
186271 }
187- res = PyObject_RichCompareBool (litem , item , Py_LT );
272+ /* if key(list[mid]) < item:
273+ * lo = mid + 1
274+ * else:
275+ * hi = mid
276+ */
277+ if (compare != NULL && Py_IS_TYPE (litem , tp )) {
278+ // A fast path for comparing objects of the same type
279+ PyObject * res_obj = compare (litem , item , Py_LT );
280+ if (res_obj == Py_True ) {
281+ Py_DECREF (res_obj );
282+ Py_DECREF (litem );
283+ lo = mid + 1 ;
284+ continue ;
285+ }
286+ if (res_obj == Py_False ) {
287+ Py_DECREF (res_obj );
288+ Py_DECREF (litem );
289+ hi = mid ;
290+ continue ;
291+ }
292+ if (res_obj == NULL ) {
293+ goto error ;
294+ }
295+ if (res_obj == Py_NotImplemented ) {
296+ Py_DECREF (res_obj );
297+ compare = NULL ;
298+ res = PyObject_RichCompareBool (litem , item , Py_LT );
299+ }
300+ else {
301+ res = PyObject_IsTrue (res_obj );
302+ }
303+ }
304+ else {
305+ // A default path for comparing arbitrary objects
306+ res = PyObject_RichCompareBool (litem , item , Py_LT );
307+ }
308+ if (res < 0 ) {
309+ goto error ;
310+ }
188311 Py_DECREF (litem );
189- if (res < 0 )
190- return -1 ;
191312 if (res )
192313 lo = mid + 1 ;
193314 else
194315 hi = mid ;
195316 }
317+ Py_LeaveRecursiveCall ();
196318 return lo ;
319+ error :
320+ Py_LeaveRecursiveCall ();
321+ Py_XDECREF (litem );
322+ return -1 ;
197323}
198324
199325
0 commit comments