LCOV - code coverage report
Current view: top level - py - modstruct.c (source / functions) Hit Total Coverage
Test: unix_coverage_v1.19.1-724-gfb7d21153.info Lines: 122 122 100.0 %
Date: 2022-12-01 09:37:31 Functions: 8 8 100.0 %
Branches: 49 49 100.0 %

           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 <assert.h>
      29                 :            : #include <string.h>
      30                 :            : 
      31                 :            : #include "py/runtime.h"
      32                 :            : #include "py/builtin.h"
      33                 :            : #include "py/objtuple.h"
      34                 :            : #include "py/binary.h"
      35                 :            : #include "py/parsenum.h"
      36                 :            : 
      37                 :            : #if MICROPY_PY_STRUCT
      38                 :            : 
      39                 :            : /*
      40                 :            :     This module implements most of character typecodes from CPython, with
      41                 :            :     some extensions:
      42                 :            : 
      43                 :            :     O - (Pointer to) an arbitrary Python object. This is useful for callback
      44                 :            :         data, etc. Note that you must keep reference to passed object in
      45                 :            :         your Python application, otherwise it may be garbage-collected,
      46                 :            :         and then when you get back this value from callback it may be
      47                 :            :         invalid (and lead to crash).
      48                 :            :     S - Pointer to a string (returned as a Python string). Note the
      49                 :            :         difference from "Ns", - the latter says "in this place of structure
      50                 :            :         is character data of up to N bytes length", while "S" means
      51                 :            :         "in this place of a structure is a pointer to zero-terminated
      52                 :            :         character data".
      53                 :            :  */
      54                 :            : 
      55                 :        880 : STATIC char get_fmt_type(const char **fmt) {
      56                 :        880 :     char t = **fmt;
      57      [ +  +  + ]:        880 :     switch (t) {
      58                 :          8 :         case '!':
      59                 :          8 :             t = '>';
      60                 :          8 :             break;
      61                 :            :         case '@':
      62                 :            :         case '=':
      63                 :            :         case '<':
      64                 :            :         case '>':
      65                 :            :             break;
      66                 :            :         default:
      67                 :            :             return '@';
      68                 :            :     }
      69                 :            :     // Skip type char
      70                 :        660 :     (*fmt)++;
      71                 :        660 :     return t;
      72                 :            : }
      73                 :            : 
      74                 :        228 : STATIC mp_uint_t get_fmt_num(const char **p) {
      75                 :        228 :     const char *num = *p;
      76                 :        228 :     uint len = 1;
      77         [ +  + ]:        240 :     while (unichar_isdigit(*++num)) {
      78                 :         12 :         len++;
      79                 :            :     }
      80                 :        228 :     mp_uint_t val = (mp_uint_t)MP_OBJ_SMALL_INT_VALUE(mp_parse_num_integer(*p, len, 10, NULL));
      81                 :        228 :     *p = num;
      82                 :        228 :     return val;
      83                 :            : }
      84                 :            : 
      85                 :        480 : STATIC size_t calc_size_items(const char *fmt, size_t *total_sz) {
      86                 :        480 :     char fmt_type = get_fmt_type(&fmt);
      87                 :        480 :     size_t total_cnt = 0;
      88                 :        480 :     size_t size;
      89         [ +  + ]:       1544 :     for (size = 0; *fmt; fmt++) {
      90                 :        620 :         mp_uint_t cnt = 1;
      91         [ +  + ]:        620 :         if (unichar_isdigit(*fmt)) {
      92                 :        148 :             cnt = get_fmt_num(&fmt);
      93                 :            :         }
      94                 :            : 
      95         [ +  + ]:        620 :         if (*fmt == 's') {
      96                 :         64 :             total_cnt += 1;
      97                 :         64 :             size += cnt;
      98                 :            :         } else {
      99                 :        556 :             total_cnt += cnt;
     100                 :        556 :             size_t align;
     101                 :        556 :             size_t sz = mp_binary_get_size(fmt_type, *fmt, &align);
     102         [ +  + ]:       1120 :             while (cnt--) {
     103                 :            :                 // Apply alignment
     104                 :        564 :                 size = (size + align - 1) & ~(align - 1);
     105                 :        564 :                 size += sz;
     106                 :            :             }
     107                 :            :         }
     108                 :            :     }
     109                 :        444 :     *total_sz = size;
     110                 :        444 :     return total_cnt;
     111                 :            : }
     112                 :            : 
     113                 :        300 : STATIC mp_obj_t struct_calcsize(mp_obj_t fmt_in) {
     114                 :        300 :     const char *fmt = mp_obj_str_get_str(fmt_in);
     115                 :        296 :     size_t size;
     116                 :        296 :     calc_size_items(fmt, &size);
     117                 :        272 :     return MP_OBJ_NEW_SMALL_INT(size);
     118                 :            : }
     119                 :            : MP_DEFINE_CONST_FUN_OBJ_1(struct_calcsize_obj, struct_calcsize);
     120                 :            : 
     121                 :        184 : STATIC mp_obj_t struct_unpack_from(size_t n_args, const mp_obj_t *args) {
     122                 :            :     // unpack requires that the buffer be exactly the right size.
     123                 :            :     // unpack_from requires that the buffer be "big enough".
     124                 :            :     // Since we implement unpack and unpack_from using the same function
     125                 :            :     // we relax the "exact" requirement, and only implement "big enough".
     126                 :        184 :     const char *fmt = mp_obj_str_get_str(args[0]);
     127                 :        184 :     size_t total_sz;
     128                 :        184 :     size_t num_items = calc_size_items(fmt, &total_sz);
     129                 :        172 :     char fmt_type = get_fmt_type(&fmt);
     130                 :        172 :     mp_obj_tuple_t *res = MP_OBJ_TO_PTR(mp_obj_new_tuple(num_items, NULL));
     131                 :        172 :     mp_buffer_info_t bufinfo;
     132                 :        172 :     mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ);
     133                 :        172 :     byte *p = bufinfo.buf;
     134                 :        172 :     byte *end_p = &p[bufinfo.len];
     135                 :        172 :     mp_int_t offset = 0;
     136                 :            : 
     137         [ +  + ]:        172 :     if (n_args > 2) {
     138                 :            :         // offset arg provided
     139                 :         28 :         offset = mp_obj_get_int(args[2]);
     140         [ +  + ]:         28 :         if (offset < 0) {
     141                 :            :             // negative offsets are relative to the end of the buffer
     142                 :          8 :             offset = bufinfo.len + offset;
     143         [ +  + ]:          8 :             if (offset < 0) {
     144                 :          4 :                 mp_raise_ValueError(MP_ERROR_TEXT("buffer too small"));
     145                 :            :             }
     146                 :            :         }
     147                 :         24 :         p += offset;
     148                 :            :     }
     149                 :        168 :     byte *p_base = p;
     150                 :            : 
     151                 :            :     // Check that the input buffer is big enough to unpack all the values
     152         [ +  + ]:        168 :     if (p + total_sz > end_p) {
     153                 :         12 :         mp_raise_ValueError(MP_ERROR_TEXT("buffer too small"));
     154                 :            :     }
     155                 :            : 
     156         [ +  + ]:        356 :     for (size_t i = 0; i < num_items;) {
     157                 :        200 :         mp_uint_t cnt = 1;
     158         [ +  + ]:        200 :         if (unichar_isdigit(*fmt)) {
     159                 :         40 :             cnt = get_fmt_num(&fmt);
     160                 :            :         }
     161                 :        200 :         mp_obj_t item;
     162         [ +  + ]:        200 :         if (*fmt == 's') {
     163                 :         20 :             item = mp_obj_new_bytes(p, cnt);
     164                 :         20 :             p += cnt;
     165                 :         20 :             res->items[i++] = item;
     166                 :            :         } else {
     167         [ +  + ]:        376 :             while (cnt--) {
     168                 :        196 :                 item = mp_binary_get_val(fmt_type, *fmt, p_base, &p);
     169                 :        196 :                 res->items[i++] = item;
     170                 :            :             }
     171                 :            :         }
     172                 :        200 :         fmt++;
     173                 :            :     }
     174                 :        156 :     return MP_OBJ_FROM_PTR(res);
     175                 :            : }
     176                 :            : MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_unpack_from_obj, 2, 3, struct_unpack_from);
     177                 :            : 
     178                 :            : // This function assumes there is enough room in p to store all the values
     179                 :        228 : STATIC void struct_pack_into_internal(mp_obj_t fmt_in, byte *p, size_t n_args, const mp_obj_t *args) {
     180                 :        228 :     const char *fmt = mp_obj_str_get_str(fmt_in);
     181                 :        228 :     char fmt_type = get_fmt_type(&fmt);
     182                 :            : 
     183                 :        228 :     byte *p_base = p;
     184                 :        228 :     size_t i;
     185         [ +  + ]:        514 :     for (i = 0; i < n_args;) {
     186                 :        288 :         mp_uint_t cnt = 1;
     187         [ +  + ]:        288 :         if (*fmt == '\0') {
     188                 :            :             // more arguments given than used by format string; CPython raises struct.error here
     189                 :            :             break;
     190                 :            :         }
     191         [ +  + ]:        286 :         if (unichar_isdigit(*fmt)) {
     192                 :         40 :             cnt = get_fmt_num(&fmt);
     193                 :            :         }
     194                 :            : 
     195         [ +  + ]:        286 :         if (*fmt == 's') {
     196                 :         20 :             mp_buffer_info_t bufinfo;
     197                 :         20 :             mp_get_buffer_raise(args[i++], &bufinfo, MP_BUFFER_READ);
     198                 :         20 :             mp_uint_t to_copy = cnt;
     199         [ +  + ]:         20 :             if (bufinfo.len < to_copy) {
     200                 :          4 :                 to_copy = bufinfo.len;
     201                 :            :             }
     202                 :         20 :             memcpy(p, bufinfo.buf, to_copy);
     203                 :         20 :             memset(p + to_copy, 0, cnt - to_copy);
     204                 :         20 :             p += cnt;
     205                 :            :         } else {
     206                 :            :             // If we run out of args then we just finish; CPython would raise struct.error
     207   [ +  +  +  + ]:        544 :             while (cnt-- && i < n_args) {
     208                 :        278 :                 mp_binary_set_val(fmt_type, *fmt, args[i++], p_base, &p);
     209                 :            :             }
     210                 :            :         }
     211                 :        286 :         fmt++;
     212                 :            :     }
     213                 :        228 : }
     214                 :            : 
     215                 :        228 : STATIC mp_obj_t struct_pack(size_t n_args, const mp_obj_t *args) {
     216                 :            :     // TODO: "The arguments must match the values required by the format exactly."
     217                 :        228 :     mp_int_t size = MP_OBJ_SMALL_INT_VALUE(struct_calcsize(args[0]));
     218                 :        212 :     vstr_t vstr;
     219                 :        212 :     vstr_init_len(&vstr, size);
     220                 :        212 :     byte *p = (byte *)vstr.buf;
     221                 :        212 :     memset(p, 0, size);
     222                 :        212 :     struct_pack_into_internal(args[0], p, n_args - 1, &args[1]);
     223                 :        212 :     return mp_obj_new_bytes_from_vstr(&vstr);
     224                 :            : }
     225                 :            : MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_pack_obj, 1, MP_OBJ_FUN_ARGS_MAX, struct_pack);
     226                 :            : 
     227                 :         36 : STATIC mp_obj_t struct_pack_into(size_t n_args, const mp_obj_t *args) {
     228                 :         36 :     mp_buffer_info_t bufinfo;
     229                 :         36 :     mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE);
     230                 :         36 :     mp_int_t offset = mp_obj_get_int(args[2]);
     231         [ +  + ]:         36 :     if (offset < 0) {
     232                 :            :         // negative offsets are relative to the end of the buffer
     233                 :          8 :         offset = (mp_int_t)bufinfo.len + offset;
     234         [ +  + ]:          8 :         if (offset < 0) {
     235                 :          4 :             mp_raise_ValueError(MP_ERROR_TEXT("buffer too small"));
     236                 :            :         }
     237                 :            :     }
     238                 :         32 :     byte *p = (byte *)bufinfo.buf;
     239                 :         32 :     byte *end_p = &p[bufinfo.len];
     240                 :         32 :     p += offset;
     241                 :            : 
     242                 :            :     // Check that the output buffer is big enough to hold all the values
     243                 :         32 :     mp_int_t sz = MP_OBJ_SMALL_INT_VALUE(struct_calcsize(args[0]));
     244         [ +  + ]:         28 :     if (p + sz > end_p) {
     245                 :         12 :         mp_raise_ValueError(MP_ERROR_TEXT("buffer too small"));
     246                 :            :     }
     247                 :            : 
     248                 :         16 :     struct_pack_into_internal(args[0], p, n_args - 3, &args[3]);
     249                 :         16 :     return mp_const_none;
     250                 :            : }
     251                 :            : MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_pack_into_obj, 3, MP_OBJ_FUN_ARGS_MAX, struct_pack_into);
     252                 :            : 
     253                 :            : STATIC const mp_rom_map_elem_t mp_module_struct_globals_table[] = {
     254                 :            :     { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ustruct) },
     255                 :            :     { MP_ROM_QSTR(MP_QSTR_calcsize), MP_ROM_PTR(&struct_calcsize_obj) },
     256                 :            :     { MP_ROM_QSTR(MP_QSTR_pack), MP_ROM_PTR(&struct_pack_obj) },
     257                 :            :     { MP_ROM_QSTR(MP_QSTR_pack_into), MP_ROM_PTR(&struct_pack_into_obj) },
     258                 :            :     { MP_ROM_QSTR(MP_QSTR_unpack), MP_ROM_PTR(&struct_unpack_from_obj) },
     259                 :            :     { MP_ROM_QSTR(MP_QSTR_unpack_from), MP_ROM_PTR(&struct_unpack_from_obj) },
     260                 :            : };
     261                 :            : 
     262                 :            : STATIC MP_DEFINE_CONST_DICT(mp_module_struct_globals, mp_module_struct_globals_table);
     263                 :            : 
     264                 :            : const mp_obj_module_t mp_module_ustruct = {
     265                 :            :     .base = { &mp_type_module },
     266                 :            :     .globals = (mp_obj_dict_t *)&mp_module_struct_globals,
     267                 :            : };
     268                 :            : 
     269                 :            : MP_REGISTER_MODULE(MP_QSTR_ustruct, mp_module_ustruct);
     270                 :            : 
     271                 :            : #endif

Generated by: LCOV version 1.15-5-g462f71d