Branch data Line data Source code
1 : : /*
2 : : * This file is part of the MicroPython project, http://micropython.org/
3 : : *
4 : : * The MIT License (MIT)
5 : : *
6 : : * Copyright (c) 2013, 2014 Damien P. George
7 : : * Copyright (c) 2014 Paul Sokolovsky
8 : : *
9 : : * Permission is hereby granted, free of charge, to any person obtaining a copy
10 : : * of this software and associated documentation files (the "Software"), to deal
11 : : * in the Software without restriction, including without limitation the rights
12 : : * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 : : * copies of the Software, and to permit persons to whom the Software is
14 : : * furnished to do so, subject to the following conditions:
15 : : *
16 : : * The above copyright notice and this permission notice shall be included in
17 : : * all copies or substantial portions of the Software.
18 : : *
19 : : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 : : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 : : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 : : * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 : : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 : : * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 : : * THE SOFTWARE.
26 : : */
27 : :
28 : : #include <string.h>
29 : : #include <assert.h>
30 : : #include <stdint.h>
31 : :
32 : : #include "py/runtime.h"
33 : : #include "py/binary.h"
34 : : #include "py/objstr.h"
35 : : #include "py/objarray.h"
36 : :
37 : : #if MICROPY_PY_ARRAY || MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_BUILTINS_MEMORYVIEW
38 : :
39 : : // About memoryview object: We want to reuse as much code as possible from
40 : : // array, and keep the memoryview object 4 words in size so it fits in 1 GC
41 : : // block. Also, memoryview must keep a pointer to the base of the buffer so
42 : : // that the buffer is not GC'd if the original parent object is no longer
43 : : // around (we are assuming that all memoryview'able objects return a pointer
44 : : // which points to the start of a GC chunk). Given the above constraints we
45 : : // do the following:
46 : : // - typecode high bit is set if the buffer is read-write (else read-only)
47 : : // - free is the offset in elements to the first item in the memoryview
48 : : // - len is the length in elements
49 : : // - items points to the start of the original buffer
50 : : // Note that we don't handle the case where the original buffer might change
51 : : // size due to a resize of the original parent object.
52 : :
53 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW
54 : : #define TYPECODE_MASK (0x7f)
55 : : #define memview_offset free
56 : : #define memview_offset_max ((1LL << MP_OBJ_ARRAY_FREE_SIZE_BITS) - 1)
57 : : #else
58 : : // make (& TYPECODE_MASK) a null operation if memorview not enabled
59 : : #define TYPECODE_MASK (~(size_t)0)
60 : : // memview_offset should not be accessed if memoryview is not enabled,
61 : : // so not defined to catch errors
62 : : #endif
63 : :
64 : : static mp_obj_t array_iterator_new(mp_obj_t array_in, mp_obj_iter_buf_t *iter_buf);
65 : : static mp_obj_t array_append(mp_obj_t self_in, mp_obj_t arg);
66 : : static mp_obj_t array_extend(mp_obj_t self_in, mp_obj_t arg_in);
67 : : static mp_int_t array_get_buffer(mp_obj_t o_in, mp_buffer_info_t *bufinfo, mp_uint_t flags);
68 : :
69 : : /******************************************************************************/
70 : : // array
71 : :
72 : : #if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY
73 : 990 : static void array_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
74 : 990 : (void)kind;
75 : 990 : mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in);
76 [ + + ]: 990 : if (o->typecode == BYTEARRAY_TYPECODE) {
77 : 866 : mp_print_str(print, "bytearray(b");
78 : 866 : mp_str_print_quoted(print, o->items, o->len, true);
79 : : } else {
80 : 124 : mp_printf(print, "array('%c'", o->typecode);
81 [ + + ]: 124 : if (o->len > 0) {
82 : 104 : mp_print_str(print, ", [");
83 [ + + ]: 452 : for (size_t i = 0; i < o->len; i++) {
84 [ + + ]: 348 : if (i > 0) {
85 : 244 : mp_print_str(print, ", ");
86 : : }
87 : 348 : mp_obj_print_helper(print, mp_binary_get_val_array(o->typecode, o->items, i), PRINT_REPR);
88 : : }
89 : 104 : mp_print_str(print, "]");
90 : : }
91 : : }
92 : 990 : mp_print_str(print, ")");
93 : 990 : }
94 : : #endif
95 : :
96 : : #if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY
97 : 5024 : static mp_obj_array_t *array_new(char typecode, size_t n) {
98 : 5024 : int typecode_size = mp_binary_get_size('@', typecode, NULL);
99 : 5021 : mp_obj_array_t *o = m_new_obj(mp_obj_array_t);
100 : : #if MICROPY_PY_BUILTINS_BYTEARRAY && MICROPY_PY_ARRAY
101 [ + + ]: 5000 : o->base.type = (typecode == BYTEARRAY_TYPECODE) ? &mp_type_bytearray : &mp_type_array;
102 : : #elif MICROPY_PY_BUILTINS_BYTEARRAY
103 : : o->base.type = &mp_type_bytearray;
104 : : #else
105 : : o->base.type = &mp_type_array;
106 : : #endif
107 : 5000 : o->typecode = typecode;
108 : 5000 : o->free = 0;
109 : 5000 : o->len = n;
110 : 5000 : o->items = m_new(byte, typecode_size * o->len);
111 : 5000 : return o;
112 : : }
113 : : #endif
114 : :
115 : : #if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY
116 : 1011 : static mp_obj_t array_construct(char typecode, mp_obj_t initializer) {
117 : : // bytearrays can be raw-initialised from anything with the buffer protocol
118 : : // other arrays can only be raw-initialised from bytes and bytearray objects
119 : 1011 : mp_buffer_info_t bufinfo;
120 [ + + ]: 1011 : if (((MICROPY_PY_BUILTINS_BYTEARRAY
121 : : && typecode == BYTEARRAY_TYPECODE)
122 : : || (MICROPY_PY_ARRAY
123 [ - + - + : 496 : && (mp_obj_is_type(initializer, &mp_type_bytes)
- + - + +
- + + ]
124 [ + - + + ]: 466 : || (MICROPY_PY_BUILTINS_BYTEARRAY && mp_obj_is_type(initializer, &mp_type_bytearray)))))
125 [ + + ]: 553 : && mp_get_buffer(initializer, &bufinfo, MP_BUFFER_READ)) {
126 : : // construct array from raw bytes
127 : : // we round-down the len to make it a multiple of sz (CPython raises error)
128 : 454 : size_t sz = mp_binary_get_size('@', typecode, NULL);
129 : 454 : size_t len = bufinfo.len / sz;
130 : 454 : mp_obj_array_t *o = array_new(typecode, len);
131 : 450 : memcpy(o->items, bufinfo.buf, len * sz);
132 : 450 : return MP_OBJ_FROM_PTR(o);
133 : : }
134 : :
135 : 557 : size_t len;
136 : : // Try to create array of exact len if initializer len is known
137 : 557 : mp_obj_t len_in = mp_obj_len_maybe(initializer);
138 [ + + ]: 557 : if (len_in == MP_OBJ_NULL) {
139 : : len = 0;
140 : : } else {
141 : 534 : len = MP_OBJ_SMALL_INT_VALUE(len_in);
142 : : }
143 : :
144 : 557 : mp_obj_array_t *array = array_new(typecode, len);
145 : :
146 : 553 : mp_obj_t iterable = mp_getiter(initializer, NULL);
147 : 553 : mp_obj_t item;
148 : 553 : size_t i = 0;
149 [ + + ]: 5922 : while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
150 [ + + ]: 5366 : if (len == 0) {
151 : 3920 : array_append(MP_OBJ_FROM_PTR(array), item);
152 : : } else {
153 : 1446 : mp_binary_set_val_array(typecode, array->items, i++, item);
154 : : }
155 : : }
156 : :
157 : : return MP_OBJ_FROM_PTR(array);
158 : : }
159 : : #endif
160 : :
161 : : #if MICROPY_PY_ARRAY
162 : 1306 : static mp_obj_t array_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
163 : 1306 : (void)type_in;
164 : 1306 : mp_arg_check_num(n_args, n_kw, 1, 2, false);
165 : :
166 : : // get typecode
167 : 1306 : const char *typecode = mp_obj_str_get_str(args[0]);
168 : :
169 [ + + ]: 1306 : if (n_args == 1) {
170 : : // 1 arg: make an empty array
171 : 810 : return MP_OBJ_FROM_PTR(array_new(*typecode, 0));
172 : : } else {
173 : : // 2 args: construct the array from the given object
174 : 496 : return array_construct(*typecode, args[1]);
175 : : }
176 : : }
177 : : #endif
178 : :
179 : : #if MICROPY_PY_BUILTINS_BYTEARRAY
180 : 3333 : static mp_obj_t bytearray_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
181 : 3333 : (void)type_in;
182 : : // Can take 2nd/3rd arg if constructs from str
183 : 3333 : mp_arg_check_num(n_args, n_kw, 0, 3, false);
184 : :
185 [ + + ]: 3332 : if (n_args == 0) {
186 : : // no args: construct an empty bytearray
187 : 52 : return MP_OBJ_FROM_PTR(array_new(BYTEARRAY_TYPECODE, 0));
188 [ + + + + : 3280 : } else if (mp_obj_is_int(args[0])) {
+ + ]
189 : : // 1 arg, an integer: construct a blank bytearray of that length
190 : 2761 : mp_uint_t len = mp_obj_get_int(args[0]);
191 : 2757 : mp_obj_array_t *o = array_new(BYTEARRAY_TYPECODE, len);
192 : 2753 : memset(o->items, 0, len);
193 : 2753 : return MP_OBJ_FROM_PTR(o);
194 : : } else {
195 : : // 1 arg: construct the bytearray from that
196 [ + + + - : 519 : if (mp_obj_is_str(args[0]) && n_args == 1) {
- + + + ]
197 : : #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE
198 : : // Match bytes_make_new.
199 : : mp_raise_TypeError(MP_ERROR_TEXT("wrong number of arguments"));
200 : : #else
201 : 4 : mp_raise_TypeError(MP_ERROR_TEXT("string argument without an encoding"));
202 : : #endif
203 : : }
204 : 515 : return array_construct(BYTEARRAY_TYPECODE, args[0]);
205 : : }
206 : : }
207 : : #endif
208 : :
209 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW
210 : :
211 : 662 : mp_obj_t mp_obj_new_memoryview(byte typecode, size_t nitems, void *items) {
212 : 662 : mp_obj_array_t *self = m_new_obj(mp_obj_array_t);
213 : 658 : mp_obj_memoryview_init(self, typecode, 0, nitems, items);
214 : 658 : return MP_OBJ_FROM_PTR(self);
215 : : }
216 : :
217 : 662 : static mp_obj_t memoryview_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
218 : 662 : (void)type_in;
219 : :
220 : : // TODO possibly allow memoryview constructor to take start/stop so that one
221 : : // can do memoryview(b, 4, 8) instead of memoryview(b)[4:8] (uses less RAM)
222 : :
223 : 662 : mp_arg_check_num(n_args, n_kw, 1, 1, false);
224 : :
225 : 662 : mp_buffer_info_t bufinfo;
226 : 662 : mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);
227 : :
228 : 662 : mp_obj_array_t *self = MP_OBJ_TO_PTR(mp_obj_new_memoryview(bufinfo.typecode,
229 : : bufinfo.len / mp_binary_get_size('@', bufinfo.typecode, NULL),
230 : : bufinfo.buf));
231 : :
232 : : // If the input object is a memoryview then need to point the items of the
233 : : // new memoryview to the start of the buffer so the GC can trace it.
234 [ + + ]: 658 : if (mp_obj_get_type(args[0]) == &mp_type_memoryview) {
235 : 8 : mp_obj_array_t *other = MP_OBJ_TO_PTR(args[0]);
236 : 8 : self->memview_offset = other->memview_offset;
237 : 8 : self->items = other->items;
238 : : }
239 : :
240 : : // test if the object can be written to
241 [ + + ]: 658 : if (mp_get_buffer(args[0], &bufinfo, MP_BUFFER_RW)) {
242 : 432 : self->typecode |= MP_OBJ_ARRAY_TYPECODE_FLAG_RW; // indicate writable buffer
243 : : }
244 : :
245 : 658 : return MP_OBJ_FROM_PTR(self);
246 : : }
247 : :
248 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE
249 : 72 : static void memoryview_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
250 [ + + ]: 72 : if (dest[0] != MP_OBJ_NULL) {
251 : : return;
252 : : }
253 [ + + ]: 68 : if (attr == MP_QSTR_itemsize) {
254 : 32 : mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in);
255 : 32 : dest[0] = MP_OBJ_NEW_SMALL_INT(mp_binary_get_size('@', self->typecode & TYPECODE_MASK, NULL));
256 : : }
257 : : #if MICROPY_PY_BUILTINS_BYTES_HEX
258 : : else {
259 : : // Need to forward to locals dict.
260 : 36 : dest[1] = MP_OBJ_SENTINEL;
261 : : }
262 : : #endif
263 : : }
264 : : #endif
265 : :
266 : : #endif
267 : :
268 : 23266 : static mp_obj_t array_unary_op(mp_unary_op_t op, mp_obj_t o_in) {
269 : 23266 : mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in);
270 [ + + + ]: 23266 : switch (op) {
271 : 8 : case MP_UNARY_OP_BOOL:
272 [ + + ]: 8 : return mp_obj_new_bool(o->len != 0);
273 : 23244 : case MP_UNARY_OP_LEN:
274 : 23244 : return MP_OBJ_NEW_SMALL_INT(o->len);
275 : : default:
276 : : return MP_OBJ_NULL; // op not supported
277 : : }
278 : : }
279 : :
280 : 1640 : static int typecode_for_comparison(int typecode, bool *is_unsigned) {
281 [ + + ]: 1640 : if (typecode == BYTEARRAY_TYPECODE) {
282 : : typecode = 'B';
283 : : }
284 [ + + ]: 1468 : if (typecode <= 'Z') {
285 : 1040 : typecode += 32; // to lowercase
286 : 1040 : *is_unsigned = true;
287 : : }
288 : 1640 : return typecode;
289 : : }
290 : :
291 : 930 : static mp_obj_t array_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
292 : 930 : mp_obj_array_t *lhs = MP_OBJ_TO_PTR(lhs_in);
293 [ + + + + : 930 : switch (op) {
+ ]
294 : 28 : case MP_BINARY_OP_ADD: {
295 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW
296 [ + + ]: 28 : if (lhs->base.type == &mp_type_memoryview) {
297 : : return MP_OBJ_NULL; // op not supported
298 : : }
299 : : #endif
300 : :
301 : : // allow to add anything that has the buffer protocol (extension to CPython)
302 : 16 : mp_buffer_info_t lhs_bufinfo;
303 : 16 : mp_buffer_info_t rhs_bufinfo;
304 : 16 : array_get_buffer(lhs_in, &lhs_bufinfo, MP_BUFFER_READ);
305 : 16 : mp_get_buffer_raise(rhs_in, &rhs_bufinfo, MP_BUFFER_READ);
306 : :
307 : 12 : size_t sz = mp_binary_get_size('@', lhs_bufinfo.typecode, NULL);
308 : :
309 : : // convert byte count to element count (in case rhs is not multiple of sz)
310 : 12 : size_t rhs_len = rhs_bufinfo.len / sz;
311 : :
312 : : // note: lhs->len is element count of lhs, lhs_bufinfo.len is byte count
313 : 12 : mp_obj_array_t *res = array_new(lhs_bufinfo.typecode, lhs->len + rhs_len);
314 : 8 : mp_seq_cat((byte *)res->items, lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_len * sz, byte);
315 : 8 : return MP_OBJ_FROM_PTR(res);
316 : : }
317 : :
318 : 36 : case MP_BINARY_OP_INPLACE_ADD: {
319 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW
320 [ + + ]: 36 : if (lhs->base.type == &mp_type_memoryview) {
321 : : return MP_OBJ_NULL; // op not supported
322 : : }
323 : : #endif
324 : 32 : array_extend(lhs_in, rhs_in);
325 : 32 : return lhs_in;
326 : : }
327 : :
328 : 30 : case MP_BINARY_OP_CONTAINS: {
329 : : #if MICROPY_PY_BUILTINS_BYTEARRAY
330 : : // Can search string only in bytearray
331 : 30 : mp_buffer_info_t lhs_bufinfo;
332 : 30 : mp_buffer_info_t rhs_bufinfo;
333 [ + + ]: 30 : if (mp_get_buffer(rhs_in, &rhs_bufinfo, MP_BUFFER_READ)) {
334 [ + - + + ]: 24 : if (!mp_obj_is_type(lhs_in, &mp_type_bytearray)) {
335 : : return mp_const_false;
336 : : }
337 : 20 : array_get_buffer(lhs_in, &lhs_bufinfo, MP_BUFFER_READ);
338 [ + + ]: 28 : return mp_obj_new_bool(
339 : 20 : find_subbytes(lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_bufinfo.len, 1) != NULL);
340 : : }
341 : : #endif
342 : :
343 : : // Otherwise, can only look for a scalar numeric value in an array
344 [ + + + - : 6 : if (mp_obj_is_int(rhs_in) || mp_obj_is_float(rhs_in)) {
+ - - + -
+ - + - +
+ - - + ]
345 : 2 : mp_raise_NotImplementedError(NULL);
346 : : }
347 : :
348 : : return mp_const_false;
349 : : }
350 : :
351 : 824 : case MP_BINARY_OP_EQUAL:
352 : : case MP_BINARY_OP_LESS:
353 : : case MP_BINARY_OP_LESS_EQUAL:
354 : : case MP_BINARY_OP_MORE:
355 : : case MP_BINARY_OP_MORE_EQUAL: {
356 : 824 : mp_buffer_info_t lhs_bufinfo;
357 : 824 : mp_buffer_info_t rhs_bufinfo;
358 : 824 : array_get_buffer(lhs_in, &lhs_bufinfo, MP_BUFFER_READ);
359 [ + + ]: 824 : if (!mp_get_buffer(rhs_in, &rhs_bufinfo, MP_BUFFER_READ)) {
360 : : return mp_const_false;
361 : : }
362 : : // mp_seq_cmp_bytes is used so only compatible representations can be correctly compared.
363 : : // The type doesn't matter: array/bytearray/str/bytes all have the same buffer layout, so
364 : : // just check if the typecodes are compatible; for testing equality the types should have the
365 : : // same code except for signedness, and not be floating point because nan never equals nan.
366 : : // For > and < the types should be the same and unsigned.
367 : : // Note that typecode_for_comparison always returns lowercase letters to save code size.
368 : : // No need for (& TYPECODE_MASK) here: xxx_get_buffer already takes care of that.
369 : 820 : bool is_unsigned = false;
370 : 820 : const int lhs_code = typecode_for_comparison(lhs_bufinfo.typecode, &is_unsigned);
371 : 820 : const int rhs_code = typecode_for_comparison(rhs_bufinfo.typecode, &is_unsigned);
372 [ + + + + : 820 : if (lhs_code == rhs_code && lhs_code != 'f' && lhs_code != 'd' && (op == MP_BINARY_OP_EQUAL || is_unsigned)) {
+ + + - ]
373 [ + + ]: 648 : return mp_obj_new_bool(mp_seq_cmp_bytes(op, lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_bufinfo.len));
374 : : }
375 : : // mp_obj_equal_not_equal treats returning MP_OBJ_NULL as 'fall back to pointer comparison'
376 : : // for MP_BINARY_OP_EQUAL but that is incompatible with CPython.
377 : 368 : mp_raise_NotImplementedError(NULL);
378 : : }
379 : :
380 : : default:
381 : : return MP_OBJ_NULL; // op not supported
382 : : }
383 : : }
384 : :
385 : : #if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY
386 : 3971 : static mp_obj_t array_append(mp_obj_t self_in, mp_obj_t arg) {
387 : : // self is not a memoryview, so we don't need to use (& TYPECODE_MASK)
388 [ + + + + : 3971 : assert((MICROPY_PY_BUILTINS_BYTEARRAY && mp_obj_is_type(self_in, &mp_type_bytearray))
+ - - + ]
389 : : || (MICROPY_PY_ARRAY && mp_obj_is_type(self_in, &mp_type_array)));
390 : 3971 : mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in);
391 : :
392 [ + + ]: 3971 : if (self->free == 0) {
393 : 543 : size_t item_sz = mp_binary_get_size('@', self->typecode, NULL);
394 : : // TODO: alloc policy
395 : 544 : size_t add_cnt = 8;
396 : 544 : self->items = m_renew(byte, self->items, item_sz * self->len, item_sz * (self->len + add_cnt));
397 : 544 : self->free = add_cnt;
398 : 544 : mp_seq_clear(self->items, self->len + 1, self->len + self->free, item_sz);
399 : : }
400 : 3972 : mp_binary_set_val_array(self->typecode, self->items, self->len, arg);
401 : : // only update length/free if set succeeded
402 : 3975 : self->len++;
403 : 3975 : self->free--;
404 : 3975 : return mp_const_none; // return None, as per CPython
405 : : }
406 : : MP_DEFINE_CONST_FUN_OBJ_2(mp_obj_array_append_obj, array_append);
407 : :
408 : 60 : static mp_obj_t array_extend(mp_obj_t self_in, mp_obj_t arg_in) {
409 : : // self is not a memoryview, so we don't need to use (& TYPECODE_MASK)
410 [ + - + + : 60 : assert((MICROPY_PY_BUILTINS_BYTEARRAY && mp_obj_is_type(self_in, &mp_type_bytearray))
+ - - + ]
411 : : || (MICROPY_PY_ARRAY && mp_obj_is_type(self_in, &mp_type_array)));
412 : 60 : mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in);
413 : :
414 : : // allow to extend by anything that has the buffer protocol (extension to CPython)
415 : 60 : mp_buffer_info_t arg_bufinfo;
416 : 60 : mp_get_buffer_raise(arg_in, &arg_bufinfo, MP_BUFFER_READ);
417 : :
418 : 60 : size_t sz = mp_binary_get_size('@', self->typecode, NULL);
419 : :
420 : : // convert byte count to element count
421 : 60 : size_t len = arg_bufinfo.len / sz;
422 : :
423 : : // make sure we have enough room to extend
424 : : // TODO: alloc policy; at the moment we go conservative
425 [ + + ]: 60 : if (self->free < len) {
426 : 56 : self->items = m_renew(byte, self->items, (self->len + self->free) * sz, (self->len + len) * sz);
427 : 48 : self->free = 0;
428 : :
429 [ + + ]: 48 : if (self_in == arg_in) {
430 : : // Get arg_bufinfo again in case self->items has moved
431 : : //
432 : : // (Note not possible to handle case that arg_in is a memoryview into self)
433 : 32 : mp_get_buffer_raise(arg_in, &arg_bufinfo, MP_BUFFER_READ);
434 : : }
435 : : } else {
436 : 4 : self->free -= len;
437 : : }
438 : :
439 : : // extend
440 : 52 : mp_seq_copy((byte *)self->items + self->len * sz, arg_bufinfo.buf, len * sz, byte);
441 : 52 : self->len += len;
442 : :
443 : 52 : return mp_const_none;
444 : : }
445 : : MP_DEFINE_CONST_FUN_OBJ_2(mp_obj_array_extend_obj, array_extend);
446 : : #endif
447 : :
448 : 17158368 : static mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) {
449 [ + + ]: 17158368 : if (value == MP_OBJ_NULL) {
450 : : // delete item
451 : : // TODO implement
452 : : // TODO: confirmed that both bytearray and array.array support
453 : : // slice deletion
454 : : return MP_OBJ_NULL; // op not supported
455 : : } else {
456 : 17158366 : mp_obj_array_t *o = MP_OBJ_TO_PTR(self_in);
457 : : #if MICROPY_PY_BUILTINS_SLICE
458 [ - + - + : 17158366 : if (mp_obj_is_type(index_in, &mp_type_slice)) {
- + - + +
+ + - ]
459 : 6806 : mp_bound_slice_t slice;
460 [ + + ]: 6806 : if (!mp_seq_get_fast_slice_indexes(o->len, index_in, &slice)) {
461 : 2 : mp_raise_NotImplementedError(MP_ERROR_TEXT("only slices with step=1 (aka None) are supported"));
462 : : }
463 [ + + ]: 6804 : if (value != MP_OBJ_SENTINEL) {
464 : : #if MICROPY_PY_ARRAY_SLICE_ASSIGN
465 : : // Assign
466 : 4048 : size_t src_len;
467 : 4048 : uint8_t *src_items;
468 : 4048 : size_t src_offs = 0;
469 : 4048 : size_t item_sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL);
470 [ + - + - : 4048 : if (mp_obj_is_obj(value) && MP_OBJ_TYPE_GET_SLOT_OR_NULL(((mp_obj_base_t *)MP_OBJ_TO_PTR(value))->type, subscr) == array_subscr) {
+ + ]
471 : : // value is array, bytearray or memoryview
472 : 2430 : mp_obj_array_t *src_slice = MP_OBJ_TO_PTR(value);
473 [ + + ]: 2430 : if (item_sz != mp_binary_get_size('@', src_slice->typecode & TYPECODE_MASK, NULL)) {
474 : 4 : compat_error:
475 : 20 : mp_raise_ValueError(MP_ERROR_TEXT("lhs and rhs should be compatible"));
476 : : }
477 : 2426 : src_len = src_slice->len;
478 : 2426 : src_items = src_slice->items;
479 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW
480 [ + - + + ]: 2426 : if (mp_obj_is_type(value, &mp_type_memoryview)) {
481 : 2344 : src_offs = src_slice->memview_offset * item_sz;
482 : : }
483 : : #endif
484 [ - + - + : 1618 : } else if (mp_obj_is_type(value, &mp_type_bytes)) {
- + - + +
- + + ]
485 [ + + ]: 1616 : if (item_sz != 1) {
486 : 4 : goto compat_error;
487 : : }
488 : 1612 : mp_buffer_info_t bufinfo;
489 : 1612 : mp_get_buffer_raise(value, &bufinfo, MP_BUFFER_READ);
490 : 1612 : src_len = bufinfo.len;
491 : 1612 : src_items = bufinfo.buf;
492 : : } else {
493 : 2 : mp_raise_NotImplementedError(MP_ERROR_TEXT("array/bytes required on right side"));
494 : : }
495 : :
496 : : // TODO: check src/dst compat
497 : 4038 : mp_int_t len_adj = src_len - (slice.stop - slice.start);
498 : 4038 : uint8_t *dest_items = o->items;
499 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW
500 [ + + ]: 4038 : if (o->base.type == &mp_type_memoryview) {
501 [ + + ]: 48 : if (!(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW)) {
502 : : // store to read-only memoryview not allowed
503 : : return MP_OBJ_NULL;
504 : : }
505 [ + + ]: 44 : if (len_adj != 0) {
506 : 12 : goto compat_error;
507 : : }
508 : 32 : dest_items += o->memview_offset * item_sz;
509 : : }
510 : : #endif
511 [ + + ]: 4022 : if (len_adj > 0) {
512 [ + + ]: 1632 : if ((size_t)len_adj > o->free) {
513 : : // TODO: alloc policy; at the moment we go conservative
514 : 1604 : o->items = m_renew(byte, o->items, (o->len + o->free) * item_sz, (o->len + len_adj) * item_sz);
515 : 1600 : o->free = len_adj;
516 : : // m_renew may have moved o->items
517 [ + + ]: 1600 : if (src_items == dest_items) {
518 : 16 : src_items = o->items;
519 : : }
520 : : dest_items = o->items;
521 : : }
522 : 1628 : mp_seq_replace_slice_grow_inplace(dest_items, o->len,
523 : 1628 : slice.start, slice.stop, src_items + src_offs, src_len, len_adj, item_sz);
524 : : } else {
525 : 2390 : mp_seq_replace_slice_no_grow(dest_items, o->len,
526 : 2390 : slice.start, slice.stop, src_items + src_offs, src_len, item_sz);
527 : : // Clear "freed" elements at the end of list
528 : : // TODO: This is actually only needed for typecode=='O'
529 : 2390 : mp_seq_clear(dest_items, o->len + len_adj, o->len, item_sz);
530 : : // TODO: alloc policy after shrinking
531 : : }
532 : 4018 : o->free -= len_adj;
533 : 4018 : o->len += len_adj;
534 : 4018 : return mp_const_none;
535 : : #else
536 : : return MP_OBJ_NULL; // op not supported
537 : : #endif
538 : : }
539 : :
540 : 2756 : mp_obj_array_t *res;
541 : 2756 : size_t sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL);
542 [ - + ]: 2756 : assert(sz > 0);
543 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW
544 [ + + ]: 2756 : if (o->base.type == &mp_type_memoryview) {
545 [ + + ]: 2476 : if (slice.start > memview_offset_max) {
546 : 4 : mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("memoryview offset too large"));
547 : : }
548 : 2472 : res = m_new_obj(mp_obj_array_t);
549 : 2468 : *res = *o;
550 : 2468 : res->memview_offset += slice.start;
551 : 2468 : res->len = slice.stop - slice.start;
552 : : } else
553 : : #endif
554 : : {
555 : 280 : res = array_new(o->typecode, slice.stop - slice.start);
556 : 276 : memcpy(res->items, (uint8_t *)o->items + slice.start * sz, (slice.stop - slice.start) * sz);
557 : : }
558 : 2744 : return MP_OBJ_FROM_PTR(res);
559 : : } else
560 : : #endif
561 : : {
562 : 17151560 : size_t index = mp_get_index(o->base.type, o->len, index_in, false);
563 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW
564 [ + + ]: 17645478 : if (o->base.type == &mp_type_memoryview) {
565 : 66 : index += o->memview_offset;
566 [ + + + + ]: 66 : if (value != MP_OBJ_SENTINEL && !(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW)) {
567 : : // store to read-only memoryview
568 : : return MP_OBJ_NULL;
569 : : }
570 : : }
571 : : #endif
572 [ + + ]: 17645456 : if (value == MP_OBJ_SENTINEL) {
573 : : // load
574 : 10278945 : return mp_binary_get_val_array(o->typecode & TYPECODE_MASK, o->items, index);
575 : : } else {
576 : : // store
577 : 7366529 : mp_binary_set_val_array(o->typecode & TYPECODE_MASK, o->items, index, value);
578 : 7366529 : return mp_const_none;
579 : : }
580 : : }
581 : : }
582 : : }
583 : :
584 : 3514 : static mp_int_t array_get_buffer(mp_obj_t o_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
585 : 3514 : mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in);
586 : 3514 : size_t sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL);
587 : 3514 : bufinfo->buf = o->items;
588 : 3514 : bufinfo->len = o->len * sz;
589 : 3514 : bufinfo->typecode = o->typecode & TYPECODE_MASK;
590 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW
591 [ + + ]: 3514 : if (o->base.type == &mp_type_memoryview) {
592 [ + + + + ]: 168 : if (!(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW) && (flags & MP_BUFFER_WRITE)) {
593 : : // read-only memoryview
594 : : return 1;
595 : : }
596 : 164 : bufinfo->buf = (uint8_t *)bufinfo->buf + (size_t)o->memview_offset * sz;
597 : : }
598 : : #else
599 : : (void)flags;
600 : : #endif
601 : : return 0;
602 : : }
603 : :
604 : : #if MICROPY_PY_ARRAY
605 : : MP_DEFINE_CONST_OBJ_TYPE(
606 : : mp_type_array,
607 : : MP_QSTR_array,
608 : : MP_TYPE_FLAG_ITER_IS_GETITER,
609 : : make_new, array_make_new,
610 : : print, array_print,
611 : : iter, array_iterator_new,
612 : : unary_op, array_unary_op,
613 : : binary_op, array_binary_op,
614 : : subscr, array_subscr,
615 : : buffer, array_get_buffer,
616 : : locals_dict, &mp_obj_array_locals_dict
617 : : );
618 : : #endif
619 : :
620 : : #if MICROPY_PY_BUILTINS_BYTEARRAY
621 : : MP_DEFINE_CONST_OBJ_TYPE(
622 : : mp_type_bytearray,
623 : : MP_QSTR_bytearray,
624 : : MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_ITER_IS_GETITER,
625 : : make_new, bytearray_make_new,
626 : : print, array_print,
627 : : iter, array_iterator_new,
628 : : unary_op, array_unary_op,
629 : : binary_op, array_binary_op,
630 : : subscr, array_subscr,
631 : : buffer, array_get_buffer,
632 : : locals_dict, &mp_obj_bytearray_locals_dict
633 : : );
634 : : #endif
635 : :
636 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW
637 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE
638 : : #define MEMORYVIEW_TYPE_ATTR attr, memoryview_attr,
639 : : #else
640 : : #define MEMORYVIEW_TYPE_ATTR
641 : : #endif
642 : :
643 : : #if MICROPY_PY_BUILTINS_BYTES_HEX
644 : : #define MEMORYVIEW_TYPE_LOCALS_DICT locals_dict, &mp_obj_memoryview_locals_dict,
645 : : #else
646 : : #define MEMORYVIEW_TYPE_LOCALS_DICT
647 : : #endif
648 : :
649 : : MP_DEFINE_CONST_OBJ_TYPE(
650 : : mp_type_memoryview,
651 : : MP_QSTR_memoryview,
652 : : MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_ITER_IS_GETITER,
653 : : make_new, memoryview_make_new,
654 : : iter, array_iterator_new,
655 : : unary_op, array_unary_op,
656 : : binary_op, array_binary_op,
657 : : MEMORYVIEW_TYPE_LOCALS_DICT
658 : : MEMORYVIEW_TYPE_ATTR
659 : : subscr, array_subscr,
660 : : buffer, array_get_buffer
661 : : );
662 : : #endif // MICROPY_PY_BUILTINS_MEMORYVIEW
663 : :
664 : : /* unused
665 : : size_t mp_obj_array_len(mp_obj_t self_in) {
666 : : return ((mp_obj_array_t *)self_in)->len;
667 : : }
668 : : */
669 : :
670 : : #if MICROPY_PY_BUILTINS_BYTEARRAY
671 : 102 : mp_obj_t mp_obj_new_bytearray(size_t n, const void *items) {
672 : 102 : mp_obj_array_t *o = array_new(BYTEARRAY_TYPECODE, n);
673 : 102 : memcpy(o->items, items, n);
674 : 102 : return MP_OBJ_FROM_PTR(o);
675 : : }
676 : :
677 : : // Create bytearray which references specified memory area
678 : 194 : mp_obj_t mp_obj_new_bytearray_by_ref(size_t n, void *items) {
679 : 194 : mp_obj_array_t *o = mp_obj_malloc(mp_obj_array_t, &mp_type_bytearray);
680 : 194 : o->typecode = BYTEARRAY_TYPECODE;
681 : 194 : o->free = 0;
682 : 194 : o->len = n;
683 : 194 : o->items = items;
684 : 194 : return MP_OBJ_FROM_PTR(o);
685 : : }
686 : : #endif
687 : :
688 : : /******************************************************************************/
689 : : // array iterator
690 : :
691 : : typedef struct _mp_obj_array_it_t {
692 : : mp_obj_base_t base;
693 : : mp_obj_array_t *array;
694 : : size_t offset;
695 : : size_t cur;
696 : : } mp_obj_array_it_t;
697 : :
698 : 4643 : static mp_obj_t array_it_iternext(mp_obj_t self_in) {
699 : 4643 : mp_obj_array_it_t *self = MP_OBJ_TO_PTR(self_in);
700 [ + + ]: 4643 : if (self->cur < self->array->len) {
701 : 4524 : return mp_binary_get_val_array(self->array->typecode & TYPECODE_MASK, self->array->items, self->offset + self->cur++);
702 : : } else {
703 : : return MP_OBJ_STOP_ITERATION;
704 : : }
705 : : }
706 : :
707 : : static MP_DEFINE_CONST_OBJ_TYPE(
708 : : mp_type_array_it,
709 : : MP_QSTR_iterator,
710 : : MP_TYPE_FLAG_ITER_IS_ITERNEXT,
711 : : iter, array_it_iternext
712 : : );
713 : :
714 : 119 : static mp_obj_t array_iterator_new(mp_obj_t array_in, mp_obj_iter_buf_t *iter_buf) {
715 : 119 : assert(sizeof(mp_obj_array_t) <= sizeof(mp_obj_iter_buf_t));
716 : 119 : mp_obj_array_t *array = MP_OBJ_TO_PTR(array_in);
717 : 119 : mp_obj_array_it_t *o = (mp_obj_array_it_t *)iter_buf;
718 : 119 : o->base.type = &mp_type_array_it;
719 : 119 : o->array = array;
720 : 119 : o->offset = 0;
721 : 119 : o->cur = 0;
722 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW
723 [ + + ]: 119 : if (array->base.type == &mp_type_memoryview) {
724 : 76 : o->offset = array->memview_offset;
725 : : }
726 : : #endif
727 : 119 : return MP_OBJ_FROM_PTR(o);
728 : : }
729 : :
730 : : #endif // MICROPY_PY_ARRAY || MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_BUILTINS_MEMORYVIEW
|