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 : 4958 : static mp_obj_array_t *array_new(char typecode, size_t n) {
98 : 4958 : int typecode_size = mp_binary_get_size('@', typecode, NULL);
99 : 4955 : mp_obj_array_t *o = m_new_obj(mp_obj_array_t);
100 : : #if MICROPY_PY_BUILTINS_BYTEARRAY && MICROPY_PY_ARRAY
101 [ + + ]: 4935 : 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 : 4935 : o->typecode = typecode;
108 : 4935 : o->free = 0;
109 : 4935 : o->len = n;
110 : 4935 : o->items = m_new(byte, typecode_size * o->len);
111 : 4935 : 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 [ + + ]: 6053 : while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
150 [ + + ]: 5494 : if (len == 0) {
151 : 4048 : 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 : 3267 : 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 : 3267 : (void)type_in;
182 : : // Can take 2nd/3rd arg if constructs from str
183 : 3267 : mp_arg_check_num(n_args, n_kw, 0, 3, false);
184 : :
185 [ + + ]: 3267 : if (n_args == 0) {
186 : : // no args: construct an empty bytearray
187 : 52 : return MP_OBJ_FROM_PTR(array_new(BYTEARRAY_TYPECODE, 0));
188 [ + + + + : 3215 : } else if (mp_obj_is_int(args[0])) {
+ + ]
189 : : // 1 arg, an integer: construct a blank bytearray of that length
190 : 2696 : mp_uint_t len = mp_obj_get_int(args[0]);
191 : 2692 : mp_obj_array_t *o = array_new(BYTEARRAY_TYPECODE, len);
192 : 2688 : memset(o->items, 0, len);
193 : 2688 : 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 : 656 : mp_obj_t mp_obj_new_memoryview(byte typecode, size_t nitems, void *items) {
212 : 656 : mp_obj_array_t *self = m_new_obj(mp_obj_array_t);
213 : 652 : mp_obj_memoryview_init(self, typecode, 0, nitems, items);
214 : 652 : return MP_OBJ_FROM_PTR(self);
215 : : }
216 : :
217 : 656 : 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 : 656 : (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 : 656 : mp_arg_check_num(n_args, n_kw, 1, 1, false);
224 : :
225 : 656 : mp_buffer_info_t bufinfo;
226 : 656 : mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);
227 : :
228 : 656 : 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 [ + + ]: 652 : 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 [ + + ]: 652 : 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 : 652 : 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 : 22981 : static mp_obj_t array_unary_op(mp_unary_op_t op, mp_obj_t o_in) {
269 : 22981 : mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in);
270 [ + + + ]: 22981 : switch (op) {
271 : 8 : case MP_UNARY_OP_BOOL:
272 [ + + ]: 8 : return mp_obj_new_bool(o->len != 0);
273 : 22959 : case MP_UNARY_OP_LEN:
274 : 22959 : 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 : 4096 : 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 [ + - + + : 4096 : 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 : 4096 : mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in);
391 : :
392 [ + + ]: 4096 : if (self->free == 0) {
393 : 545 : size_t item_sz = mp_binary_get_size('@', self->typecode, NULL);
394 : : // TODO: alloc policy
395 : 545 : self->free = 8;
396 : 545 : self->items = m_renew(byte, self->items, item_sz * self->len, item_sz * (self->len + self->free));
397 : 544 : mp_seq_clear(self->items, self->len + 1, self->len + self->free, item_sz);
398 : : }
399 : 4095 : mp_binary_set_val_array(self->typecode, self->items, self->len, arg);
400 : : // only update length/free if set succeeded
401 : 4098 : self->len++;
402 : 4098 : self->free--;
403 : 4098 : return mp_const_none; // return None, as per CPython
404 : : }
405 : : MP_DEFINE_CONST_FUN_OBJ_2(mp_obj_array_append_obj, array_append);
406 : :
407 : 60 : static mp_obj_t array_extend(mp_obj_t self_in, mp_obj_t arg_in) {
408 : : // self is not a memoryview, so we don't need to use (& TYPECODE_MASK)
409 [ + - + + : 60 : assert((MICROPY_PY_BUILTINS_BYTEARRAY && mp_obj_is_type(self_in, &mp_type_bytearray))
+ - - + ]
410 : : || (MICROPY_PY_ARRAY && mp_obj_is_type(self_in, &mp_type_array)));
411 : 60 : mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in);
412 : :
413 : : // allow to extend by anything that has the buffer protocol (extension to CPython)
414 : 60 : mp_buffer_info_t arg_bufinfo;
415 : 60 : mp_get_buffer_raise(arg_in, &arg_bufinfo, MP_BUFFER_READ);
416 : :
417 : 60 : size_t sz = mp_binary_get_size('@', self->typecode, NULL);
418 : :
419 : : // convert byte count to element count
420 : 60 : size_t len = arg_bufinfo.len / sz;
421 : :
422 : : // make sure we have enough room to extend
423 : : // TODO: alloc policy; at the moment we go conservative
424 [ + + ]: 60 : if (self->free < len) {
425 : 56 : self->items = m_renew(byte, self->items, (self->len + self->free) * sz, (self->len + len) * sz);
426 : 48 : self->free = 0;
427 : :
428 [ + + ]: 48 : if (self_in == arg_in) {
429 : : // Get arg_bufinfo again in case self->items has moved
430 : : //
431 : : // (Note not possible to handle case that arg_in is a memoryview into self)
432 : 32 : mp_get_buffer_raise(arg_in, &arg_bufinfo, MP_BUFFER_READ);
433 : : }
434 : : } else {
435 : 4 : self->free -= len;
436 : : }
437 : :
438 : : // extend
439 : 52 : mp_seq_copy((byte *)self->items + self->len * sz, arg_bufinfo.buf, len * sz, byte);
440 : 52 : self->len += len;
441 : :
442 : 52 : return mp_const_none;
443 : : }
444 : : MP_DEFINE_CONST_FUN_OBJ_2(mp_obj_array_extend_obj, array_extend);
445 : : #endif
446 : :
447 : 16797898 : static mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) {
448 [ + + ]: 16797898 : if (value == MP_OBJ_NULL) {
449 : : // delete item
450 : : // TODO implement
451 : : // TODO: confirmed that both bytearray and array.array support
452 : : // slice deletion
453 : : return MP_OBJ_NULL; // op not supported
454 : : } else {
455 : 16797896 : mp_obj_array_t *o = MP_OBJ_TO_PTR(self_in);
456 : : #if MICROPY_PY_BUILTINS_SLICE
457 [ - + - + : 16797896 : if (mp_obj_is_type(index_in, &mp_type_slice)) {
- + - + +
+ + - ]
458 : 6752 : mp_bound_slice_t slice;
459 [ + + ]: 6752 : if (!mp_seq_get_fast_slice_indexes(o->len, index_in, &slice)) {
460 : 2 : mp_raise_NotImplementedError(MP_ERROR_TEXT("only slices with step=1 (aka None) are supported"));
461 : : }
462 [ + + ]: 6750 : if (value != MP_OBJ_SENTINEL) {
463 : : #if MICROPY_PY_ARRAY_SLICE_ASSIGN
464 : : // Assign
465 : 4036 : size_t src_len;
466 : 4036 : uint8_t *src_items;
467 : 4036 : size_t src_offs = 0;
468 : 4036 : size_t item_sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL);
469 [ + - + - : 4036 : 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) {
+ + ]
470 : : // value is array, bytearray or memoryview
471 : 2418 : mp_obj_array_t *src_slice = MP_OBJ_TO_PTR(value);
472 [ + + ]: 2418 : if (item_sz != mp_binary_get_size('@', src_slice->typecode & TYPECODE_MASK, NULL)) {
473 : 4 : compat_error:
474 : 20 : mp_raise_ValueError(MP_ERROR_TEXT("lhs and rhs should be compatible"));
475 : : }
476 : 2414 : src_len = src_slice->len;
477 : 2414 : src_items = src_slice->items;
478 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW
479 [ + - + + ]: 2414 : if (mp_obj_is_type(value, &mp_type_memoryview)) {
480 : 2332 : src_offs = src_slice->memview_offset * item_sz;
481 : : }
482 : : #endif
483 [ - + - + : 1618 : } else if (mp_obj_is_type(value, &mp_type_bytes)) {
- + - + +
- + + ]
484 [ + + ]: 1616 : if (item_sz != 1) {
485 : 4 : goto compat_error;
486 : : }
487 : 1612 : mp_buffer_info_t bufinfo;
488 : 1612 : mp_get_buffer_raise(value, &bufinfo, MP_BUFFER_READ);
489 : 1612 : src_len = bufinfo.len;
490 : 1612 : src_items = bufinfo.buf;
491 : : } else {
492 : 2 : mp_raise_NotImplementedError(MP_ERROR_TEXT("array/bytes required on right side"));
493 : : }
494 : :
495 : : // TODO: check src/dst compat
496 : 4026 : mp_int_t len_adj = src_len - (slice.stop - slice.start);
497 : 4026 : uint8_t *dest_items = o->items;
498 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW
499 [ + + ]: 4026 : if (o->base.type == &mp_type_memoryview) {
500 [ + + ]: 48 : if (!(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW)) {
501 : : // store to read-only memoryview not allowed
502 : : return MP_OBJ_NULL;
503 : : }
504 [ + + ]: 44 : if (len_adj != 0) {
505 : 12 : goto compat_error;
506 : : }
507 : 32 : dest_items += o->memview_offset * item_sz;
508 : : }
509 : : #endif
510 [ + + ]: 4010 : if (len_adj > 0) {
511 [ + + ]: 1632 : if ((size_t)len_adj > o->free) {
512 : : // TODO: alloc policy; at the moment we go conservative
513 : 1604 : o->items = m_renew(byte, o->items, (o->len + o->free) * item_sz, (o->len + len_adj) * item_sz);
514 : 1600 : o->free = len_adj;
515 : : // m_renew may have moved o->items
516 [ + + ]: 1600 : if (src_items == dest_items) {
517 : 16 : src_items = o->items;
518 : : }
519 : : dest_items = o->items;
520 : : }
521 : 1628 : mp_seq_replace_slice_grow_inplace(dest_items, o->len,
522 : 1628 : slice.start, slice.stop, src_items + src_offs, src_len, len_adj, item_sz);
523 : : } else {
524 : 2378 : mp_seq_replace_slice_no_grow(dest_items, o->len,
525 : 2378 : slice.start, slice.stop, src_items + src_offs, src_len, item_sz);
526 : : // Clear "freed" elements at the end of list
527 : : // TODO: This is actually only needed for typecode=='O'
528 : 2378 : mp_seq_clear(dest_items, o->len + len_adj, o->len, item_sz);
529 : : // TODO: alloc policy after shrinking
530 : : }
531 : 4006 : o->free -= len_adj;
532 : 4006 : o->len += len_adj;
533 : 4006 : return mp_const_none;
534 : : #else
535 : : return MP_OBJ_NULL; // op not supported
536 : : #endif
537 : : }
538 : :
539 : 2714 : mp_obj_array_t *res;
540 : 2714 : size_t sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL);
541 [ - + ]: 2714 : assert(sz > 0);
542 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW
543 [ + + ]: 2714 : if (o->base.type == &mp_type_memoryview) {
544 [ + + ]: 2434 : if (slice.start > memview_offset_max) {
545 : 4 : mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("memoryview offset too large"));
546 : : }
547 : 2430 : res = m_new_obj(mp_obj_array_t);
548 : 2426 : *res = *o;
549 : 2426 : res->memview_offset += slice.start;
550 : 2426 : res->len = slice.stop - slice.start;
551 : : } else
552 : : #endif
553 : : {
554 : 280 : res = array_new(o->typecode, slice.stop - slice.start);
555 : 276 : memcpy(res->items, (uint8_t *)o->items + slice.start * sz, (slice.stop - slice.start) * sz);
556 : : }
557 : 2702 : return MP_OBJ_FROM_PTR(res);
558 : : } else
559 : : #endif
560 : : {
561 : 16791144 : size_t index = mp_get_index(o->base.type, o->len, index_in, false);
562 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW
563 [ + + ]: 18142907 : if (o->base.type == &mp_type_memoryview) {
564 : 66 : index += o->memview_offset;
565 [ + + + + ]: 66 : if (value != MP_OBJ_SENTINEL && !(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW)) {
566 : : // store to read-only memoryview
567 : : return MP_OBJ_NULL;
568 : : }
569 : : }
570 : : #endif
571 [ + + ]: 18142885 : if (value == MP_OBJ_SENTINEL) {
572 : : // load
573 : 10611116 : return mp_binary_get_val_array(o->typecode & TYPECODE_MASK, o->items, index);
574 : : } else {
575 : : // store
576 : 7531787 : mp_binary_set_val_array(o->typecode & TYPECODE_MASK, o->items, index, value);
577 : 7531787 : return mp_const_none;
578 : : }
579 : : }
580 : : }
581 : : }
582 : :
583 : 3404 : static mp_int_t array_get_buffer(mp_obj_t o_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
584 : 3404 : mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in);
585 : 3404 : size_t sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL);
586 : 3404 : bufinfo->buf = o->items;
587 : 3404 : bufinfo->len = o->len * sz;
588 : 3404 : bufinfo->typecode = o->typecode & TYPECODE_MASK;
589 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW
590 [ + + ]: 3404 : if (o->base.type == &mp_type_memoryview) {
591 [ + + + + ]: 140 : if (!(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW) && (flags & MP_BUFFER_WRITE)) {
592 : : // read-only memoryview
593 : : return 1;
594 : : }
595 : 136 : bufinfo->buf = (uint8_t *)bufinfo->buf + (size_t)o->memview_offset * sz;
596 : : }
597 : : #else
598 : : (void)flags;
599 : : #endif
600 : : return 0;
601 : : }
602 : :
603 : : #if MICROPY_PY_ARRAY
604 : : MP_DEFINE_CONST_OBJ_TYPE(
605 : : mp_type_array,
606 : : MP_QSTR_array,
607 : : MP_TYPE_FLAG_ITER_IS_GETITER,
608 : : make_new, array_make_new,
609 : : print, array_print,
610 : : iter, array_iterator_new,
611 : : unary_op, array_unary_op,
612 : : binary_op, array_binary_op,
613 : : subscr, array_subscr,
614 : : buffer, array_get_buffer,
615 : : locals_dict, &mp_obj_array_locals_dict
616 : : );
617 : : #endif
618 : :
619 : : #if MICROPY_PY_BUILTINS_BYTEARRAY
620 : : MP_DEFINE_CONST_OBJ_TYPE(
621 : : mp_type_bytearray,
622 : : MP_QSTR_bytearray,
623 : : MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_ITER_IS_GETITER,
624 : : make_new, bytearray_make_new,
625 : : print, array_print,
626 : : iter, array_iterator_new,
627 : : unary_op, array_unary_op,
628 : : binary_op, array_binary_op,
629 : : subscr, array_subscr,
630 : : buffer, array_get_buffer,
631 : : locals_dict, &mp_obj_bytearray_locals_dict
632 : : );
633 : : #endif
634 : :
635 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW
636 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE
637 : : #define MEMORYVIEW_TYPE_ATTR attr, memoryview_attr,
638 : : #else
639 : : #define MEMORYVIEW_TYPE_ATTR
640 : : #endif
641 : :
642 : : #if MICROPY_PY_BUILTINS_BYTES_HEX
643 : : #define MEMORYVIEW_TYPE_LOCALS_DICT locals_dict, &mp_obj_memoryview_locals_dict,
644 : : #else
645 : : #define MEMORYVIEW_TYPE_LOCALS_DICT
646 : : #endif
647 : :
648 : : MP_DEFINE_CONST_OBJ_TYPE(
649 : : mp_type_memoryview,
650 : : MP_QSTR_memoryview,
651 : : MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_ITER_IS_GETITER,
652 : : make_new, memoryview_make_new,
653 : : iter, array_iterator_new,
654 : : unary_op, array_unary_op,
655 : : binary_op, array_binary_op,
656 : : MEMORYVIEW_TYPE_LOCALS_DICT
657 : : MEMORYVIEW_TYPE_ATTR
658 : : subscr, array_subscr,
659 : : buffer, array_get_buffer
660 : : );
661 : : #endif // MICROPY_PY_BUILTINS_MEMORYVIEW
662 : :
663 : : /* unused
664 : : size_t mp_obj_array_len(mp_obj_t self_in) {
665 : : return ((mp_obj_array_t *)self_in)->len;
666 : : }
667 : : */
668 : :
669 : : #if MICROPY_PY_BUILTINS_BYTEARRAY
670 : 102 : mp_obj_t mp_obj_new_bytearray(size_t n, const void *items) {
671 : 102 : mp_obj_array_t *o = array_new(BYTEARRAY_TYPECODE, n);
672 : 102 : memcpy(o->items, items, n);
673 : 102 : return MP_OBJ_FROM_PTR(o);
674 : : }
675 : :
676 : : // Create bytearray which references specified memory area
677 : 176 : mp_obj_t mp_obj_new_bytearray_by_ref(size_t n, void *items) {
678 : 176 : mp_obj_array_t *o = mp_obj_malloc(mp_obj_array_t, &mp_type_bytearray);
679 : 176 : o->typecode = BYTEARRAY_TYPECODE;
680 : 176 : o->free = 0;
681 : 176 : o->len = n;
682 : 176 : o->items = items;
683 : 176 : return MP_OBJ_FROM_PTR(o);
684 : : }
685 : : #endif
686 : :
687 : : /******************************************************************************/
688 : : // array iterator
689 : :
690 : : typedef struct _mp_obj_array_it_t {
691 : : mp_obj_base_t base;
692 : : mp_obj_array_t *array;
693 : : size_t offset;
694 : : size_t cur;
695 : : } mp_obj_array_it_t;
696 : :
697 : 4643 : static mp_obj_t array_it_iternext(mp_obj_t self_in) {
698 : 4643 : mp_obj_array_it_t *self = MP_OBJ_TO_PTR(self_in);
699 [ + + ]: 4643 : if (self->cur < self->array->len) {
700 : 4524 : return mp_binary_get_val_array(self->array->typecode & TYPECODE_MASK, self->array->items, self->offset + self->cur++);
701 : : } else {
702 : : return MP_OBJ_STOP_ITERATION;
703 : : }
704 : : }
705 : :
706 : : static MP_DEFINE_CONST_OBJ_TYPE(
707 : : mp_type_array_it,
708 : : MP_QSTR_iterator,
709 : : MP_TYPE_FLAG_ITER_IS_ITERNEXT,
710 : : iter, array_it_iternext
711 : : );
712 : :
713 : 119 : static mp_obj_t array_iterator_new(mp_obj_t array_in, mp_obj_iter_buf_t *iter_buf) {
714 : 119 : assert(sizeof(mp_obj_array_t) <= sizeof(mp_obj_iter_buf_t));
715 : 119 : mp_obj_array_t *array = MP_OBJ_TO_PTR(array_in);
716 : 119 : mp_obj_array_it_t *o = (mp_obj_array_it_t *)iter_buf;
717 : 119 : o->base.type = &mp_type_array_it;
718 : 119 : o->array = array;
719 : 119 : o->offset = 0;
720 : 119 : o->cur = 0;
721 : : #if MICROPY_PY_BUILTINS_MEMORYVIEW
722 [ + + ]: 119 : if (array->base.type == &mp_type_memoryview) {
723 : 76 : o->offset = array->memview_offset;
724 : : }
725 : : #endif
726 : 119 : return MP_OBJ_FROM_PTR(o);
727 : : }
728 : :
729 : : #endif // MICROPY_PY_ARRAY || MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_BUILTINS_MEMORYVIEW
|