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 #else
57 // make (& TYPECODE_MASK) a null operation if memorview not enabled
58 #define TYPECODE_MASK (~(size_t)0)
59 // memview_offset should not be accessed if memoryview is not enabled,
60 // so not defined to catch errors
61 #endif
62 
63 STATIC mp_obj_t array_iterator_new(mp_obj_t array_in, mp_obj_iter_buf_t *iter_buf);
64 STATIC mp_obj_t array_append(mp_obj_t self_in, mp_obj_t arg);
65 STATIC mp_obj_t array_extend(mp_obj_t self_in, mp_obj_t arg_in);
66 STATIC mp_int_t array_get_buffer(mp_obj_t o_in, mp_buffer_info_t *bufinfo, mp_uint_t flags);
67 
68 /******************************************************************************/
69 // array
70 
71 #if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY
array_print(const mp_print_t * print,mp_obj_t o_in,mp_print_kind_t kind)72 STATIC void array_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
73     (void)kind;
74     mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in);
75     if (o->typecode == BYTEARRAY_TYPECODE) {
76         mp_print_str(print, "bytearray(b");
77         mp_str_print_quoted(print, o->items, o->len, true);
78     } else {
79         mp_printf(print, "array('%c'", o->typecode);
80         if (o->len > 0) {
81             mp_print_str(print, ", [");
82             for (size_t i = 0; i < o->len; i++) {
83                 if (i > 0) {
84                     mp_print_str(print, ", ");
85                 }
86                 mp_obj_print_helper(print, mp_binary_get_val_array(o->typecode, o->items, i), PRINT_REPR);
87             }
88             mp_print_str(print, "]");
89         }
90     }
91     mp_print_str(print, ")");
92 }
93 #endif
94 
95 #if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY
array_new(char typecode,size_t n)96 STATIC mp_obj_array_t *array_new(char typecode, size_t n) {
97     int typecode_size = mp_binary_get_size('@', typecode, NULL);
98     mp_obj_array_t *o = m_new_obj(mp_obj_array_t);
99     #if MICROPY_PY_BUILTINS_BYTEARRAY && MICROPY_PY_ARRAY
100     o->base.type = (typecode == BYTEARRAY_TYPECODE) ? &mp_type_bytearray : &mp_type_array;
101     #elif MICROPY_PY_BUILTINS_BYTEARRAY
102     o->base.type = &mp_type_bytearray;
103     #else
104     o->base.type = &mp_type_array;
105     #endif
106     o->typecode = typecode;
107     o->free = 0;
108     o->len = n;
109     o->items = m_new(byte, typecode_size * o->len);
110     return o;
111 }
112 #endif
113 
114 #if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY
array_construct(char typecode,mp_obj_t initializer)115 STATIC mp_obj_t array_construct(char typecode, mp_obj_t initializer) {
116     // bytearrays can be raw-initialised from anything with the buffer protocol
117     // other arrays can only be raw-initialised from bytes and bytearray objects
118     mp_buffer_info_t bufinfo;
119     if (((MICROPY_PY_BUILTINS_BYTEARRAY
120           && typecode == BYTEARRAY_TYPECODE)
121          || (MICROPY_PY_ARRAY
122              && (mp_obj_is_type(initializer, &mp_type_bytes)
123                  || (MICROPY_PY_BUILTINS_BYTEARRAY && mp_obj_is_type(initializer, &mp_type_bytearray)))))
124         && mp_get_buffer(initializer, &bufinfo, MP_BUFFER_READ)) {
125         // construct array from raw bytes
126         // we round-down the len to make it a multiple of sz (CPython raises error)
127         size_t sz = mp_binary_get_size('@', typecode, NULL);
128         size_t len = bufinfo.len / sz;
129         mp_obj_array_t *o = array_new(typecode, len);
130         memcpy(o->items, bufinfo.buf, len * sz);
131         return MP_OBJ_FROM_PTR(o);
132     }
133 
134     size_t len;
135     // Try to create array of exact len if initializer len is known
136     mp_obj_t len_in = mp_obj_len_maybe(initializer);
137     if (len_in == MP_OBJ_NULL) {
138         len = 0;
139     } else {
140         len = MP_OBJ_SMALL_INT_VALUE(len_in);
141     }
142 
143     mp_obj_array_t *array = array_new(typecode, len);
144 
145     mp_obj_t iterable = mp_getiter(initializer, NULL);
146     mp_obj_t item;
147     size_t i = 0;
148     while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
149         if (len == 0) {
150             array_append(MP_OBJ_FROM_PTR(array), item);
151         } else {
152             mp_binary_set_val_array(typecode, array->items, i++, item);
153         }
154     }
155 
156     return MP_OBJ_FROM_PTR(array);
157 }
158 #endif
159 
160 #if MICROPY_PY_ARRAY
array_make_new(const mp_obj_type_t * type_in,size_t n_args,size_t n_kw,const mp_obj_t * args)161 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) {
162     (void)type_in;
163     mp_arg_check_num(n_args, n_kw, 1, 2, false);
164 
165     // get typecode
166     const char *typecode = mp_obj_str_get_str(args[0]);
167 
168     if (n_args == 1) {
169         // 1 arg: make an empty array
170         return MP_OBJ_FROM_PTR(array_new(*typecode, 0));
171     } else {
172         // 2 args: construct the array from the given object
173         return array_construct(*typecode, args[1]);
174     }
175 }
176 #endif
177 
178 #if MICROPY_PY_BUILTINS_BYTEARRAY
bytearray_make_new(const mp_obj_type_t * type_in,size_t n_args,size_t n_kw,const mp_obj_t * args)179 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) {
180     (void)type_in;
181     // Can take 2nd/3rd arg if constructs from str
182     mp_arg_check_num(n_args, n_kw, 0, 3, false);
183 
184     if (n_args == 0) {
185         // no args: construct an empty bytearray
186         return MP_OBJ_FROM_PTR(array_new(BYTEARRAY_TYPECODE, 0));
187     } else if (mp_obj_is_int(args[0])) {
188         // 1 arg, an integer: construct a blank bytearray of that length
189         mp_uint_t len = mp_obj_get_int(args[0]);
190         mp_obj_array_t *o = array_new(BYTEARRAY_TYPECODE, len);
191         memset(o->items, 0, len);
192         return MP_OBJ_FROM_PTR(o);
193     } else {
194         // 1 arg: construct the bytearray from that
195         return array_construct(BYTEARRAY_TYPECODE, args[0]);
196     }
197 }
198 #endif
199 
200 #if MICROPY_PY_BUILTINS_MEMORYVIEW
201 
mp_obj_new_memoryview(byte typecode,size_t nitems,void * items)202 mp_obj_t mp_obj_new_memoryview(byte typecode, size_t nitems, void *items) {
203     mp_obj_array_t *self = m_new_obj(mp_obj_array_t);
204     mp_obj_memoryview_init(self, typecode, 0, nitems, items);
205     return MP_OBJ_FROM_PTR(self);
206 }
207 
memoryview_make_new(const mp_obj_type_t * type_in,size_t n_args,size_t n_kw,const mp_obj_t * args)208 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) {
209     (void)type_in;
210 
211     // TODO possibly allow memoryview constructor to take start/stop so that one
212     // can do memoryview(b, 4, 8) instead of memoryview(b)[4:8] (uses less RAM)
213 
214     mp_arg_check_num(n_args, n_kw, 1, 1, false);
215 
216     mp_buffer_info_t bufinfo;
217     mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);
218 
219     mp_obj_array_t *self = MP_OBJ_TO_PTR(mp_obj_new_memoryview(bufinfo.typecode,
220         bufinfo.len / mp_binary_get_size('@', bufinfo.typecode, NULL),
221         bufinfo.buf));
222 
223     // If the input object is a memoryview then need to point the items of the
224     // new memoryview to the start of the buffer so the GC can trace it.
225     if (mp_obj_get_type(args[0]) == &mp_type_memoryview) {
226         mp_obj_array_t *other = MP_OBJ_TO_PTR(args[0]);
227         self->memview_offset = other->memview_offset;
228         self->items = other->items;
229     }
230 
231     // test if the object can be written to
232     if (mp_get_buffer(args[0], &bufinfo, MP_BUFFER_RW)) {
233         self->typecode |= MP_OBJ_ARRAY_TYPECODE_FLAG_RW; // indicate writable buffer
234     }
235 
236     return MP_OBJ_FROM_PTR(self);
237 }
238 
239 #if MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE
memoryview_attr(mp_obj_t self_in,qstr attr,mp_obj_t * dest)240 STATIC void memoryview_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
241     if (dest[0] != MP_OBJ_NULL) {
242         return;
243     }
244     if (attr == MP_QSTR_itemsize) {
245         mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in);
246         dest[0] = MP_OBJ_NEW_SMALL_INT(mp_binary_get_size('@', self->typecode & TYPECODE_MASK, NULL));
247     }
248 }
249 #endif
250 
251 #endif
252 
array_unary_op(mp_unary_op_t op,mp_obj_t o_in)253 STATIC mp_obj_t array_unary_op(mp_unary_op_t op, mp_obj_t o_in) {
254     mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in);
255     switch (op) {
256         case MP_UNARY_OP_BOOL:
257             return mp_obj_new_bool(o->len != 0);
258         case MP_UNARY_OP_LEN:
259             return MP_OBJ_NEW_SMALL_INT(o->len);
260         default:
261             return MP_OBJ_NULL;      // op not supported
262     }
263 }
264 
typecode_for_comparison(int typecode,bool * is_unsigned)265 STATIC int typecode_for_comparison(int typecode, bool *is_unsigned) {
266     if (typecode == BYTEARRAY_TYPECODE) {
267         typecode = 'B';
268     }
269     if (typecode <= 'Z') {
270         typecode += 32; // to lowercase
271         *is_unsigned = true;
272     }
273     return typecode;
274 }
275 
array_binary_op(mp_binary_op_t op,mp_obj_t lhs_in,mp_obj_t rhs_in)276 STATIC mp_obj_t array_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
277     mp_obj_array_t *lhs = MP_OBJ_TO_PTR(lhs_in);
278     switch (op) {
279         case MP_BINARY_OP_ADD: {
280             // allow to add anything that has the buffer protocol (extension to CPython)
281             mp_buffer_info_t lhs_bufinfo;
282             mp_buffer_info_t rhs_bufinfo;
283             array_get_buffer(lhs_in, &lhs_bufinfo, MP_BUFFER_READ);
284             mp_get_buffer_raise(rhs_in, &rhs_bufinfo, MP_BUFFER_READ);
285 
286             size_t sz = mp_binary_get_size('@', lhs_bufinfo.typecode, NULL);
287 
288             // convert byte count to element count (in case rhs is not multiple of sz)
289             size_t rhs_len = rhs_bufinfo.len / sz;
290 
291             // note: lhs->len is element count of lhs, lhs_bufinfo.len is byte count
292             mp_obj_array_t *res = array_new(lhs_bufinfo.typecode, lhs->len + rhs_len);
293             mp_seq_cat((byte *)res->items, lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_len * sz, byte);
294             return MP_OBJ_FROM_PTR(res);
295         }
296 
297         case MP_BINARY_OP_INPLACE_ADD: {
298             #if MICROPY_PY_BUILTINS_MEMORYVIEW
299             if (lhs->base.type == &mp_type_memoryview) {
300                 return MP_OBJ_NULL; // op not supported
301             }
302             #endif
303             array_extend(lhs_in, rhs_in);
304             return lhs_in;
305         }
306 
307         case MP_BINARY_OP_CONTAINS: {
308             #if MICROPY_PY_BUILTINS_BYTEARRAY
309             // Can search string only in bytearray
310             mp_buffer_info_t lhs_bufinfo;
311             mp_buffer_info_t rhs_bufinfo;
312             if (mp_get_buffer(rhs_in, &rhs_bufinfo, MP_BUFFER_READ)) {
313                 if (!mp_obj_is_type(lhs_in, &mp_type_bytearray)) {
314                     return mp_const_false;
315                 }
316                 array_get_buffer(lhs_in, &lhs_bufinfo, MP_BUFFER_READ);
317                 return mp_obj_new_bool(
318                     find_subbytes(lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_bufinfo.len, 1) != NULL);
319             }
320             #endif
321 
322             // Otherwise, can only look for a scalar numeric value in an array
323             if (mp_obj_is_int(rhs_in) || mp_obj_is_float(rhs_in)) {
324                 mp_raise_NotImplementedError(NULL);
325             }
326 
327             return mp_const_false;
328         }
329 
330         case MP_BINARY_OP_EQUAL:
331         case MP_BINARY_OP_LESS:
332         case MP_BINARY_OP_LESS_EQUAL:
333         case MP_BINARY_OP_MORE:
334         case MP_BINARY_OP_MORE_EQUAL: {
335             mp_buffer_info_t lhs_bufinfo;
336             mp_buffer_info_t rhs_bufinfo;
337             array_get_buffer(lhs_in, &lhs_bufinfo, MP_BUFFER_READ);
338             if (!mp_get_buffer(rhs_in, &rhs_bufinfo, MP_BUFFER_READ)) {
339                 return mp_const_false;
340             }
341             // mp_seq_cmp_bytes is used so only compatible representations can be correctly compared.
342             // The type doesn't matter: array/bytearray/str/bytes all have the same buffer layout, so
343             // just check if the typecodes are compatible; for testing equality the types should have the
344             // same code except for signedness, and not be floating point because nan never equals nan.
345             // For > and < the types should be the same and unsigned.
346             // Note that typecode_for_comparison always returns lowercase letters to save code size.
347             // No need for (& TYPECODE_MASK) here: xxx_get_buffer already takes care of that.
348             bool is_unsigned = false;
349             const int lhs_code = typecode_for_comparison(lhs_bufinfo.typecode, &is_unsigned);
350             const int rhs_code = typecode_for_comparison(rhs_bufinfo.typecode, &is_unsigned);
351             if (lhs_code == rhs_code && lhs_code != 'f' && lhs_code != 'd' && (op == MP_BINARY_OP_EQUAL || is_unsigned)) {
352                 return mp_obj_new_bool(mp_seq_cmp_bytes(op, lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_bufinfo.len));
353             }
354             // mp_obj_equal_not_equal treats returning MP_OBJ_NULL as 'fall back to pointer comparison'
355             // for MP_BINARY_OP_EQUAL but that is incompatible with CPython.
356             mp_raise_NotImplementedError(NULL);
357         }
358 
359         default:
360             return MP_OBJ_NULL; // op not supported
361     }
362 }
363 
364 #if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY
array_append(mp_obj_t self_in,mp_obj_t arg)365 STATIC mp_obj_t array_append(mp_obj_t self_in, mp_obj_t arg) {
366     // self is not a memoryview, so we don't need to use (& TYPECODE_MASK)
367     assert((MICROPY_PY_BUILTINS_BYTEARRAY && mp_obj_is_type(self_in, &mp_type_bytearray))
368         || (MICROPY_PY_ARRAY && mp_obj_is_type(self_in, &mp_type_array)));
369     mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in);
370 
371     if (self->free == 0) {
372         size_t item_sz = mp_binary_get_size('@', self->typecode, NULL);
373         // TODO: alloc policy
374         self->free = 8;
375         self->items = m_renew(byte, self->items, item_sz * self->len, item_sz * (self->len + self->free));
376         mp_seq_clear(self->items, self->len + 1, self->len + self->free, item_sz);
377     }
378     mp_binary_set_val_array(self->typecode, self->items, self->len, arg);
379     // only update length/free if set succeeded
380     self->len++;
381     self->free--;
382     return mp_const_none; // return None, as per CPython
383 }
384 STATIC MP_DEFINE_CONST_FUN_OBJ_2(array_append_obj, array_append);
385 
array_extend(mp_obj_t self_in,mp_obj_t arg_in)386 STATIC mp_obj_t array_extend(mp_obj_t self_in, mp_obj_t arg_in) {
387     // self is not a memoryview, so we don't need to use (& TYPECODE_MASK)
388     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     mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in);
391 
392     // allow to extend by anything that has the buffer protocol (extension to CPython)
393     mp_buffer_info_t arg_bufinfo;
394     mp_get_buffer_raise(arg_in, &arg_bufinfo, MP_BUFFER_READ);
395 
396     size_t sz = mp_binary_get_size('@', self->typecode, NULL);
397 
398     // convert byte count to element count
399     size_t len = arg_bufinfo.len / sz;
400 
401     // make sure we have enough room to extend
402     // TODO: alloc policy; at the moment we go conservative
403     if (self->free < len) {
404         self->items = m_renew(byte, self->items, (self->len + self->free) * sz, (self->len + len) * sz);
405         self->free = 0;
406     } else {
407         self->free -= len;
408     }
409 
410     // extend
411     mp_seq_copy((byte *)self->items + self->len * sz, arg_bufinfo.buf, len * sz, byte);
412     self->len += len;
413 
414     return mp_const_none;
415 }
416 STATIC MP_DEFINE_CONST_FUN_OBJ_2(array_extend_obj, array_extend);
417 #endif
418 
array_subscr(mp_obj_t self_in,mp_obj_t index_in,mp_obj_t value)419 STATIC mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) {
420     if (value == MP_OBJ_NULL) {
421         // delete item
422         // TODO implement
423         // TODO: confirmed that both bytearray and array.array support
424         // slice deletion
425         return MP_OBJ_NULL; // op not supported
426     } else {
427         mp_obj_array_t *o = MP_OBJ_TO_PTR(self_in);
428         #if MICROPY_PY_BUILTINS_SLICE
429         if (mp_obj_is_type(index_in, &mp_type_slice)) {
430             mp_bound_slice_t slice;
431             if (!mp_seq_get_fast_slice_indexes(o->len, index_in, &slice)) {
432                 mp_raise_NotImplementedError(MP_ERROR_TEXT("only slices with step=1 (aka None) are supported"));
433             }
434             if (value != MP_OBJ_SENTINEL) {
435                 #if MICROPY_PY_ARRAY_SLICE_ASSIGN
436                 // Assign
437                 size_t src_len;
438                 void *src_items;
439                 size_t item_sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL);
440                 if (mp_obj_is_obj(value) && ((mp_obj_base_t *)MP_OBJ_TO_PTR(value))->type->subscr == array_subscr) {
441                     // value is array, bytearray or memoryview
442                     mp_obj_array_t *src_slice = MP_OBJ_TO_PTR(value);
443                     if (item_sz != mp_binary_get_size('@', src_slice->typecode & TYPECODE_MASK, NULL)) {
444                     compat_error:
445                         mp_raise_ValueError(MP_ERROR_TEXT("lhs and rhs should be compatible"));
446                     }
447                     src_len = src_slice->len;
448                     src_items = src_slice->items;
449                     #if MICROPY_PY_BUILTINS_MEMORYVIEW
450                     if (mp_obj_is_type(value, &mp_type_memoryview)) {
451                         src_items = (uint8_t *)src_items + (src_slice->memview_offset * item_sz);
452                     }
453                     #endif
454                 } else if (mp_obj_is_type(value, &mp_type_bytes)) {
455                     if (item_sz != 1) {
456                         goto compat_error;
457                     }
458                     mp_buffer_info_t bufinfo;
459                     mp_get_buffer_raise(value, &bufinfo, MP_BUFFER_READ);
460                     src_len = bufinfo.len;
461                     src_items = bufinfo.buf;
462                 } else {
463                     mp_raise_NotImplementedError(MP_ERROR_TEXT("array/bytes required on right side"));
464                 }
465 
466                 // TODO: check src/dst compat
467                 mp_int_t len_adj = src_len - (slice.stop - slice.start);
468                 uint8_t *dest_items = o->items;
469                 #if MICROPY_PY_BUILTINS_MEMORYVIEW
470                 if (o->base.type == &mp_type_memoryview) {
471                     if (!(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW)) {
472                         // store to read-only memoryview not allowed
473                         return MP_OBJ_NULL;
474                     }
475                     if (len_adj != 0) {
476                         goto compat_error;
477                     }
478                     dest_items += o->memview_offset * item_sz;
479                 }
480                 #endif
481                 if (len_adj > 0) {
482                     if ((size_t)len_adj > o->free) {
483                         // TODO: alloc policy; at the moment we go conservative
484                         o->items = m_renew(byte, o->items, (o->len + o->free) * item_sz, (o->len + len_adj) * item_sz);
485                         o->free = len_adj;
486                         dest_items = o->items;
487                     }
488                     mp_seq_replace_slice_grow_inplace(dest_items, o->len,
489                         slice.start, slice.stop, src_items, src_len, len_adj, item_sz);
490                 } else {
491                     mp_seq_replace_slice_no_grow(dest_items, o->len,
492                         slice.start, slice.stop, src_items, src_len, item_sz);
493                     // Clear "freed" elements at the end of list
494                     // TODO: This is actually only needed for typecode=='O'
495                     mp_seq_clear(dest_items, o->len + len_adj, o->len, item_sz);
496                     // TODO: alloc policy after shrinking
497                 }
498                 o->free -= len_adj;
499                 o->len += len_adj;
500                 return mp_const_none;
501                 #else
502                 return MP_OBJ_NULL; // op not supported
503                 #endif
504             }
505 
506             mp_obj_array_t *res;
507             size_t sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL);
508             assert(sz > 0);
509             #if MICROPY_PY_BUILTINS_MEMORYVIEW
510             if (o->base.type == &mp_type_memoryview) {
511                 res = m_new_obj(mp_obj_array_t);
512                 *res = *o;
513                 res->memview_offset += slice.start;
514                 res->len = slice.stop - slice.start;
515             } else
516             #endif
517             {
518                 res = array_new(o->typecode, slice.stop - slice.start);
519                 memcpy(res->items, (uint8_t *)o->items + slice.start * sz, (slice.stop - slice.start) * sz);
520             }
521             return MP_OBJ_FROM_PTR(res);
522         } else
523         #endif
524         {
525             size_t index = mp_get_index(o->base.type, o->len, index_in, false);
526             #if MICROPY_PY_BUILTINS_MEMORYVIEW
527             if (o->base.type == &mp_type_memoryview) {
528                 index += o->memview_offset;
529                 if (value != MP_OBJ_SENTINEL && !(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW)) {
530                     // store to read-only memoryview
531                     return MP_OBJ_NULL;
532                 }
533             }
534             #endif
535             if (value == MP_OBJ_SENTINEL) {
536                 // load
537                 return mp_binary_get_val_array(o->typecode & TYPECODE_MASK, o->items, index);
538             } else {
539                 // store
540                 mp_binary_set_val_array(o->typecode & TYPECODE_MASK, o->items, index, value);
541                 return mp_const_none;
542             }
543         }
544     }
545 }
546 
array_get_buffer(mp_obj_t o_in,mp_buffer_info_t * bufinfo,mp_uint_t flags)547 STATIC mp_int_t array_get_buffer(mp_obj_t o_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
548     mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in);
549     size_t sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL);
550     bufinfo->buf = o->items;
551     bufinfo->len = o->len * sz;
552     bufinfo->typecode = o->typecode & TYPECODE_MASK;
553     #if MICROPY_PY_BUILTINS_MEMORYVIEW
554     if (o->base.type == &mp_type_memoryview) {
555         if (!(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW) && (flags & MP_BUFFER_WRITE)) {
556             // read-only memoryview
557             return 1;
558         }
559         bufinfo->buf = (uint8_t *)bufinfo->buf + (size_t)o->memview_offset * sz;
560     }
561     #else
562     (void)flags;
563     #endif
564     return 0;
565 }
566 
567 #if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY
568 STATIC const mp_rom_map_elem_t array_locals_dict_table[] = {
569     { MP_ROM_QSTR(MP_QSTR_append), MP_ROM_PTR(&array_append_obj) },
570     { MP_ROM_QSTR(MP_QSTR_extend), MP_ROM_PTR(&array_extend_obj) },
571     #if MICROPY_CPYTHON_COMPAT
572     { MP_ROM_QSTR(MP_QSTR_decode), MP_ROM_PTR(&bytes_decode_obj) },
573     #endif
574 };
575 
576 STATIC MP_DEFINE_CONST_DICT(array_locals_dict, array_locals_dict_table);
577 #endif
578 
579 #if MICROPY_PY_ARRAY
580 const mp_obj_type_t mp_type_array = {
581     { &mp_type_type },
582     .name = MP_QSTR_array,
583     .print = array_print,
584     .make_new = array_make_new,
585     .getiter = array_iterator_new,
586     .unary_op = array_unary_op,
587     .binary_op = array_binary_op,
588     .subscr = array_subscr,
589     .buffer_p = { .get_buffer = array_get_buffer },
590     .locals_dict = (mp_obj_dict_t *)&array_locals_dict,
591 };
592 #endif
593 
594 #if MICROPY_PY_BUILTINS_BYTEARRAY
595 const mp_obj_type_t mp_type_bytearray = {
596     { &mp_type_type },
597     .flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE,
598     .name = MP_QSTR_bytearray,
599     .print = array_print,
600     .make_new = bytearray_make_new,
601     .getiter = array_iterator_new,
602     .unary_op = array_unary_op,
603     .binary_op = array_binary_op,
604     .subscr = array_subscr,
605     .buffer_p = { .get_buffer = array_get_buffer },
606     .locals_dict = (mp_obj_dict_t *)&array_locals_dict,
607 };
608 #endif
609 
610 #if MICROPY_PY_BUILTINS_MEMORYVIEW
611 const mp_obj_type_t mp_type_memoryview = {
612     { &mp_type_type },
613     .flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE,
614     .name = MP_QSTR_memoryview,
615     .make_new = memoryview_make_new,
616     .getiter = array_iterator_new,
617     .unary_op = array_unary_op,
618     .binary_op = array_binary_op,
619     #if MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE
620     .attr = memoryview_attr,
621     #endif
622     .subscr = array_subscr,
623     .buffer_p = { .get_buffer = array_get_buffer },
624 };
625 #endif
626 
627 /* unused
628 size_t mp_obj_array_len(mp_obj_t self_in) {
629     return ((mp_obj_array_t *)self_in)->len;
630 }
631 */
632 
633 #if MICROPY_PY_BUILTINS_BYTEARRAY
mp_obj_new_bytearray(size_t n,void * items)634 mp_obj_t mp_obj_new_bytearray(size_t n, void *items) {
635     mp_obj_array_t *o = array_new(BYTEARRAY_TYPECODE, n);
636     memcpy(o->items, items, n);
637     return MP_OBJ_FROM_PTR(o);
638 }
639 
640 // Create bytearray which references specified memory area
mp_obj_new_bytearray_by_ref(size_t n,void * items)641 mp_obj_t mp_obj_new_bytearray_by_ref(size_t n, void *items) {
642     mp_obj_array_t *o = m_new_obj(mp_obj_array_t);
643     o->base.type = &mp_type_bytearray;
644     o->typecode = BYTEARRAY_TYPECODE;
645     o->free = 0;
646     o->len = n;
647     o->items = items;
648     return MP_OBJ_FROM_PTR(o);
649 }
650 #endif
651 
652 /******************************************************************************/
653 // array iterator
654 
655 typedef struct _mp_obj_array_it_t {
656     mp_obj_base_t base;
657     mp_obj_array_t *array;
658     size_t offset;
659     size_t cur;
660 } mp_obj_array_it_t;
661 
array_it_iternext(mp_obj_t self_in)662 STATIC mp_obj_t array_it_iternext(mp_obj_t self_in) {
663     mp_obj_array_it_t *self = MP_OBJ_TO_PTR(self_in);
664     if (self->cur < self->array->len) {
665         return mp_binary_get_val_array(self->array->typecode & TYPECODE_MASK, self->array->items, self->offset + self->cur++);
666     } else {
667         return MP_OBJ_STOP_ITERATION;
668     }
669 }
670 
671 STATIC const mp_obj_type_t mp_type_array_it = {
672     { &mp_type_type },
673     .name = MP_QSTR_iterator,
674     .getiter = mp_identity_getiter,
675     .iternext = array_it_iternext,
676 };
677 
array_iterator_new(mp_obj_t array_in,mp_obj_iter_buf_t * iter_buf)678 STATIC mp_obj_t array_iterator_new(mp_obj_t array_in, mp_obj_iter_buf_t *iter_buf) {
679     assert(sizeof(mp_obj_array_t) <= sizeof(mp_obj_iter_buf_t));
680     mp_obj_array_t *array = MP_OBJ_TO_PTR(array_in);
681     mp_obj_array_it_t *o = (mp_obj_array_it_t *)iter_buf;
682     o->base.type = &mp_type_array_it;
683     o->array = array;
684     o->offset = 0;
685     o->cur = 0;
686     #if MICROPY_PY_BUILTINS_MEMORYVIEW
687     if (array->base.type == &mp_type_memoryview) {
688         o->offset = array->memview_offset;
689     }
690     #endif
691     return MP_OBJ_FROM_PTR(o);
692 }
693 
694 #endif // MICROPY_PY_ARRAY || MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_BUILTINS_MEMORYVIEW
695