LCOV - code coverage report
Current view: top level - py - objarray.c (source / functions) Hit Total Coverage
Test: unix_coverage_v1.19.1-992-g38e7b842c.info Lines: 272 272 100.0 %
Date: 2023-03-23 12:38:05 Functions: 19 19 100.0 %
Branches: 175 212 82.5 %

           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                 :        944 : STATIC void array_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
      74                 :        944 :     (void)kind;
      75                 :        944 :     mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in);
      76         [ +  + ]:        944 :     if (o->typecode == BYTEARRAY_TYPECODE) {
      77                 :        820 :         mp_print_str(print, "bytearray(b");
      78                 :        820 :         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                 :        944 :     mp_print_str(print, ")");
      93                 :        944 : }
      94                 :            : #endif
      95                 :            : 
      96                 :            : #if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY
      97                 :       4808 : STATIC mp_obj_array_t *array_new(char typecode, size_t n) {
      98                 :       4808 :     int typecode_size = mp_binary_get_size('@', typecode, NULL);
      99                 :       4804 :     mp_obj_array_t *o = m_new_obj(mp_obj_array_t);
     100                 :            :     #if MICROPY_PY_BUILTINS_BYTEARRAY && MICROPY_PY_ARRAY
     101         [ +  + ]:       4784 :     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                 :       4784 :     o->typecode = typecode;
     108                 :       4784 :     o->free = 0;
     109                 :       4784 :     o->len = n;
     110                 :       4784 :     o->items = m_new(byte, typecode_size * o->len);
     111                 :       4784 :     return o;
     112                 :            : }
     113                 :            : #endif
     114                 :            : 
     115                 :            : #if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY
     116                 :        960 : 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                 :        960 :     mp_buffer_info_t bufinfo;
     120         [ +  + ]:        960 :     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         [ +  + ]:        502 :         && 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                 :        404 :         size_t sz = mp_binary_get_size('@', typecode, NULL);
     129                 :        404 :         size_t len = bufinfo.len / sz;
     130                 :        404 :         mp_obj_array_t *o = array_new(typecode, len);
     131                 :        400 :         memcpy(o->items, bufinfo.buf, len * sz);
     132                 :        400 :         return MP_OBJ_FROM_PTR(o);
     133                 :            :     }
     134                 :            : 
     135                 :        556 :     size_t len;
     136                 :            :     // Try to create array of exact len if initializer len is known
     137                 :        556 :     mp_obj_t len_in = mp_obj_len_maybe(initializer);
     138         [ +  + ]:        556 :     if (len_in == MP_OBJ_NULL) {
     139                 :            :         len = 0;
     140                 :            :     } else {
     141                 :        534 :         len = MP_OBJ_SMALL_INT_VALUE(len_in);
     142                 :            :     }
     143                 :            : 
     144                 :        556 :     mp_obj_array_t *array = array_new(typecode, len);
     145                 :            : 
     146                 :        552 :     mp_obj_t iterable = mp_getiter(initializer, NULL);
     147                 :        552 :     mp_obj_t item;
     148                 :        552 :     size_t i = 0;
     149         [ +  + ]:       5802 :     while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
     150         [ +  + ]:       5245 :         if (len == 0) {
     151                 :       3799 :             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                 :       3140 : 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                 :       3140 :     (void)type_in;
     182                 :            :     // Can take 2nd/3rd arg if constructs from str
     183                 :       3140 :     mp_arg_check_num(n_args, n_kw, 0, 3, false);
     184                 :            : 
     185         [ +  + ]:       3140 :     if (n_args == 0) {
     186                 :            :         // no args: construct an empty bytearray
     187                 :         52 :         return MP_OBJ_FROM_PTR(array_new(BYTEARRAY_TYPECODE, 0));
     188   [ +  +  +  +  :       3088 :     } else if (mp_obj_is_int(args[0])) {
                   +  + ]
     189                 :            :         // 1 arg, an integer: construct a blank bytearray of that length
     190                 :       2620 :         mp_uint_t len = mp_obj_get_int(args[0]);
     191                 :       2616 :         mp_obj_array_t *o = array_new(BYTEARRAY_TYPECODE, len);
     192                 :       2612 :         memset(o->items, 0, len);
     193                 :       2612 :         return MP_OBJ_FROM_PTR(o);
     194                 :            :     } else {
     195                 :            :         // 1 arg: construct the bytearray from that
     196   [ +  +  +  -  :        468 :         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                 :        464 :         return array_construct(BYTEARRAY_TYPECODE, args[0]);
     205                 :            :     }
     206                 :            : }
     207                 :            : #endif
     208                 :            : 
     209                 :            : #if MICROPY_PY_BUILTINS_MEMORYVIEW
     210                 :            : 
     211                 :        642 : mp_obj_t mp_obj_new_memoryview(byte typecode, size_t nitems, void *items) {
     212                 :        642 :     mp_obj_array_t *self = m_new_obj(mp_obj_array_t);
     213                 :        638 :     mp_obj_memoryview_init(self, typecode, 0, nitems, items);
     214                 :        638 :     return MP_OBJ_FROM_PTR(self);
     215                 :            : }
     216                 :            : 
     217                 :        642 : 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                 :        642 :     (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                 :        642 :     mp_arg_check_num(n_args, n_kw, 1, 1, false);
     224                 :            : 
     225                 :        642 :     mp_buffer_info_t bufinfo;
     226                 :        642 :     mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);
     227                 :            : 
     228                 :        642 :     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         [ +  + ]:        638 :     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         [ +  + ]:        638 :     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                 :        638 :     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                 :      20577 : STATIC mp_obj_t array_unary_op(mp_unary_op_t op, mp_obj_t o_in) {
     269                 :      20577 :     mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in);
     270      [ +  +  + ]:      20577 :     switch (op) {
     271                 :          8 :         case MP_UNARY_OP_BOOL:
     272         [ +  + ]:          8 :             return mp_obj_new_bool(o->len != 0);
     273                 :      20565 :         case MP_UNARY_OP_LEN:
     274                 :      20565 :             return MP_OBJ_NEW_SMALL_INT(o->len);
     275                 :            :         default:
     276                 :            :             return MP_OBJ_NULL;      // op not supported
     277                 :            :     }
     278                 :            : }
     279                 :            : 
     280                 :       1660 : STATIC int typecode_for_comparison(int typecode, bool *is_unsigned) {
     281         [ +  + ]:       1660 :     if (typecode == BYTEARRAY_TYPECODE) {
     282                 :            :         typecode = 'B';
     283                 :            :     }
     284         [ +  + ]:       1478 :     if (typecode <= 'Z') {
     285                 :       1060 :         typecode += 32; // to lowercase
     286                 :       1060 :         *is_unsigned = true;
     287                 :            :     }
     288                 :       1660 :     return typecode;
     289                 :            : }
     290                 :            : 
     291                 :        904 : STATIC mp_obj_t array_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
     292                 :        904 :     mp_obj_array_t *lhs = MP_OBJ_TO_PTR(lhs_in);
     293   [ +  +  +  +  :        904 :     switch (op) {
                      + ]
     294                 :         16 :         case MP_BINARY_OP_ADD: {
     295                 :            :             // allow to add anything that has the buffer protocol (extension to CPython)
     296                 :         16 :             mp_buffer_info_t lhs_bufinfo;
     297                 :         16 :             mp_buffer_info_t rhs_bufinfo;
     298                 :         16 :             array_get_buffer(lhs_in, &lhs_bufinfo, MP_BUFFER_READ);
     299                 :         16 :             mp_get_buffer_raise(rhs_in, &rhs_bufinfo, MP_BUFFER_READ);
     300                 :            : 
     301                 :         12 :             size_t sz = mp_binary_get_size('@', lhs_bufinfo.typecode, NULL);
     302                 :            : 
     303                 :            :             // convert byte count to element count (in case rhs is not multiple of sz)
     304                 :         12 :             size_t rhs_len = rhs_bufinfo.len / sz;
     305                 :            : 
     306                 :            :             // note: lhs->len is element count of lhs, lhs_bufinfo.len is byte count
     307                 :         12 :             mp_obj_array_t *res = array_new(lhs_bufinfo.typecode, lhs->len + rhs_len);
     308                 :          8 :             mp_seq_cat((byte *)res->items, lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_len * sz, byte);
     309                 :          8 :             return MP_OBJ_FROM_PTR(res);
     310                 :            :         }
     311                 :            : 
     312                 :         20 :         case MP_BINARY_OP_INPLACE_ADD: {
     313                 :            :             #if MICROPY_PY_BUILTINS_MEMORYVIEW
     314         [ +  + ]:         20 :             if (lhs->base.type == &mp_type_memoryview) {
     315                 :            :                 return MP_OBJ_NULL; // op not supported
     316                 :            :             }
     317                 :            :             #endif
     318                 :         16 :             array_extend(lhs_in, rhs_in);
     319                 :         16 :             return lhs_in;
     320                 :            :         }
     321                 :            : 
     322                 :         30 :         case MP_BINARY_OP_CONTAINS: {
     323                 :            :             #if MICROPY_PY_BUILTINS_BYTEARRAY
     324                 :            :             // Can search string only in bytearray
     325                 :         30 :             mp_buffer_info_t lhs_bufinfo;
     326                 :         30 :             mp_buffer_info_t rhs_bufinfo;
     327         [ +  + ]:         30 :             if (mp_get_buffer(rhs_in, &rhs_bufinfo, MP_BUFFER_READ)) {
     328   [ +  -  +  + ]:         24 :                 if (!mp_obj_is_type(lhs_in, &mp_type_bytearray)) {
     329                 :            :                     return mp_const_false;
     330                 :            :                 }
     331                 :         20 :                 array_get_buffer(lhs_in, &lhs_bufinfo, MP_BUFFER_READ);
     332         [ +  + ]:         28 :                 return mp_obj_new_bool(
     333                 :         20 :                     find_subbytes(lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_bufinfo.len, 1) != NULL);
     334                 :            :             }
     335                 :            :             #endif
     336                 :            : 
     337                 :            :             // Otherwise, can only look for a scalar numeric value in an array
     338   [ +  +  +  -  :          6 :             if (mp_obj_is_int(rhs_in) || mp_obj_is_float(rhs_in)) {
          +  -  -  +  -  
          +  -  +  -  +  
             +  -  -  + ]
     339                 :          2 :                 mp_raise_NotImplementedError(NULL);
     340                 :            :             }
     341                 :            : 
     342                 :            :             return mp_const_false;
     343                 :            :         }
     344                 :            : 
     345                 :        834 :         case MP_BINARY_OP_EQUAL:
     346                 :            :         case MP_BINARY_OP_LESS:
     347                 :            :         case MP_BINARY_OP_LESS_EQUAL:
     348                 :            :         case MP_BINARY_OP_MORE:
     349                 :            :         case MP_BINARY_OP_MORE_EQUAL: {
     350                 :        834 :             mp_buffer_info_t lhs_bufinfo;
     351                 :        834 :             mp_buffer_info_t rhs_bufinfo;
     352                 :        834 :             array_get_buffer(lhs_in, &lhs_bufinfo, MP_BUFFER_READ);
     353         [ +  + ]:        834 :             if (!mp_get_buffer(rhs_in, &rhs_bufinfo, MP_BUFFER_READ)) {
     354                 :            :                 return mp_const_false;
     355                 :            :             }
     356                 :            :             // mp_seq_cmp_bytes is used so only compatible representations can be correctly compared.
     357                 :            :             // The type doesn't matter: array/bytearray/str/bytes all have the same buffer layout, so
     358                 :            :             // just check if the typecodes are compatible; for testing equality the types should have the
     359                 :            :             // same code except for signedness, and not be floating point because nan never equals nan.
     360                 :            :             // For > and < the types should be the same and unsigned.
     361                 :            :             // Note that typecode_for_comparison always returns lowercase letters to save code size.
     362                 :            :             // No need for (& TYPECODE_MASK) here: xxx_get_buffer already takes care of that.
     363                 :        830 :             bool is_unsigned = false;
     364                 :        830 :             const int lhs_code = typecode_for_comparison(lhs_bufinfo.typecode, &is_unsigned);
     365                 :        830 :             const int rhs_code = typecode_for_comparison(rhs_bufinfo.typecode, &is_unsigned);
     366   [ +  +  +  +  :        830 :             if (lhs_code == rhs_code && lhs_code != 'f' && lhs_code != 'd' && (op == MP_BINARY_OP_EQUAL || is_unsigned)) {
             +  +  +  - ]
     367         [ +  + ]:        658 :                 return mp_obj_new_bool(mp_seq_cmp_bytes(op, lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_bufinfo.len));
     368                 :            :             }
     369                 :            :             // mp_obj_equal_not_equal treats returning MP_OBJ_NULL as 'fall back to pointer comparison'
     370                 :            :             // for MP_BINARY_OP_EQUAL but that is incompatible with CPython.
     371                 :        368 :             mp_raise_NotImplementedError(NULL);
     372                 :            :         }
     373                 :            : 
     374                 :            :         default:
     375                 :            :             return MP_OBJ_NULL; // op not supported
     376                 :            :     }
     377                 :            : }
     378                 :            : 
     379                 :            : #if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY
     380                 :       3847 : STATIC mp_obj_t array_append(mp_obj_t self_in, mp_obj_t arg) {
     381                 :            :     // self is not a memoryview, so we don't need to use (& TYPECODE_MASK)
     382   [ +  #  +  +  :       3847 :     assert((MICROPY_PY_BUILTINS_BYTEARRAY && mp_obj_is_type(self_in, &mp_type_bytearray))
             +  -  -  + ]
     383                 :            :         || (MICROPY_PY_ARRAY && mp_obj_is_type(self_in, &mp_type_array)));
     384                 :       3847 :     mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in);
     385                 :            : 
     386         [ +  + ]:       3847 :     if (self->free == 0) {
     387                 :        514 :         size_t item_sz = mp_binary_get_size('@', self->typecode, NULL);
     388                 :            :         // TODO: alloc policy
     389                 :        514 :         self->free = 8;
     390                 :        514 :         self->items = m_renew(byte, self->items, item_sz * self->len, item_sz * (self->len + self->free));
     391                 :        512 :         mp_seq_clear(self->items, self->len + 1, self->len + self->free, item_sz);
     392                 :            :     }
     393                 :       3845 :     mp_binary_set_val_array(self->typecode, self->items, self->len, arg);
     394                 :            :     // only update length/free if set succeeded
     395                 :       3847 :     self->len++;
     396                 :       3847 :     self->free--;
     397                 :       3847 :     return mp_const_none; // return None, as per CPython
     398                 :            : }
     399                 :            : MP_DEFINE_CONST_FUN_OBJ_2(mp_obj_array_append_obj, array_append);
     400                 :            : 
     401                 :         28 : STATIC mp_obj_t array_extend(mp_obj_t self_in, mp_obj_t arg_in) {
     402                 :            :     // self is not a memoryview, so we don't need to use (& TYPECODE_MASK)
     403   [ +  -  +  +  :         28 :     assert((MICROPY_PY_BUILTINS_BYTEARRAY && mp_obj_is_type(self_in, &mp_type_bytearray))
             +  -  -  + ]
     404                 :            :         || (MICROPY_PY_ARRAY && mp_obj_is_type(self_in, &mp_type_array)));
     405                 :         28 :     mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in);
     406                 :            : 
     407                 :            :     // allow to extend by anything that has the buffer protocol (extension to CPython)
     408                 :         28 :     mp_buffer_info_t arg_bufinfo;
     409                 :         28 :     mp_get_buffer_raise(arg_in, &arg_bufinfo, MP_BUFFER_READ);
     410                 :            : 
     411                 :         28 :     size_t sz = mp_binary_get_size('@', self->typecode, NULL);
     412                 :            : 
     413                 :            :     // convert byte count to element count
     414                 :         28 :     size_t len = arg_bufinfo.len / sz;
     415                 :            : 
     416                 :            :     // make sure we have enough room to extend
     417                 :            :     // TODO: alloc policy; at the moment we go conservative
     418         [ +  + ]:         28 :     if (self->free < len) {
     419                 :         24 :         self->items = m_renew(byte, self->items, (self->len + self->free) * sz, (self->len + len) * sz);
     420                 :         16 :         self->free = 0;
     421                 :            :     } else {
     422                 :          4 :         self->free -= len;
     423                 :            :     }
     424                 :            : 
     425                 :            :     // extend
     426                 :         20 :     mp_seq_copy((byte *)self->items + self->len * sz, arg_bufinfo.buf, len * sz, byte);
     427                 :         20 :     self->len += len;
     428                 :            : 
     429                 :         20 :     return mp_const_none;
     430                 :            : }
     431                 :            : MP_DEFINE_CONST_FUN_OBJ_2(mp_obj_array_extend_obj, array_extend);
     432                 :            : #endif
     433                 :            : 
     434                 :   18917208 : STATIC mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) {
     435         [ +  + ]:   18917208 :     if (value == MP_OBJ_NULL) {
     436                 :            :         // delete item
     437                 :            :         // TODO implement
     438                 :            :         // TODO: confirmed that both bytearray and array.array support
     439                 :            :         // slice deletion
     440                 :            :         return MP_OBJ_NULL; // op not supported
     441                 :            :     } else {
     442                 :   18917206 :         mp_obj_array_t *o = MP_OBJ_TO_PTR(self_in);
     443                 :            :         #if MICROPY_PY_BUILTINS_SLICE
     444   [ -  +  -  +  :   18917206 :         if (mp_obj_is_type(index_in, &mp_type_slice)) {
          -  +  -  +  +  
                +  +  - ]
     445                 :       6804 :             mp_bound_slice_t slice;
     446         [ +  + ]:       6804 :             if (!mp_seq_get_fast_slice_indexes(o->len, index_in, &slice)) {
     447                 :          2 :                 mp_raise_NotImplementedError(MP_ERROR_TEXT("only slices with step=1 (aka None) are supported"));
     448                 :            :             }
     449         [ +  + ]:       6802 :             if (value != MP_OBJ_SENTINEL) {
     450                 :            :                 #if MICROPY_PY_ARRAY_SLICE_ASSIGN
     451                 :            :                 // Assign
     452                 :       4056 :                 size_t src_len;
     453                 :       4056 :                 void *src_items;
     454                 :       4056 :                 size_t item_sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL);
     455   [ +  -  +  -  :       4056 :                 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) {
                   +  + ]
     456                 :            :                     // value is array, bytearray or memoryview
     457                 :       2438 :                     mp_obj_array_t *src_slice = MP_OBJ_TO_PTR(value);
     458         [ +  + ]:       2438 :                     if (item_sz != mp_binary_get_size('@', src_slice->typecode & TYPECODE_MASK, NULL)) {
     459                 :          4 :                     compat_error:
     460                 :         20 :                         mp_raise_ValueError(MP_ERROR_TEXT("lhs and rhs should be compatible"));
     461                 :            :                     }
     462                 :       2434 :                     src_len = src_slice->len;
     463                 :       2434 :                     src_items = src_slice->items;
     464                 :            :                     #if MICROPY_PY_BUILTINS_MEMORYVIEW
     465         [ +  + ]:       2434 :                     if (mp_obj_is_type(value, &mp_type_memoryview)) {
     466                 :       2364 :                         src_items = (uint8_t *)src_items + (src_slice->memview_offset * item_sz);
     467                 :            :                     }
     468                 :            :                     #endif
     469   [ -  +  -  +  :       1618 :                 } else if (mp_obj_is_type(value, &mp_type_bytes)) {
          -  +  -  +  +  
                -  +  + ]
     470         [ +  + ]:       1616 :                     if (item_sz != 1) {
     471                 :          4 :                         goto compat_error;
     472                 :            :                     }
     473                 :       1612 :                     mp_buffer_info_t bufinfo;
     474                 :       1612 :                     mp_get_buffer_raise(value, &bufinfo, MP_BUFFER_READ);
     475                 :       1612 :                     src_len = bufinfo.len;
     476                 :       1612 :                     src_items = bufinfo.buf;
     477                 :            :                 } else {
     478                 :          2 :                     mp_raise_NotImplementedError(MP_ERROR_TEXT("array/bytes required on right side"));
     479                 :            :                 }
     480                 :            : 
     481                 :            :                 // TODO: check src/dst compat
     482                 :       4046 :                 mp_int_t len_adj = src_len - (slice.stop - slice.start);
     483                 :       4046 :                 uint8_t *dest_items = o->items;
     484                 :            :                 #if MICROPY_PY_BUILTINS_MEMORYVIEW
     485         [ +  + ]:       4046 :                 if (o->base.type == &mp_type_memoryview) {
     486         [ +  + ]:         48 :                     if (!(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW)) {
     487                 :            :                         // store to read-only memoryview not allowed
     488                 :            :                         return MP_OBJ_NULL;
     489                 :            :                     }
     490         [ +  + ]:         44 :                     if (len_adj != 0) {
     491                 :         12 :                         goto compat_error;
     492                 :            :                     }
     493                 :         32 :                     dest_items += o->memview_offset * item_sz;
     494                 :            :                 }
     495                 :            :                 #endif
     496         [ +  + ]:       4030 :                 if (len_adj > 0) {
     497         [ +  + ]:       1620 :                     if ((size_t)len_adj > o->free) {
     498                 :            :                         // TODO: alloc policy; at the moment we go conservative
     499                 :       1592 :                         o->items = m_renew(byte, o->items, (o->len + o->free) * item_sz, (o->len + len_adj) * item_sz);
     500                 :       1588 :                         o->free = len_adj;
     501                 :       1588 :                         dest_items = o->items;
     502                 :            :                     }
     503                 :       1616 :                     mp_seq_replace_slice_grow_inplace(dest_items, o->len,
     504                 :       1616 :                         slice.start, slice.stop, src_items, src_len, len_adj, item_sz);
     505                 :            :                 } else {
     506                 :       2410 :                     mp_seq_replace_slice_no_grow(dest_items, o->len,
     507                 :       2410 :                         slice.start, slice.stop, src_items, src_len, item_sz);
     508                 :            :                     // Clear "freed" elements at the end of list
     509                 :            :                     // TODO: This is actually only needed for typecode=='O'
     510                 :       2410 :                     mp_seq_clear(dest_items, o->len + len_adj, o->len, item_sz);
     511                 :            :                     // TODO: alloc policy after shrinking
     512                 :            :                 }
     513                 :       4026 :                 o->free -= len_adj;
     514                 :       4026 :                 o->len += len_adj;
     515                 :       4026 :                 return mp_const_none;
     516                 :            :                 #else
     517                 :            :                 return MP_OBJ_NULL; // op not supported
     518                 :            :                 #endif
     519                 :            :             }
     520                 :            : 
     521                 :       2746 :             mp_obj_array_t *res;
     522                 :       2746 :             size_t sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL);
     523         [ -  + ]:       2746 :             assert(sz > 0);
     524                 :            :             #if MICROPY_PY_BUILTINS_MEMORYVIEW
     525         [ +  + ]:       2746 :             if (o->base.type == &mp_type_memoryview) {
     526         [ +  + ]:       2466 :                 if (slice.start > memview_offset_max) {
     527                 :          4 :                     mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("memoryview offset too large"));
     528                 :            :                 }
     529                 :       2462 :                 res = m_new_obj(mp_obj_array_t);
     530                 :       2458 :                 *res = *o;
     531                 :       2458 :                 res->memview_offset += slice.start;
     532                 :       2458 :                 res->len = slice.stop - slice.start;
     533                 :            :             } else
     534                 :            :             #endif
     535                 :            :             {
     536                 :        280 :                 res = array_new(o->typecode, slice.stop - slice.start);
     537                 :        276 :                 memcpy(res->items, (uint8_t *)o->items + slice.start * sz, (slice.stop - slice.start) * sz);
     538                 :            :             }
     539                 :       2734 :             return MP_OBJ_FROM_PTR(res);
     540                 :            :         } else
     541                 :            :         #endif
     542                 :            :         {
     543                 :   18910402 :             size_t index = mp_get_index(o->base.type, o->len, index_in, false);
     544                 :            :             #if MICROPY_PY_BUILTINS_MEMORYVIEW
     545         [ +  + ]:   19712403 :             if (o->base.type == &mp_type_memoryview) {
     546                 :         66 :                 index += o->memview_offset;
     547   [ +  +  +  + ]:         66 :                 if (value != MP_OBJ_SENTINEL && !(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW)) {
     548                 :            :                     // store to read-only memoryview
     549                 :            :                     return MP_OBJ_NULL;
     550                 :            :                 }
     551                 :            :             }
     552                 :            :             #endif
     553         [ +  + ]:   19712381 :             if (value == MP_OBJ_SENTINEL) {
     554                 :            :                 // load
     555                 :   11331442 :                 return mp_binary_get_val_array(o->typecode & TYPECODE_MASK, o->items, index);
     556                 :            :             } else {
     557                 :            :                 // store
     558                 :    8380957 :                 mp_binary_set_val_array(o->typecode & TYPECODE_MASK, o->items, index, value);
     559                 :    8380957 :                 return mp_const_none;
     560                 :            :             }
     561                 :            :         }
     562                 :            :     }
     563                 :            : }
     564                 :            : 
     565                 :       3208 : STATIC mp_int_t array_get_buffer(mp_obj_t o_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
     566                 :       3208 :     mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in);
     567                 :       3208 :     size_t sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL);
     568                 :       3208 :     bufinfo->buf = o->items;
     569                 :       3208 :     bufinfo->len = o->len * sz;
     570                 :       3208 :     bufinfo->typecode = o->typecode & TYPECODE_MASK;
     571                 :            :     #if MICROPY_PY_BUILTINS_MEMORYVIEW
     572         [ +  + ]:       3208 :     if (o->base.type == &mp_type_memoryview) {
     573   [ +  +  +  + ]:        132 :         if (!(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW) && (flags & MP_BUFFER_WRITE)) {
     574                 :            :             // read-only memoryview
     575                 :            :             return 1;
     576                 :            :         }
     577                 :        128 :         bufinfo->buf = (uint8_t *)bufinfo->buf + (size_t)o->memview_offset * sz;
     578                 :            :     }
     579                 :            :     #else
     580                 :            :     (void)flags;
     581                 :            :     #endif
     582                 :            :     return 0;
     583                 :            : }
     584                 :            : 
     585                 :            : #if MICROPY_PY_ARRAY
     586                 :            : MP_DEFINE_CONST_OBJ_TYPE(
     587                 :            :     mp_type_array,
     588                 :            :     MP_QSTR_array,
     589                 :            :     MP_TYPE_FLAG_ITER_IS_GETITER,
     590                 :            :     make_new, array_make_new,
     591                 :            :     print, array_print,
     592                 :            :     iter, array_iterator_new,
     593                 :            :     unary_op, array_unary_op,
     594                 :            :     binary_op, array_binary_op,
     595                 :            :     subscr, array_subscr,
     596                 :            :     buffer, array_get_buffer,
     597                 :            :     locals_dict, &mp_obj_array_locals_dict
     598                 :            :     );
     599                 :            : #endif
     600                 :            : 
     601                 :            : #if MICROPY_PY_BUILTINS_BYTEARRAY
     602                 :            : MP_DEFINE_CONST_OBJ_TYPE(
     603                 :            :     mp_type_bytearray,
     604                 :            :     MP_QSTR_bytearray,
     605                 :            :     MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_ITER_IS_GETITER,
     606                 :            :     make_new, bytearray_make_new,
     607                 :            :     print, array_print,
     608                 :            :     iter, array_iterator_new,
     609                 :            :     unary_op, array_unary_op,
     610                 :            :     binary_op, array_binary_op,
     611                 :            :     subscr, array_subscr,
     612                 :            :     buffer, array_get_buffer,
     613                 :            :     locals_dict, &mp_obj_bytearray_locals_dict
     614                 :            :     );
     615                 :            : #endif
     616                 :            : 
     617                 :            : #if MICROPY_PY_BUILTINS_MEMORYVIEW
     618                 :            : #if MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE
     619                 :            : #define MEMORYVIEW_TYPE_ATTR attr, memoryview_attr,
     620                 :            : #else
     621                 :            : #define MEMORYVIEW_TYPE_ATTR
     622                 :            : #endif
     623                 :            : 
     624                 :            : #if MICROPY_PY_BUILTINS_BYTES_HEX
     625                 :            : #define MEMORYVIEW_TYPE_LOCALS_DICT locals_dict, &mp_obj_memoryview_locals_dict,
     626                 :            : #else
     627                 :            : #define MEMORYVIEW_TYPE_LOCALS_DICT
     628                 :            : #endif
     629                 :            : 
     630                 :            : MP_DEFINE_CONST_OBJ_TYPE(
     631                 :            :     mp_type_memoryview,
     632                 :            :     MP_QSTR_memoryview,
     633                 :            :     MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_ITER_IS_GETITER,
     634                 :            :     make_new, memoryview_make_new,
     635                 :            :     iter, array_iterator_new,
     636                 :            :     unary_op, array_unary_op,
     637                 :            :     binary_op, array_binary_op,
     638                 :            :     MEMORYVIEW_TYPE_LOCALS_DICT
     639                 :            :     MEMORYVIEW_TYPE_ATTR
     640                 :            :     subscr, array_subscr,
     641                 :            :     buffer, array_get_buffer
     642                 :            :     );
     643                 :            : #endif // MICROPY_PY_BUILTINS_MEMORYVIEW
     644                 :            : 
     645                 :            : /* unused
     646                 :            : size_t mp_obj_array_len(mp_obj_t self_in) {
     647                 :            :     return ((mp_obj_array_t *)self_in)->len;
     648                 :            : }
     649                 :            : */
     650                 :            : 
     651                 :            : #if MICROPY_PY_BUILTINS_BYTEARRAY
     652                 :         78 : mp_obj_t mp_obj_new_bytearray(size_t n, const void *items) {
     653                 :         78 :     mp_obj_array_t *o = array_new(BYTEARRAY_TYPECODE, n);
     654                 :         78 :     memcpy(o->items, items, n);
     655                 :         78 :     return MP_OBJ_FROM_PTR(o);
     656                 :            : }
     657                 :            : 
     658                 :            : // Create bytearray which references specified memory area
     659                 :        194 : mp_obj_t mp_obj_new_bytearray_by_ref(size_t n, void *items) {
     660                 :        194 :     mp_obj_array_t *o = mp_obj_malloc(mp_obj_array_t, &mp_type_bytearray);
     661                 :        194 :     o->typecode = BYTEARRAY_TYPECODE;
     662                 :        194 :     o->free = 0;
     663                 :        194 :     o->len = n;
     664                 :        194 :     o->items = items;
     665                 :        194 :     return MP_OBJ_FROM_PTR(o);
     666                 :            : }
     667                 :            : #endif
     668                 :            : 
     669                 :            : /******************************************************************************/
     670                 :            : // array iterator
     671                 :            : 
     672                 :            : typedef struct _mp_obj_array_it_t {
     673                 :            :     mp_obj_base_t base;
     674                 :            :     mp_obj_array_t *array;
     675                 :            :     size_t offset;
     676                 :            :     size_t cur;
     677                 :            : } mp_obj_array_it_t;
     678                 :            : 
     679                 :       4386 : STATIC mp_obj_t array_it_iternext(mp_obj_t self_in) {
     680                 :       4386 :     mp_obj_array_it_t *self = MP_OBJ_TO_PTR(self_in);
     681         [ +  + ]:       4386 :     if (self->cur < self->array->len) {
     682                 :       4268 :         return mp_binary_get_val_array(self->array->typecode & TYPECODE_MASK, self->array->items, self->offset + self->cur++);
     683                 :            :     } else {
     684                 :            :         return MP_OBJ_STOP_ITERATION;
     685                 :            :     }
     686                 :            : }
     687                 :            : 
     688                 :            : STATIC MP_DEFINE_CONST_OBJ_TYPE(
     689                 :            :     mp_type_array_it,
     690                 :            :     MP_QSTR_iterator,
     691                 :            :     MP_TYPE_FLAG_ITER_IS_ITERNEXT,
     692                 :            :     iter, array_it_iternext
     693                 :            :     );
     694                 :            : 
     695                 :        118 : STATIC mp_obj_t array_iterator_new(mp_obj_t array_in, mp_obj_iter_buf_t *iter_buf) {
     696                 :        118 :     assert(sizeof(mp_obj_array_t) <= sizeof(mp_obj_iter_buf_t));
     697                 :        118 :     mp_obj_array_t *array = MP_OBJ_TO_PTR(array_in);
     698                 :        118 :     mp_obj_array_it_t *o = (mp_obj_array_it_t *)iter_buf;
     699                 :        118 :     o->base.type = &mp_type_array_it;
     700                 :        118 :     o->array = array;
     701                 :        118 :     o->offset = 0;
     702                 :        118 :     o->cur = 0;
     703                 :            :     #if MICROPY_PY_BUILTINS_MEMORYVIEW
     704         [ +  + ]:        118 :     if (array->base.type == &mp_type_memoryview) {
     705                 :         76 :         o->offset = array->memview_offset;
     706                 :            :     }
     707                 :            :     #endif
     708                 :        118 :     return MP_OBJ_FROM_PTR(o);
     709                 :            : }
     710                 :            : 
     711                 :            : #endif // MICROPY_PY_ARRAY || MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_BUILTINS_MEMORYVIEW

Generated by: LCOV version 1.15-5-g462f71d