LCOV - code coverage report
Current view: top level - py - objarray.c (source / functions) Hit Total Coverage
Test: unix_coverage_v1.22.0-335-g9c7f0659e.info Lines: 277 277 100.0 %
Date: 2024-04-24 08:31:58 Functions: 19 19 100.0 %
Branches: 182 220 82.7 %

           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

Generated by: LCOV version 1.15-5-g462f71d