diff -r d384bf58f5f8 Lib/test/test_deque.py --- a/Lib/test/test_deque.py Mon May 16 23:20:32 2016 -0700 +++ b/Lib/test/test_deque.py Tue May 17 22:31:44 2016 +0300 @@ -755,15 +755,17 @@ class TestBasic(unittest.TestCase): @support.cpython_only def test_sizeof(self): BLOCKLEN = 64 - basesize = support.calcobjsize('2P4nlP') - blocksize = struct.calcsize('2P%dP' % BLOCKLEN) + basesize = support.calcvobjsize('P7nP') + P = struct.calcsize('P') + blocksize = P * BLOCKLEN self.assertEqual(object.__sizeof__(deque()), basesize) check = self.check_sizeof - check(deque(), basesize + blocksize) - check(deque('a'), basesize + blocksize) - check(deque('a' * (BLOCKLEN - 1)), basesize + blocksize) - check(deque('a' * BLOCKLEN), basesize + 2 * blocksize) - check(deque('a' * (42 * BLOCKLEN)), basesize + 43 * blocksize) + check(deque(), basesize + 4 * P + blocksize) + check(deque('a'), basesize + 4 * P + blocksize) + check(deque('a' * (BLOCKLEN - 1)), basesize + 4 * P + blocksize) + check(deque('a' * BLOCKLEN), basesize + 4 * P + 2 * blocksize) + check(deque('a' * (24 * BLOCKLEN)), basesize + 27 * P + 25 * blocksize) + check(deque('a' * (25 * BLOCKLEN)), basesize + 40 * P + 26 * blocksize) class TestVariousIteratorArgs(unittest.TestCase): diff -r d384bf58f5f8 Modules/_collectionsmodule.c --- a/Modules/_collectionsmodule.c Mon May 16 23:20:32 2016 -0700 +++ b/Modules/_collectionsmodule.c Tue May 17 22:31:44 2016 +0300 @@ -20,27 +20,20 @@ #define BLOCKLEN 64 #define CENTER ((BLOCKLEN - 1) / 2) +#define MINBLOCKS 4 -/* Data for deque objects is stored in a doubly-linked list of fixed - * length blocks. This assures that appends or pops never move any - * other data elements besides the one being appended or popped. +/* Data for deque objects is stored in an array of fixed length blocks. + * Free space is reserved at both sides of used space. + * + * This assures that appends or pops have constant amortized cost, and + * indexing has constant cost. * - * Another advantage is that it completely avoids use of realloc(), - * resulting in more predictable performance. + * The array of blocks is never empty, so d.leftblock is always less or + * equal d.rightblock, and d.blocks[d.leftblock] is always valid block. + * The list is not circular. * - * Textbook implementations of doubly-linked lists store one datum - * per link, but that gives them a 200% memory overhead (a prev and - * next link for each datum) and it costs one malloc() call per data - * element. By using fixed-length blocks, the link to data ratio is - * significantly improved and there are proportionally fewer calls - * to malloc() and free(). The data blocks of consecutive pointers - * also improve cache locality. - * - * The list of blocks is never empty, so d.leftblock and d.rightblock - * are never equal to NULL. The list is not circular. - * - * A deque d's first element is at d.leftblock[leftindex] - * and its last element is at d.rightblock[rightindex]. + * A deque d's first element is at d.blocks[d.leftblock][leftindex] + * and its last element is at d.blocks[d.rightblock][rightindex]. * * Unlike Python slice indices, these indices are inclusive on both * ends. This makes the algorithms for left and right operations @@ -68,16 +61,14 @@ * Checking for d.len == 0 is the intended way to see whether d is empty. */ -typedef struct BLOCK { - struct BLOCK *leftlink; - PyObject *data[BLOCKLEN]; - struct BLOCK *rightlink; -} block; +typedef PyObject *block; typedef struct { PyObject_VAR_HEAD - block *leftblock; - block *rightblock; + block **blocks; + Py_ssize_t allocated; + Py_ssize_t leftblock; /* 0 <= leftblock <= rightblock */ + Py_ssize_t rightblock; /* leftblock <= rightblock < allocated */ Py_ssize_t leftindex; /* 0 <= leftindex < BLOCKLEN */ Py_ssize_t rightindex; /* 0 <= rightindex < BLOCKLEN */ size_t state; /* incremented whenever the indices move */ @@ -87,27 +78,6 @@ typedef struct { static PyTypeObject deque_type; -/* For debug builds, add error checking to track the endpoints - * in the chain of links. The goal is to make sure that link - * assignments only take place at endpoints so that links already - * in use do not get overwritten. - * - * CHECK_END should happen before each assignment to a block's link field. - * MARK_END should happen whenever a link field becomes a new endpoint. - * This happens when new blocks are added or whenever an existing - * block is freed leaving another existing block as the new endpoint. - */ - -#ifndef NDEBUG -#define MARK_END(link) link = NULL; -#define CHECK_END(link) assert(link == NULL); -#define CHECK_NOT_END(link) assert(link != NULL); -#else -#define MARK_END(link) -#define CHECK_END(link) -#define CHECK_NOT_END(link) -#endif - /* A simple freelisting scheme is used to minimize calls to the memory allocator. It accommodates common use cases where new blocks are being added at about the same rate as old blocks are being freed. @@ -124,7 +94,7 @@ newblock(void) { numfreeblocks--; return freeblocks[numfreeblocks]; } - b = PyMem_Malloc(sizeof(block)); + b = PyMem_Malloc(sizeof(PyObject *) * BLOCKLEN); if (b != NULL) { return b; } @@ -159,13 +129,18 @@ deque_new(PyTypeObject *type, PyObject * Py_DECREF(deque); return NULL; } - MARK_END(b->leftlink); - MARK_END(b->rightlink); - + deque->blocks = (block **)PyMem_Malloc(MINBLOCKS * sizeof(block *)); + if (deque->blocks == NULL) { + freeblock(b); + Py_DECREF(deque); + return NULL; + } + deque->allocated = MINBLOCKS; + deque->blocks[MINBLOCKS/2] = b; assert(BLOCKLEN >= 2); Py_SIZE(deque) = 0; - deque->leftblock = b; - deque->rightblock = b; + deque->leftblock = MINBLOCKS/2; + deque->rightblock = MINBLOCKS/2; deque->leftindex = CENTER + 1; deque->rightindex = CENTER; deque->state = 0; @@ -175,29 +150,47 @@ deque_new(PyTypeObject *type, PyObject * return (PyObject *)deque; } +/* Advance block/index pair */ +#define INCR_POS(b, i) do { \ + if (++(i) == BLOCKLEN) { \ + (b)++; \ + (i) = 0; \ + } \ + } while (0); + +/* Step backwards with block/index pair */ +#define DECR_POS(b, i) do { \ + if ((i)-- == 0) { \ + (b)--; \ + (i) = BLOCKLEN - 1; \ + } \ + } while (0); + +#define UNPACK_INDEX(deque, index, b, i) do { \ + (b) = deque->leftblock + \ + (Py_ssize_t)((size_t) (deque->leftindex + index) / BLOCKLEN); \ + (i) = (Py_ssize_t)((size_t) (deque->leftindex + index) % BLOCKLEN); \ + } while (0); + static PyObject * deque_pop(dequeobject *deque, PyObject *unused) { PyObject *item; - block *prevblock; if (Py_SIZE(deque) == 0) { PyErr_SetString(PyExc_IndexError, "pop from an empty deque"); return NULL; } - item = deque->rightblock->data[deque->rightindex]; + item = deque->blocks[deque->rightblock][deque->rightindex]; deque->rightindex--; Py_SIZE(deque)--; deque->state++; if (deque->rightindex < 0) { if (Py_SIZE(deque)) { - prevblock = deque->rightblock->leftlink; - assert(deque->leftblock != deque->rightblock); - freeblock(deque->rightblock); - CHECK_NOT_END(prevblock); - MARK_END(prevblock->rightlink); - deque->rightblock = prevblock; + assert(deque->leftblock < deque->rightblock); + freeblock(deque->blocks[deque->rightblock]); + deque->rightblock--; deque->rightindex = BLOCKLEN - 1; } else { assert(deque->leftblock == deque->rightblock); @@ -216,26 +209,21 @@ static PyObject * deque_popleft(dequeobject *deque, PyObject *unused) { PyObject *item; - block *prevblock; if (Py_SIZE(deque) == 0) { PyErr_SetString(PyExc_IndexError, "pop from an empty deque"); return NULL; } - assert(deque->leftblock != NULL); - item = deque->leftblock->data[deque->leftindex]; + item = deque->blocks[deque->leftblock][deque->leftindex]; deque->leftindex++; Py_SIZE(deque)--; deque->state++; if (deque->leftindex == BLOCKLEN) { if (Py_SIZE(deque)) { - assert(deque->leftblock != deque->rightblock); - prevblock = deque->leftblock->rightlink; - freeblock(deque->leftblock); - CHECK_NOT_END(prevblock); - MARK_END(prevblock->leftlink); - deque->leftblock = prevblock; + assert(deque->leftblock < deque->rightblock); + freeblock(deque->blocks[deque->leftblock]); + deque->leftblock++; deque->leftindex = 0; } else { assert(deque->leftblock == deque->rightblock); @@ -264,23 +252,96 @@ PyDoc_STRVAR(popleft_doc, "Remove and re #define NEEDS_TRIM(deque, maxlen) ((size_t)(maxlen) < (size_t)(Py_SIZE(deque))) -int +static int +deque_append_block(dequeobject *deque) +{ + Py_ssize_t allocated = deque->allocated; + block *b = newblock(); + if (b == NULL) { + return -1; + } + assert(0 <= deque->leftblock); + assert(deque->leftblock <= deque->rightblock); + assert(deque->rightblock < allocated); + if (deque->rightblock >= allocated - 1) { + if (deque->leftblock > allocated / 2) { + Py_ssize_t leftblock = deque->leftblock; + deque->leftblock = allocated / 4; + memmove(&deque->blocks[deque->leftblock], + &deque->blocks[leftblock], + (deque->rightblock - leftblock + 1) * sizeof(block *)); + deque->rightblock -= leftblock - deque->leftblock; + } + else { + block **blocks; + allocated += Py_MAX(4, allocated / 2); + blocks = PyMem_Realloc(deque->blocks, allocated * sizeof(block *)); + if (blocks == NULL) { + freeblock(b); + return -1; + } + deque->blocks = blocks; + deque->allocated = allocated; + } + } + deque->rightblock++; + deque->blocks[deque->rightblock] = b; + return 0; +} + +static int +deque_appendleft_block(dequeobject *deque) +{ + Py_ssize_t allocated = deque->allocated; + block *b = newblock(); + if (b == NULL) { + return -1; + } + assert(0 <= deque->leftblock); + assert(deque->leftblock <= deque->rightblock); + assert(deque->rightblock < allocated); + if (deque->leftblock <= 0) { + Py_ssize_t leftblock = deque->leftblock; + Py_ssize_t rightblock = deque->rightblock; + if (deque->rightblock < allocated / 2) { + deque->rightblock = allocated - allocated / 4; + deque->leftblock += deque->rightblock - rightblock; + } + else { + block **blocks; + Py_ssize_t delta = Py_MAX(4, allocated / 2); + allocated += delta; + blocks = PyMem_Realloc(deque->blocks, allocated * sizeof(block *)); + if (blocks == NULL) { + freeblock(b); + return -1; + } + deque->blocks = blocks; + deque->leftblock += delta; + deque->rightblock += delta; + deque->allocated = allocated; + } + memmove(&deque->blocks[deque->leftblock], + &deque->blocks[leftblock], + (rightblock - leftblock + 1) * sizeof(block *)); + } + deque->leftblock--; + deque->blocks[deque->leftblock] = b; + return 0; +} + +static int deque_append_internal(dequeobject *deque, PyObject *item, Py_ssize_t maxlen) { if (deque->rightindex == BLOCKLEN - 1) { - block *b = newblock(); - if (b == NULL) + if (deque_append_block(deque) < 0) { return -1; - b->leftlink = deque->rightblock; - CHECK_END(deque->rightblock->rightlink); - deque->rightblock->rightlink = b; - deque->rightblock = b; - MARK_END(b->rightlink); + } deque->rightindex = -1; } Py_SIZE(deque)++; deque->rightindex++; - deque->rightblock->data[deque->rightindex] = item; + deque->blocks[deque->rightblock][deque->rightindex] = item; if (NEEDS_TRIM(deque, maxlen)) { PyObject *olditem = deque_popleft(deque, NULL); Py_DECREF(olditem); @@ -301,23 +362,18 @@ deque_append(dequeobject *deque, PyObjec PyDoc_STRVAR(append_doc, "Add an element to the right side of the deque."); -int +static int deque_appendleft_internal(dequeobject *deque, PyObject *item, Py_ssize_t maxlen) { if (deque->leftindex == 0) { - block *b = newblock(); - if (b == NULL) + if (deque_appendleft_block(deque) < 0) { return -1; - b->rightlink = deque->leftblock; - CHECK_END(deque->leftblock->leftlink); - deque->leftblock->leftlink = b; - deque->leftblock = b; - MARK_END(b->leftlink); + } deque->leftindex = BLOCKLEN; } Py_SIZE(deque)++; deque->leftindex--; - deque->leftblock->data[deque->leftindex] = item; + deque->blocks[deque->leftblock][deque->leftindex] = item; if (NEEDS_TRIM(deque, deque->maxlen)) { PyObject *olditem = deque_pop(deque, NULL); Py_DECREF(olditem); @@ -489,7 +545,7 @@ deque_copy(PyObject *deque) new_deque->maxlen = old_deque->maxlen; /* Fast path for the deque_repeat() common case where len(deque) == 1 */ if (Py_SIZE(deque) == 1) { - PyObject *item = old_deque->leftblock->data[old_deque->leftindex]; + PyObject *item = old_deque->blocks[old_deque->leftblock][old_deque->leftindex]; rv = deque_append(new_deque, item); } else { rv = deque_extend(new_deque, deque); @@ -542,9 +598,13 @@ static void deque_clear(dequeobject *deque) { block *b; - block *prevblock; - block *leftblock; + block **blocks; + block **new_blocks; + Py_ssize_t leftblock; Py_ssize_t leftindex; +#ifndef NDEBUG + Py_ssize_t rightblock; +#endif Py_ssize_t n, m; PyObject *item; PyObject **itemptr, **limit; @@ -572,16 +632,27 @@ deque_clear(dequeobject *deque) } /* Remember the old size, leftblock, and leftindex */ + blocks = deque->blocks; n = Py_SIZE(deque); leftblock = deque->leftblock; leftindex = deque->leftindex; +#ifndef NDEBUG + rightblock = deque->rightblock; +#endif /* Set the deque to be empty using the newly allocated block */ - MARK_END(b->leftlink); - MARK_END(b->rightlink); + new_blocks = (block **)PyMem_Malloc(MINBLOCKS * sizeof(block *)); + if (new_blocks == NULL) { + PyMem_Free(b); + goto alternate_method; + } + deque->blocks = new_blocks; + deque->allocated = MINBLOCKS; + deque->blocks[MINBLOCKS/2] = b; + assert(BLOCKLEN >= 2); Py_SIZE(deque) = 0; - deque->leftblock = b; - deque->rightblock = b; + deque->leftblock = MINBLOCKS/2; + deque->rightblock = MINBLOCKS/2; deque->leftindex = CENTER + 1; deque->rightindex = CENTER; deque->state++; @@ -590,27 +661,27 @@ deque_clear(dequeobject *deque) the empty deque and we can use them to decref the pointers. */ m = (BLOCKLEN - leftindex > n) ? n : BLOCKLEN - leftindex; - itemptr = &leftblock->data[leftindex]; + itemptr = &blocks[leftblock][leftindex]; limit = itemptr + m; n -= m; while (1) { if (itemptr == limit) { if (n == 0) break; - CHECK_NOT_END(leftblock->rightlink); - prevblock = leftblock; - leftblock = leftblock->rightlink; + assert(leftblock < rightblock); + freeblock(blocks[leftblock]); + leftblock++; m = (n > BLOCKLEN) ? BLOCKLEN : n; - itemptr = leftblock->data; + itemptr = blocks[leftblock]; limit = itemptr + m; n -= m; - freeblock(prevblock); } item = *(itemptr++); Py_DECREF(item); } - CHECK_END(leftblock->rightlink); - freeblock(leftblock); + assert(leftblock == rightblock); + freeblock(blocks[leftblock]); + PyMem_Free(blocks); return; alternate_method: @@ -651,34 +722,30 @@ deque_inplace_repeat(dequeobject *deque, if (size == 1) { /* common case, repeating a single element */ - PyObject *item = deque->leftblock->data[deque->leftindex]; + PyObject *item = deque->blocks[deque->leftblock][deque->leftindex]; if (deque->maxlen >= 0 && n > deque->maxlen) n = deque->maxlen; deque->state++; for (i = 0 ; i < n-1 ; ) { + PyObject **itemptr; if (deque->rightindex == BLOCKLEN - 1) { - block *b = newblock(); - if (b == NULL) { + if (deque_append_block(deque) < 0) { Py_SIZE(deque) += i; return NULL; } - b->leftlink = deque->rightblock; - CHECK_END(deque->rightblock->rightlink); - deque->rightblock->rightlink = b; - deque->rightblock = b; - MARK_END(b->rightlink); deque->rightindex = -1; } m = n - 1 - i; if (m > BLOCKLEN - 1 - deque->rightindex) m = BLOCKLEN - 1 - deque->rightindex; i += m; + itemptr = &deque->blocks[deque->rightblock][deque->rightindex]; + deque->rightindex += m; while (m--) { - deque->rightindex++; Py_INCREF(item); - deque->rightblock->data[deque->rightindex] = item; + *++itemptr = item; } } Py_SIZE(deque) += i; @@ -749,9 +816,6 @@ destination block. If a block is left-o static int _deque_rotate(dequeobject *deque, Py_ssize_t n) { - block *b = NULL; - block *leftblock = deque->leftblock; - block *rightblock = deque->rightblock; Py_ssize_t leftindex = deque->leftindex; Py_ssize_t rightindex = deque->rightindex; Py_ssize_t len=Py_SIZE(deque), halflen=len>>1; @@ -772,18 +836,10 @@ static int deque->state++; while (n > 0) { if (leftindex == 0) { - if (b == NULL) { - b = newblock(); - if (b == NULL) - goto done; + if (deque_appendleft_block(deque) < 0) { + goto done; } - b->rightlink = leftblock; - CHECK_END(leftblock->leftlink); - leftblock->leftlink = b; - leftblock = b; - MARK_END(b->leftlink); leftindex = BLOCKLEN; - b = NULL; } assert(leftindex > 0); { @@ -797,37 +853,26 @@ static int assert (m > 0 && m <= len); rightindex -= m; leftindex -= m; - src = &rightblock->data[rightindex + 1]; - dest = &leftblock->data[leftindex]; + src = &deque->blocks[deque->rightblock][rightindex + 1]; + dest = &deque->blocks[deque->leftblock][leftindex]; n -= m; do { *(dest++) = *(src++); } while (--m); } if (rightindex < 0) { - assert(leftblock != rightblock); - assert(b == NULL); - b = rightblock; - CHECK_NOT_END(rightblock->leftlink); - rightblock = rightblock->leftlink; - MARK_END(rightblock->rightlink); + assert(deque->leftblock < deque->rightblock); + freeblock(deque->blocks[deque->rightblock]); + deque->rightblock--; rightindex = BLOCKLEN - 1; } } while (n < 0) { if (rightindex == BLOCKLEN - 1) { - if (b == NULL) { - b = newblock(); - if (b == NULL) - goto done; + if (deque_append_block(deque) < 0) { + goto done; } - b->leftlink = rightblock; - CHECK_END(rightblock->rightlink); - rightblock->rightlink = b; - rightblock = b; - MARK_END(b->rightlink); rightindex = -1; - b = NULL; } assert (rightindex < BLOCKLEN - 1); { @@ -839,8 +884,8 @@ static int if (m > BLOCKLEN - 1 - rightindex) m = BLOCKLEN - 1 - rightindex; assert (m > 0 && m <= len); - src = &leftblock->data[leftindex]; - dest = &rightblock->data[rightindex + 1]; + src = &deque->blocks[deque->leftblock][leftindex]; + dest = &deque->blocks[deque->rightblock][rightindex + 1]; leftindex += m; rightindex += m; n += m; @@ -849,21 +894,14 @@ static int } while (--m); } if (leftindex == BLOCKLEN) { - assert(leftblock != rightblock); - assert(b == NULL); - b = leftblock; - CHECK_NOT_END(leftblock->rightlink); - leftblock = leftblock->rightlink; - MARK_END(leftblock->leftlink); + assert(deque->leftblock < deque->rightblock); + freeblock(deque->blocks[deque->leftblock]); + deque->leftblock++; leftindex = 0; } } rv = 0; done: - if (b != NULL) - freeblock(b); - deque->leftblock = leftblock; - deque->rightblock = rightblock; deque->leftindex = leftindex; deque->rightindex = rightindex; @@ -888,8 +926,9 @@ PyDoc_STRVAR(rotate_doc, static PyObject * deque_reverse(dequeobject *deque, PyObject *unused) { - block *leftblock = deque->leftblock; - block *rightblock = deque->rightblock; + block **blocks = deque->blocks; + Py_ssize_t leftblock = deque->leftblock; + Py_ssize_t rightblock = deque->rightblock; Py_ssize_t leftindex = deque->leftindex; Py_ssize_t rightindex = deque->rightindex; Py_ssize_t n = Py_SIZE(deque) >> 1; @@ -898,28 +937,15 @@ deque_reverse(dequeobject *deque, PyObje n++; while (--n) { /* Validate that pointers haven't met in the middle */ - assert(leftblock != rightblock || leftindex < rightindex); - CHECK_NOT_END(leftblock); - CHECK_NOT_END(rightblock); + assert(leftblock < rightblock || leftindex < rightindex); /* Swap */ - tmp = leftblock->data[leftindex]; - leftblock->data[leftindex] = rightblock->data[rightindex]; - rightblock->data[rightindex] = tmp; + tmp = blocks[leftblock][leftindex]; + blocks[leftblock][leftindex] = blocks[rightblock][rightindex]; + blocks[rightblock][rightindex] = tmp; - /* Advance left block/index pair */ - leftindex++; - if (leftindex == BLOCKLEN) { - leftblock = leftblock->rightlink; - leftindex = 0; - } - - /* Step backwards with the right block/index pair */ - rightindex--; - if (rightindex < 0) { - rightblock = rightblock->leftlink; - rightindex = BLOCKLEN - 1; - } + INCR_POS(leftblock, leftindex); + DECR_POS(rightblock, rightindex); } Py_RETURN_NONE; } @@ -930,7 +956,7 @@ PyDoc_STRVAR(reverse_doc, static PyObject * deque_count(dequeobject *deque, PyObject *v) { - block *b = deque->leftblock; + Py_ssize_t b = deque->leftblock; Py_ssize_t index = deque->leftindex; Py_ssize_t n = Py_SIZE(deque); Py_ssize_t count = 0; @@ -940,8 +966,8 @@ deque_count(dequeobject *deque, PyObject n++; while (--n) { - CHECK_NOT_END(b); - item = b->data[index]; + assert(b <= deque->rightblock); + item = deque->blocks[b][index]; cmp = PyObject_RichCompareBool(item, v, Py_EQ); if (cmp < 0) return NULL; @@ -953,12 +979,7 @@ deque_count(dequeobject *deque, PyObject return NULL; } - /* Advance left block/index pair */ - index++; - if (index == BLOCKLEN) { - b = b->rightlink; - index = 0; - } + INCR_POS(b, index); } return PyLong_FromSsize_t(count); } @@ -969,7 +990,7 @@ PyDoc_STRVAR(count_doc, static int deque_contains(dequeobject *deque, PyObject *v) { - block *b = deque->leftblock; + Py_ssize_t b = deque->leftblock; Py_ssize_t index = deque->leftindex; Py_ssize_t n = Py_SIZE(deque); size_t start_state = deque->state; @@ -978,8 +999,8 @@ deque_contains(dequeobject *deque, PyObj n++; while (--n) { - CHECK_NOT_END(b); - item = b->data[index]; + assert(b <= deque->rightblock); + item = deque->blocks[b][index]; cmp = PyObject_RichCompareBool(item, v, Py_EQ); if (cmp) { return cmp; @@ -989,11 +1010,7 @@ deque_contains(dequeobject *deque, PyObj "deque mutated during iteration"); return -1; } - index++; - if (index == BLOCKLEN) { - b = b->rightlink; - index = 0; - } + INCR_POS(b, index); } return 0; } @@ -1007,10 +1024,10 @@ deque_len(dequeobject *deque) static PyObject * deque_index(dequeobject *deque, PyObject *args) { - Py_ssize_t i, n, start=0, stop=Py_SIZE(deque); + Py_ssize_t n, start=0, stop=Py_SIZE(deque); PyObject *v, *item; - block *b = deque->leftblock; - Py_ssize_t index = deque->leftindex; + Py_ssize_t b; + Py_ssize_t index; size_t start_state = deque->state; int cmp; @@ -1028,25 +1045,18 @@ deque_index(dequeobject *deque, PyObject if (stop < 0) stop = 0; } - if (stop > Py_SIZE(deque)) + else if (stop > Py_SIZE(deque)) stop = Py_SIZE(deque); - if (start > stop) - start = stop; + if (start >= stop) { + goto notfound; + } assert(0 <= start && start <= stop && stop <= Py_SIZE(deque)); - /* XXX Replace this loop with faster code from deque_item() */ - for (i=0 ; irightlink; - index = 0; - } - } - - n = stop - i + 1; + UNPACK_INDEX(deque, start, b, index); + n = stop - start + 1; while (--n) { - CHECK_NOT_END(b); - item = b->data[index]; + assert(b <= deque->rightblock); + item = deque->blocks[b][index]; cmp = PyObject_RichCompareBool(item, v, Py_EQ); if (cmp > 0) return PyLong_FromSsize_t(stop - n); @@ -1057,12 +1067,10 @@ deque_index(dequeobject *deque, PyObject "deque mutated during iteration"); return NULL; } - index++; - if (index == BLOCKLEN) { - b = b->rightlink; - index = 0; - } + INCR_POS(b, index); } + +notfound: PyErr_Format(PyExc_ValueError, "%R is not in deque", v); return NULL; } @@ -1120,7 +1128,7 @@ deque_remove(dequeobject *deque, PyObjec Py_ssize_t i, n=Py_SIZE(deque); for (i=0 ; ileftblock->data[deque->leftindex]; + PyObject *item = deque->blocks[deque->leftblock][deque->leftindex]; int cmp = PyObject_RichCompareBool(item, value, Py_EQ); if (Py_SIZE(deque) != n) { @@ -1160,41 +1168,16 @@ valid_index(Py_ssize_t i, Py_ssize_t lim static PyObject * deque_item(dequeobject *deque, Py_ssize_t i) { - block *b; PyObject *item; - Py_ssize_t n, index=i; + Py_ssize_t n, index; if (!valid_index(i, Py_SIZE(deque))) { PyErr_SetString(PyExc_IndexError, "deque index out of range"); return NULL; } - if (i == 0) { - i = deque->leftindex; - b = deque->leftblock; - } else if (i == Py_SIZE(deque) - 1) { - i = deque->rightindex; - b = deque->rightblock; - } else { - i += deque->leftindex; - n = (Py_ssize_t)((size_t) i / BLOCKLEN); - i = (Py_ssize_t)((size_t) i % BLOCKLEN); - if (index < (Py_SIZE(deque) >> 1)) { - b = deque->leftblock; - n++; - while (--n) - b = b->rightlink; - } else { - n = (Py_ssize_t)( - ((size_t)(deque->leftindex + Py_SIZE(deque) - 1)) - / BLOCKLEN - n); - b = deque->rightblock; - n++; - while (--n) - b = b->leftlink; - } - } - item = b->data[i]; + UNPACK_INDEX(deque, i, n, index); + item = deque->blocks[n][index]; Py_INCREF(item); return item; } @@ -1218,9 +1201,8 @@ deque_del_item(dequeobject *deque, Py_ss static int deque_ass_item(dequeobject *deque, Py_ssize_t i, PyObject *v) { - PyObject *old_value; - block *b; - Py_ssize_t n, len=Py_SIZE(deque), halflen=(len+1)>>1, index=i; + PyObject **p; + Py_ssize_t n, index, len=Py_SIZE(deque); if (!valid_index(i, len)) { PyErr_SetString(PyExc_IndexError, "deque index out of range"); @@ -1229,27 +1211,10 @@ deque_ass_item(dequeobject *deque, Py_ss if (v == NULL) return deque_del_item(deque, i); - i += deque->leftindex; - n = (Py_ssize_t)((size_t) i / BLOCKLEN); - i = (Py_ssize_t)((size_t) i % BLOCKLEN); - if (index <= halflen) { - b = deque->leftblock; - n++; - while (--n) - b = b->rightlink; - } else { - n = (Py_ssize_t)( - ((size_t)(deque->leftindex + Py_SIZE(deque) - 1)) - / BLOCKLEN - n); - b = deque->rightblock; - n++; - while (--n) - b = b->leftlink; - } + UNPACK_INDEX(deque, i, n, index); + p = &deque->blocks[n][index]; Py_INCREF(v); - old_value = b->data[i]; - b->data[i] = v; - Py_DECREF(old_value); + Py_SETREF(*p, v); return 0; } @@ -1259,35 +1224,34 @@ deque_dealloc(dequeobject *deque) PyObject_GC_UnTrack(deque); if (deque->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) deque); - if (deque->leftblock != NULL) { + if (deque->blocks != NULL) { deque_clear(deque); - assert(deque->leftblock != NULL); - freeblock(deque->leftblock); + freeblock(deque->blocks[deque->leftblock]); + PyMem_Free(deque->blocks); + deque->blocks = NULL; } - deque->leftblock = NULL; - deque->rightblock = NULL; Py_TYPE(deque)->tp_free(deque); } static int deque_traverse(dequeobject *deque, visitproc visit, void *arg) { - block *b; + block **pb, **rightb = &deque->blocks[deque->rightblock]; PyObject *item; Py_ssize_t index; Py_ssize_t indexlo = deque->leftindex; Py_ssize_t indexhigh; - for (b = deque->leftblock; b != deque->rightblock; b = b->rightlink) { + for (pb = &deque->blocks[deque->leftblock]; pb != rightb; pb++) { for (index = indexlo; index < BLOCKLEN ; index++) { - item = b->data[index]; + item = (*pb)[index]; Py_VISIT(item); } indexlo = 0; } indexhigh = deque->rightindex; for (index = indexlo; index <= indexhigh; index++) { - item = b->data[index]; + item = (*pb)[index]; Py_VISIT(item); } return 0; @@ -1475,10 +1439,9 @@ deque_sizeof(dequeobject *deque, void *u Py_ssize_t blocks; res = _PyObject_SIZE(Py_TYPE(deque)); - blocks = (size_t)(deque->leftindex + Py_SIZE(deque) + BLOCKLEN - 1) / BLOCKLEN; - assert(deque->leftindex + Py_SIZE(deque) - 1 == - (blocks - 1) * BLOCKLEN + deque->rightindex); - res += blocks * sizeof(block); + blocks = deque->rightblock - deque->leftblock + 1; + res += blocks * (sizeof(PyObject *) * BLOCKLEN); + res += deque->allocated * sizeof(block *); return PyLong_FromSsize_t(res); } @@ -1633,7 +1596,6 @@ static PyTypeObject deque_type = { typedef struct { PyObject_HEAD - block *b; Py_ssize_t index; dequeobject *deque; size_t state; /* state when the iterator is created */ @@ -1650,8 +1612,7 @@ deque_iter(dequeobject *deque) it = PyObject_GC_New(dequeiterobject, &dequeiter_type); if (it == NULL) return NULL; - it->b = deque->leftblock; - it->index = deque->leftindex; + it->index = 0; Py_INCREF(deque); it->deque = deque; it->state = deque->state; @@ -1677,9 +1638,11 @@ dequeiter_dealloc(dequeiterobject *dio) static PyObject * dequeiter_next(dequeiterobject *it) { + dequeobject *deque = it->deque; PyObject *item; + Py_ssize_t i, n; - if (it->deque->state != it->state) { + if (deque->state != it->state) { it->counter = 0; PyErr_SetString(PyExc_RuntimeError, "deque mutated during iteration"); @@ -1687,17 +1650,12 @@ dequeiter_next(dequeiterobject *it) } if (it->counter == 0) return NULL; - assert (!(it->b == it->deque->rightblock && - it->index > it->deque->rightindex)); + assert (it->index < Py_SIZE(deque)); - item = it->b->data[it->index]; + UNPACK_INDEX(deque, it->index, n, i); + item = deque->blocks[n][i]; it->index++; it->counter--; - if (it->index == BLOCKLEN && it->counter > 0) { - CHECK_NOT_END(it->b->rightlink); - it->b = it->b->rightlink; - it->index = 0; - } Py_INCREF(item); return item; } @@ -1806,8 +1764,7 @@ deque_reviter(dequeobject *deque) it = PyObject_GC_New(dequeiterobject, &dequereviter_type); if (it == NULL) return NULL; - it->b = deque->rightblock; - it->index = deque->rightindex; + it->index = Py_SIZE(deque) - 1; Py_INCREF(deque); it->deque = deque; it->state = deque->state; @@ -1819,27 +1776,24 @@ deque_reviter(dequeobject *deque) static PyObject * dequereviter_next(dequeiterobject *it) { + dequeobject *deque = it->deque; PyObject *item; + Py_ssize_t i, n; if (it->counter == 0) return NULL; - if (it->deque->state != it->state) { + if (deque->state != it->state) { it->counter = 0; PyErr_SetString(PyExc_RuntimeError, "deque mutated during iteration"); return NULL; } - assert (!(it->b == it->deque->leftblock && - it->index < it->deque->leftindex)); + assert (it->index >= 0); - item = it->b->data[it->index]; + UNPACK_INDEX(deque, it->index, n, i); + item = deque->blocks[n][i]; it->index--; it->counter--; - if (it->index < 0 && it->counter > 0) { - CHECK_NOT_END(it->b->leftlink); - it->b = it->b->leftlink; - it->index = BLOCKLEN - 1; - } Py_INCREF(item); return item; }