Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 82df925

Browse files
committed
Use macros for marking and checking endpoints in the doubly-linked list of blocks.
* Add comment explaining the endpoint checks * Only do the checks in a debug build * Simplify newblock() to only require a length argument and leave the link updates to the calling code. * Also add comment for the freelisting logic.
1 parent 0cb2aaf commit 82df925

1 file changed

Lines changed: 81 additions & 47 deletions

File tree

Modules/_collectionsmodule.c

Lines changed: 81 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,14 @@
1818
#define CENTER ((BLOCKLEN - 1) / 2)
1919

2020
/* A `dequeobject` is composed of a doubly-linked list of `block` nodes.
21-
* This list is not circular (the leftmost block has leftlink==NULL,
22-
* and the rightmost block has rightlink==NULL). A deque d's first
23-
* element is at d.leftblock[leftindex] and its last element is at
24-
* d.rightblock[rightindex]; note that, unlike as for Python slice
25-
* indices, these indices are inclusive on both ends. By being inclusive
26-
* on both ends, algorithms for left and right operations become
27-
* symmetrical which simplifies the design.
28-
*
2921
* The list of blocks is never empty, so d.leftblock and d.rightblock
30-
* are never equal to NULL.
22+
* are never equal to NULL. The list is not circular.
23+
*
24+
* A deque d's first element is at d.leftblock[leftindex]
25+
* and its last element is at d.rightblock[rightindex].
26+
* Unlike Python slice indices, these indices are inclusive
27+
* on both ends. This makes the algorithms for left and
28+
* right operations more symmetrical and simplifies the design.
3129
*
3230
* The indices, d.leftindex and d.rightindex are always in the range
3331
* 0 <= index < BLOCKLEN.
@@ -52,12 +50,38 @@ typedef struct BLOCK {
5250
struct BLOCK *rightlink;
5351
} block;
5452

53+
/* For debug builds, add error checking to track the endpoints
54+
* in the chain of links. The goal is to make sure that link
55+
* assignments only take place at endpoints so that links already
56+
* in use do not get overwritten.
57+
*
58+
* CHECK_END should happen before each assignment to a block's link field.
59+
* MARK_END should happen whenever a link field becomes a new endpoint.
60+
* This happens when new blocks are added or whenever an existing
61+
* block is freed leaving another existing block as the new endpoint.
62+
*/
63+
64+
#if Py_DEBUG
65+
#define MARK_END(link) link = NULL;
66+
#define CHECK_END(link) assert(link == NULL);
67+
#define CHECK_NOT_END(link) assert(link != NULL);
68+
#else
69+
#define MARK_END(link)
70+
#define CHECK_END(link)
71+
#define CHECK_NOT_END(link)
72+
#endif
73+
74+
/* A simple freelisting scheme is used to minimize calls to the memory
75+
allocator. It accomodates common use cases where new blocks are being
76+
added at about the same rate as old blocks are being freed.
77+
*/
78+
5579
#define MAXFREEBLOCKS 10
5680
static Py_ssize_t numfreeblocks = 0;
5781
static block *freeblocks[MAXFREEBLOCKS];
5882

5983
static block *
60-
newblock(block *leftlink, block *rightlink, Py_ssize_t len) {
84+
newblock(Py_ssize_t len) {
6185
block *b;
6286
/* To prevent len from overflowing PY_SSIZE_T_MAX, we refuse to
6387
* allocate new blocks if the current len is nearing overflow. */
@@ -68,17 +92,14 @@ newblock(block *leftlink, block *rightlink, Py_ssize_t len) {
6892
}
6993
if (numfreeblocks) {
7094
numfreeblocks--;
71-
b = freeblocks[numfreeblocks];
72-
} else {
73-
b = PyMem_Malloc(sizeof(block));
74-
if (b == NULL) {
75-
PyErr_NoMemory();
76-
return NULL;
77-
}
95+
return freeblocks[numfreeblocks];
96+
}
97+
b = PyMem_Malloc(sizeof(block));
98+
if (b != NULL) {
99+
return b;
78100
}
79-
b->leftlink = leftlink;
80-
b->rightlink = rightlink;
81-
return b;
101+
PyErr_NoMemory();
102+
return NULL;
82103
}
83104

84105
static void
@@ -132,11 +153,13 @@ deque_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
132153
if (deque == NULL)
133154
return NULL;
134155

135-
b = newblock(NULL, NULL, 0);
156+
b = newblock(0);
136157
if (b == NULL) {
137158
Py_DECREF(deque);
138159
return NULL;
139160
}
161+
MARK_END(b->leftlink);
162+
MARK_END(b->rightlink);
140163

141164
assert(BLOCKLEN >= 2);
142165
deque->leftblock = b;
@@ -177,7 +200,8 @@ deque_pop(dequeobject *deque, PyObject *unused)
177200
prevblock = deque->rightblock->leftlink;
178201
assert(deque->leftblock != deque->rightblock);
179202
freeblock(deque->rightblock);
180-
prevblock->rightlink = NULL;
203+
CHECK_NOT_END(prevblock);
204+
MARK_END(prevblock->rightlink);
181205
deque->rightblock = prevblock;
182206
deque->rightindex = BLOCKLEN - 1;
183207
}
@@ -214,8 +238,8 @@ deque_popleft(dequeobject *deque, PyObject *unused)
214238
assert(deque->leftblock != deque->rightblock);
215239
prevblock = deque->leftblock->rightlink;
216240
freeblock(deque->leftblock);
217-
assert(prevblock != NULL);
218-
prevblock->leftlink = NULL;
241+
CHECK_NOT_END(prevblock);
242+
MARK_END(prevblock->leftlink);
219243
deque->leftblock = prevblock;
220244
deque->leftindex = 0;
221245
}
@@ -230,12 +254,14 @@ deque_append(dequeobject *deque, PyObject *item)
230254
{
231255
deque->state++;
232256
if (deque->rightindex == BLOCKLEN-1) {
233-
block *b = newblock(deque->rightblock, NULL, Py_SIZE(deque));
257+
block *b = newblock(Py_SIZE(deque));
234258
if (b == NULL)
235259
return NULL;
236-
assert(deque->rightblock->rightlink == NULL);
260+
b->leftlink = deque->rightblock;
261+
CHECK_END(deque->rightblock->rightlink);
237262
deque->rightblock->rightlink = b;
238263
deque->rightblock = b;
264+
MARK_END(b->rightlink);
239265
deque->rightindex = -1;
240266
}
241267
Py_INCREF(item);
@@ -253,12 +279,14 @@ deque_appendleft(dequeobject *deque, PyObject *item)
253279
{
254280
deque->state++;
255281
if (deque->leftindex == 0) {
256-
block *b = newblock(NULL, deque->leftblock, Py_SIZE(deque));
282+
block *b = newblock(Py_SIZE(deque));
257283
if (b == NULL)
258284
return NULL;
259-
assert(deque->leftblock->leftlink == NULL);
285+
b->rightlink = deque->leftblock;
286+
CHECK_END(deque->leftblock->leftlink);
260287
deque->leftblock->leftlink = b;
261288
deque->leftblock = b;
289+
MARK_END(b->leftlink);
262290
deque->leftindex = BLOCKLEN;
263291
}
264292
Py_INCREF(item);
@@ -314,16 +342,17 @@ deque_extend(dequeobject *deque, PyObject *iterable)
314342
while ((item = PyIter_Next(it)) != NULL) {
315343
deque->state++;
316344
if (deque->rightindex == BLOCKLEN-1) {
317-
block *b = newblock(deque->rightblock, NULL,
318-
Py_SIZE(deque));
345+
block *b = newblock(Py_SIZE(deque));
319346
if (b == NULL) {
320347
Py_DECREF(item);
321348
Py_DECREF(it);
322349
return NULL;
323350
}
324-
assert(deque->rightblock->rightlink == NULL);
351+
b->leftlink = deque->rightblock;
352+
CHECK_END(deque->rightblock->rightlink);
325353
deque->rightblock->rightlink = b;
326354
deque->rightblock = b;
355+
MARK_END(b->rightlink);
327356
deque->rightindex = -1;
328357
}
329358
Py_SIZE(deque)++;
@@ -366,16 +395,17 @@ deque_extendleft(dequeobject *deque, PyObject *iterable)
366395
while ((item = PyIter_Next(it)) != NULL) {
367396
deque->state++;
368397
if (deque->leftindex == 0) {
369-
block *b = newblock(NULL, deque->leftblock,
370-
Py_SIZE(deque));
398+
block *b = newblock(Py_SIZE(deque));
371399
if (b == NULL) {
372400
Py_DECREF(item);
373401
Py_DECREF(it);
374402
return NULL;
375403
}
376-
assert(deque->leftblock->leftlink == NULL);
404+
b->rightlink = deque->leftblock;
405+
CHECK_END(deque->leftblock->leftlink);
377406
deque->leftblock->leftlink = b;
378407
deque->leftblock = b;
408+
MARK_END(b->leftlink);
379409
deque->leftindex = BLOCKLEN;
380410
}
381411
Py_SIZE(deque)++;
@@ -430,14 +460,16 @@ _deque_rotate(dequeobject *deque, Py_ssize_t n)
430460
deque->state++;
431461
while (n > 0) {
432462
if (leftindex == 0) {
433-
block *b = newblock(NULL, leftblock, len);
463+
block *b = newblock(len);
434464
if (b == NULL) {
435465
rv = -1;
436466
goto done;
437467
}
438-
assert(leftblock->leftlink == NULL);
468+
b->rightlink = leftblock;
469+
CHECK_END(leftblock->leftlink);
439470
leftblock->leftlink = b;
440471
leftblock = b;
472+
MARK_END(b->leftlink);
441473
leftindex = BLOCKLEN;
442474
}
443475
assert(leftindex > 0);
@@ -462,24 +494,26 @@ _deque_rotate(dequeobject *deque, Py_ssize_t n)
462494

463495
if (rightindex == -1) {
464496
block *prevblock = rightblock->leftlink;
465-
assert(rightblock != NULL);
466497
assert(leftblock != rightblock);
467498
freeblock(rightblock);
468-
prevblock->rightlink = NULL;
499+
CHECK_NOT_END(prevblock);
500+
MARK_END(prevblock->rightlink);
469501
rightblock = prevblock;
470502
rightindex = BLOCKLEN - 1;
471503
}
472504
}
473505
while (n < 0) {
474506
if (rightindex == BLOCKLEN - 1) {
475-
block *b = newblock(rightblock, NULL, len);
507+
block *b = newblock(len);
476508
if (b == NULL) {
477509
rv = -1;
478510
goto done;
479511
}
480-
assert(rightblock->rightlink == NULL);
512+
b->leftlink = rightblock;
513+
CHECK_END(rightblock->rightlink);
481514
rightblock->rightlink = b;
482515
rightblock = b;
516+
MARK_END(b->rightlink);
483517
rightindex = -1;
484518
}
485519
assert (rightindex < BLOCKLEN - 1);
@@ -506,8 +540,8 @@ _deque_rotate(dequeobject *deque, Py_ssize_t n)
506540
block *nextblock = leftblock->rightlink;
507541
assert(leftblock != rightblock);
508542
freeblock(leftblock);
509-
assert(nextblock != NULL);
510-
nextblock->leftlink = NULL;
543+
CHECK_NOT_END(nextblock);
544+
MARK_END(nextblock->leftlink);
511545
leftblock = nextblock;
512546
leftindex = 0;
513547
}
@@ -550,8 +584,8 @@ deque_reverse(dequeobject *deque, PyObject *unused)
550584
for (i=0 ; i<n ; i++) {
551585
/* Validate that pointers haven't met in the middle */
552586
assert(leftblock != rightblock || leftindex < rightindex);
553-
assert(leftblock != NULL);
554-
assert(rightblock != NULL);
587+
CHECK_NOT_END(leftblock);
588+
CHECK_NOT_END(rightblock);
555589

556590
/* Swap */
557591
tmp = leftblock->data[leftindex];
@@ -591,7 +625,7 @@ deque_count(dequeobject *deque, PyObject *v)
591625
int cmp;
592626

593627
for (i=0 ; i<n ; i++) {
594-
assert(b != NULL);
628+
CHECK_NOT_END(b);
595629
item = b->data[index];
596630
cmp = PyObject_RichCompareBool(item, v, Py_EQ);
597631
if (cmp > 0)
@@ -1199,7 +1233,7 @@ dequeiter_next(dequeiterobject *it)
11991233
it->index++;
12001234
it->counter--;
12011235
if (it->index == BLOCKLEN && it->counter > 0) {
1202-
assert (it->b->rightlink != NULL);
1236+
CHECK_NOT_END(it->b->rightlink);
12031237
it->b = it->b->rightlink;
12041238
it->index = 0;
12051239
}
@@ -1341,7 +1375,7 @@ dequereviter_next(dequeiterobject *it)
13411375
it->index--;
13421376
it->counter--;
13431377
if (it->index == -1 && it->counter > 0) {
1344-
assert (it->b->leftlink != NULL);
1378+
CHECK_NOT_END(it->b->leftlink);
13451379
it->b = it->b->leftlink;
13461380
it->index = BLOCKLEN - 1;
13471381
}

0 commit comments

Comments
 (0)