Skip to content

Commit 7dff38f

Browse files
LordFlashmeowdpgeorge
authored andcommitted
py/objdeque: Expand implementation to be doubly-ended and support iter.
Add `pop()`, `appendleft()`, and `extend()` methods, support iteration and indexing, and initializing from an existing sequence. Iteration and indexing (subscription) have independent configuration flags to enable them. They are enabled by default at the same level that collections.deque is enabled (the extra features level). Also add tests for checking new behavior. Signed-off-by: Damien George <damien@micropython.org>
1 parent cd8eea2 commit 7dff38f

File tree

7 files changed

+324
-28
lines changed

7 files changed

+324
-28
lines changed

ports/windows/mpconfigport.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@
117117
#define MICROPY_PY_SYS_STDFILES (1)
118118
#define MICROPY_PY_SYS_EXC_INFO (1)
119119
#define MICROPY_PY_COLLECTIONS_DEQUE (1)
120+
#define MICROPY_PY_COLLECTIONS_DEQUE_ITER (1)
121+
#define MICROPY_PY_COLLECTIONS_DEQUE_SUBSCR (1)
120122
#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1)
121123
#ifndef MICROPY_PY_MATH_SPECIAL_FUNCTIONS
122124
#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1)

py/mpconfig.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,6 +1303,16 @@ typedef double mp_float_t;
13031303
#define MICROPY_PY_COLLECTIONS_DEQUE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)
13041304
#endif
13051305

1306+
// Whether "collections.deque" supports iteration
1307+
#ifndef MICROPY_PY_COLLECTIONS_DEQUE_ITER
1308+
#define MICROPY_PY_COLLECTIONS_DEQUE_ITER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)
1309+
#endif
1310+
1311+
// Whether "collections.deque" supports subscription
1312+
#ifndef MICROPY_PY_COLLECTIONS_DEQUE_SUBSCR
1313+
#define MICROPY_PY_COLLECTIONS_DEQUE_SUBSCR (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)
1314+
#endif
1315+
13061316
// Whether to provide "collections.OrderedDict" type
13071317
#ifndef MICROPY_PY_COLLECTIONS_ORDEREDDICT
13081318
#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)

py/objdeque.c

Lines changed: 165 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,11 @@
2525
*/
2626

2727
#include <unistd.h> // for ssize_t
28-
#include <string.h>
29-
30-
#include "py/mpconfig.h"
31-
#if MICROPY_PY_COLLECTIONS_DEQUE
3228

3329
#include "py/runtime.h"
3430

31+
#if MICROPY_PY_COLLECTIONS_DEQUE
32+
3533
typedef struct _mp_obj_deque_t {
3634
mp_obj_base_t base;
3735
size_t alloc;
@@ -42,15 +40,15 @@ typedef struct _mp_obj_deque_t {
4240
#define FLAG_CHECK_OVERFLOW 1
4341
} mp_obj_deque_t;
4442

43+
static mp_obj_t mp_obj_deque_append(mp_obj_t self_in, mp_obj_t arg);
44+
static mp_obj_t mp_obj_deque_extend(mp_obj_t self_in, mp_obj_t arg_in);
45+
#if MICROPY_PY_COLLECTIONS_DEQUE_ITER
46+
static mp_obj_t mp_obj_new_deque_it(mp_obj_t deque, mp_obj_iter_buf_t *iter_buf);
47+
#endif
48+
4549
static mp_obj_t deque_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
4650
mp_arg_check_num(n_args, n_kw, 2, 3, false);
4751

48-
/* Initialization from existing sequence is not supported, so an empty
49-
tuple must be passed as such. */
50-
if (args[0] != mp_const_empty_tuple) {
51-
mp_raise_ValueError(NULL);
52-
}
53-
5452
// Protect against -1 leading to zero-length allocation and bad array access
5553
mp_int_t maxlen = mp_obj_get_int(args[1]);
5654
if (maxlen < 0) {
@@ -66,21 +64,27 @@ static mp_obj_t deque_make_new(const mp_obj_type_t *type, size_t n_args, size_t
6664
o->flags = mp_obj_get_int(args[2]);
6765
}
6866

67+
mp_obj_deque_extend(MP_OBJ_FROM_PTR(o), args[0]);
68+
6969
return MP_OBJ_FROM_PTR(o);
7070
}
7171

72+
static size_t deque_len(mp_obj_deque_t *self) {
73+
ssize_t len = self->i_put - self->i_get;
74+
if (len < 0) {
75+
len += self->alloc;
76+
}
77+
return len;
78+
}
79+
7280
static mp_obj_t deque_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
7381
mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in);
7482
switch (op) {
7583
case MP_UNARY_OP_BOOL:
7684
return mp_obj_new_bool(self->i_get != self->i_put);
77-
case MP_UNARY_OP_LEN: {
78-
ssize_t len = self->i_put - self->i_get;
79-
if (len < 0) {
80-
len += self->alloc;
81-
}
82-
return MP_OBJ_NEW_SMALL_INT(len);
83-
}
85+
case MP_UNARY_OP_LEN:
86+
return MP_OBJ_NEW_SMALL_INT(deque_len(self));
87+
8488
#if MICROPY_PY_SYS_GETSIZEOF
8589
case MP_UNARY_OP_SIZEOF: {
8690
size_t sz = sizeof(*self) + sizeof(mp_obj_t) * self->alloc;
@@ -117,6 +121,45 @@ static mp_obj_t mp_obj_deque_append(mp_obj_t self_in, mp_obj_t arg) {
117121
}
118122
static MP_DEFINE_CONST_FUN_OBJ_2(deque_append_obj, mp_obj_deque_append);
119123

124+
static mp_obj_t mp_obj_deque_appendleft(mp_obj_t self_in, mp_obj_t arg) {
125+
mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in);
126+
127+
size_t new_i_get = self->i_get - 1;
128+
if (self->i_get == 0) {
129+
new_i_get = self->alloc - 1;
130+
}
131+
132+
if (self->flags & FLAG_CHECK_OVERFLOW && new_i_get == self->i_put) {
133+
mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("full"));
134+
}
135+
136+
self->i_get = new_i_get;
137+
self->items[self->i_get] = arg;
138+
139+
// overwriting first element in deque
140+
if (self->i_put == new_i_get) {
141+
if (self->i_put == 0) {
142+
self->i_put = self->alloc - 1;
143+
} else {
144+
self->i_put--;
145+
}
146+
}
147+
148+
return mp_const_none;
149+
}
150+
static MP_DEFINE_CONST_FUN_OBJ_2(deque_appendleft_obj, mp_obj_deque_appendleft);
151+
152+
static mp_obj_t mp_obj_deque_extend(mp_obj_t self_in, mp_obj_t arg_in) {
153+
mp_obj_iter_buf_t iter_buf;
154+
mp_obj_t iter = mp_getiter(arg_in, &iter_buf);
155+
mp_obj_t item;
156+
while ((item = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
157+
mp_obj_deque_append(self_in, item);
158+
}
159+
return mp_const_none;
160+
}
161+
static MP_DEFINE_CONST_FUN_OBJ_2(deque_extend_obj, mp_obj_deque_extend);
162+
120163
static mp_obj_t deque_popleft(mp_obj_t self_in) {
121164
mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in);
122165

@@ -135,6 +178,51 @@ static mp_obj_t deque_popleft(mp_obj_t self_in) {
135178
}
136179
static MP_DEFINE_CONST_FUN_OBJ_1(deque_popleft_obj, deque_popleft);
137180

181+
static mp_obj_t deque_pop(mp_obj_t self_in) {
182+
mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in);
183+
184+
if (self->i_get == self->i_put) {
185+
mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("empty"));
186+
}
187+
188+
if (self->i_put == 0) {
189+
self->i_put = self->alloc - 1;
190+
} else {
191+
self->i_put--;
192+
}
193+
194+
mp_obj_t ret = self->items[self->i_put];
195+
self->items[self->i_put] = MP_OBJ_NULL;
196+
197+
return ret;
198+
}
199+
static MP_DEFINE_CONST_FUN_OBJ_1(deque_pop_obj, deque_pop);
200+
201+
#if MICROPY_PY_COLLECTIONS_DEQUE_SUBSCR
202+
static mp_obj_t deque_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
203+
if (value == MP_OBJ_NULL) {
204+
// delete not supported, fall back to mp_obj_subscr() error message
205+
return MP_OBJ_NULL;
206+
}
207+
mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in);
208+
209+
size_t offset = mp_get_index(self->base.type, deque_len(self), index, false);
210+
size_t index_val = self->i_get + offset;
211+
if (index_val > self->alloc) {
212+
index_val -= self->alloc;
213+
}
214+
215+
if (value == MP_OBJ_SENTINEL) {
216+
// load
217+
return self->items[index_val];
218+
} else {
219+
// store into deque
220+
self->items[index_val] = value;
221+
return mp_const_none;
222+
}
223+
}
224+
#endif
225+
138226
#if 0
139227
static mp_obj_t deque_clear(mp_obj_t self_in) {
140228
mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in);
@@ -147,21 +235,80 @@ static MP_DEFINE_CONST_FUN_OBJ_1(deque_clear_obj, deque_clear);
147235

148236
static const mp_rom_map_elem_t deque_locals_dict_table[] = {
149237
{ MP_ROM_QSTR(MP_QSTR_append), MP_ROM_PTR(&deque_append_obj) },
238+
{ MP_ROM_QSTR(MP_QSTR_appendleft), MP_ROM_PTR(&deque_appendleft_obj) },
239+
{ MP_ROM_QSTR(MP_QSTR_extend), MP_ROM_PTR(&deque_extend_obj) },
150240
#if 0
151241
{ MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&deque_clear_obj) },
152242
#endif
243+
{ MP_ROM_QSTR(MP_QSTR_pop), MP_ROM_PTR(&deque_pop_obj) },
153244
{ MP_ROM_QSTR(MP_QSTR_popleft), MP_ROM_PTR(&deque_popleft_obj) },
154245
};
155246

156247
static MP_DEFINE_CONST_DICT(deque_locals_dict, deque_locals_dict_table);
157248

249+
#if MICROPY_PY_COLLECTIONS_DEQUE_ITER
250+
#define DEQUE_TYPE_FLAGS MP_TYPE_FLAG_ITER_IS_GETITER
251+
#define DEQUE_TYPE_ITER iter, mp_obj_new_deque_it,
252+
#else
253+
#define DEQUE_TYPE_FLAGS MP_TYPE_FLAG_NONE
254+
#define DEQUE_TYPE_ITER
255+
#endif
256+
257+
#if MICROPY_PY_COLLECTIONS_DEQUE_SUBSCR
258+
#define DEQUE_TYPE_SUBSCR subscr, deque_subscr,
259+
#else
260+
#define DEQUE_TYPE_SUBSCR
261+
#endif
262+
158263
MP_DEFINE_CONST_OBJ_TYPE(
159264
mp_type_deque,
160265
MP_QSTR_deque,
161-
MP_TYPE_FLAG_NONE,
266+
MP_TYPE_FLAG_ITER_IS_GETITER,
162267
make_new, deque_make_new,
163268
unary_op, deque_unary_op,
269+
DEQUE_TYPE_SUBSCR
270+
DEQUE_TYPE_ITER
164271
locals_dict, &deque_locals_dict
165272
);
166273

274+
/******************************************************************************/
275+
/* deque iterator */
276+
277+
#if MICROPY_PY_COLLECTIONS_DEQUE_ITER
278+
279+
typedef struct _mp_obj_deque_it_t {
280+
mp_obj_base_t base;
281+
mp_fun_1_t iternext;
282+
mp_obj_t deque;
283+
size_t cur;
284+
} mp_obj_deque_it_t;
285+
286+
static mp_obj_t deque_it_iternext(mp_obj_t self_in) {
287+
mp_obj_deque_it_t *self = MP_OBJ_TO_PTR(self_in);
288+
mp_obj_deque_t *deque = MP_OBJ_TO_PTR(self->deque);
289+
if (self->cur != deque->i_put) {
290+
mp_obj_t o_out = deque->items[self->cur];
291+
if (++self->cur == deque->alloc) {
292+
self->cur = 0;
293+
}
294+
return o_out;
295+
} else {
296+
return MP_OBJ_STOP_ITERATION;
297+
}
298+
}
299+
300+
static mp_obj_t mp_obj_new_deque_it(mp_obj_t deque, mp_obj_iter_buf_t *iter_buf) {
301+
mp_obj_deque_t *deque_ = MP_OBJ_TO_PTR(deque);
302+
size_t i_get = deque_->i_get;
303+
assert(sizeof(mp_obj_deque_it_t) <= sizeof(mp_obj_iter_buf_t));
304+
mp_obj_deque_it_t *o = (mp_obj_deque_it_t *)iter_buf;
305+
o->base.type = &mp_type_polymorph_iter;
306+
o->iternext = deque_it_iternext;
307+
o->deque = deque;
308+
o->cur = i_get;
309+
return MP_OBJ_FROM_PTR(o);
310+
}
311+
312+
#endif
313+
167314
#endif // MICROPY_PY_COLLECTIONS_DEQUE

tests/basics/deque1.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,52 @@
6363
~d
6464
except TypeError:
6565
print("TypeError")
66+
67+
68+
# Same tests, but now with pop() and appendleft()
69+
70+
d = deque((), 2)
71+
print(len(d))
72+
print(bool(d))
73+
74+
try:
75+
d.popleft()
76+
except IndexError:
77+
print("IndexError")
78+
79+
print(d.append(1))
80+
print(len(d))
81+
print(bool(d))
82+
print(d.popleft())
83+
print(len(d))
84+
85+
d.append(2)
86+
print(d.popleft())
87+
88+
d.append(3)
89+
d.append(4)
90+
print(len(d))
91+
print(d.popleft(), d.popleft())
92+
try:
93+
d.popleft()
94+
except IndexError:
95+
print("IndexError")
96+
97+
d.append(5)
98+
d.append(6)
99+
d.append(7)
100+
print(len(d))
101+
print(d.popleft(), d.popleft())
102+
print(len(d))
103+
try:
104+
d.popleft()
105+
except IndexError:
106+
print("IndexError")
107+
108+
d = deque((), 2)
109+
d.appendleft(1)
110+
d.appendleft(2)
111+
d.appendleft(3)
112+
d.appendleft(4)
113+
d.appendleft(5)
114+
print(d.pop(), d.pop())

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy