LCOV - code coverage report
Current view: top level - py - modstruct.c (source / functions) Hit Total Coverage
Test: unix_coverage_v1.24.0-74-g406bccc75.info Lines: 129 129 100.0 %
Date: 2024-12-03 15:10:00 Functions: 8 8 100.0 %
Branches: 55 55 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                 :       1592 : static char get_fmt_type(const char **fmt) {
      56                 :       1592 :     char t = **fmt;
      57      [ +  +  + ]:       1592 :     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                 :       1336 :     (*fmt)++;
      71                 :       1336 :     return t;
      72                 :            : }
      73                 :            : 
      74                 :        256 : static mp_uint_t get_fmt_num(const char **p) {
      75                 :        256 :     const char *num = *p;
      76                 :        256 :     uint len = 1;
      77         [ +  + ]:        268 :     while (unichar_isdigit(*++num)) {
      78                 :         12 :         len++;
      79                 :            :     }
      80                 :        256 :     mp_uint_t val = (mp_uint_t)MP_OBJ_SMALL_INT_VALUE(mp_parse_num_integer(*p, len, 10, NULL));
      81                 :        256 :     *p = num;
      82                 :        256 :     return val;
      83                 :            : }
      84                 :            : 
      85                 :        840 : static size_t calc_size_items(const char *fmt, size_t *total_sz) {
      86                 :        840 :     char fmt_type = get_fmt_type(&fmt);
      87                 :        840 :     size_t total_cnt = 0;
      88                 :        840 :     size_t size;
      89         [ +  + ]:       2680 :     for (size = 0; *fmt; fmt++) {
      90                 :       1036 :         mp_uint_t cnt = 1;
      91         [ +  + ]:       1036 :         if (unichar_isdigit(*fmt)) {
      92                 :        168 :             cnt = get_fmt_num(&fmt);
      93                 :            :         }
      94                 :            : 
      95         [ +  + ]:       1036 :         if (*fmt == 'x') {
      96                 :         48 :             size += cnt;
      97         [ +  + ]:        988 :         } else if (*fmt == 's') {
      98                 :         64 :             total_cnt += 1;
      99                 :         64 :             size += cnt;
     100                 :            :         } else {
     101                 :        924 :             total_cnt += cnt;
     102                 :        924 :             size_t align;
     103                 :        924 :             size_t sz = mp_binary_get_size(fmt_type, *fmt, &align);
     104         [ +  + ]:       1856 :             while (cnt--) {
     105                 :            :                 // Apply alignment
     106                 :        932 :                 size = (size + align - 1) & ~(align - 1);
     107                 :        932 :                 size += sz;
     108                 :            :             }
     109                 :            :         }
     110                 :            :     }
     111                 :        804 :     *total_sz = size;
     112                 :        804 :     return total_cnt;
     113                 :            : }
     114                 :            : 
     115                 :        492 : static mp_obj_t struct_calcsize(mp_obj_t fmt_in) {
     116                 :        492 :     const char *fmt = mp_obj_str_get_str(fmt_in);
     117                 :        488 :     size_t size;
     118                 :        488 :     calc_size_items(fmt, &size);
     119                 :        464 :     return MP_OBJ_NEW_SMALL_INT(size);
     120                 :            : }
     121                 :            : MP_DEFINE_CONST_FUN_OBJ_1(struct_calcsize_obj, struct_calcsize);
     122                 :            : 
     123                 :        352 : static mp_obj_t struct_unpack_from(size_t n_args, const mp_obj_t *args) {
     124                 :            :     // unpack requires that the buffer be exactly the right size.
     125                 :            :     // unpack_from requires that the buffer be "big enough".
     126                 :            :     // Since we implement unpack and unpack_from using the same function
     127                 :            :     // we relax the "exact" requirement, and only implement "big enough".
     128                 :        352 :     const char *fmt = mp_obj_str_get_str(args[0]);
     129                 :        352 :     size_t total_sz;
     130                 :        352 :     size_t num_items = calc_size_items(fmt, &total_sz);
     131                 :        340 :     char fmt_type = get_fmt_type(&fmt);
     132                 :        340 :     mp_obj_tuple_t *res = MP_OBJ_TO_PTR(mp_obj_new_tuple(num_items, NULL));
     133                 :        340 :     mp_buffer_info_t bufinfo;
     134                 :        340 :     mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ);
     135                 :        340 :     byte *p = bufinfo.buf;
     136                 :        340 :     byte *end_p = &p[bufinfo.len];
     137                 :        340 :     mp_int_t offset = 0;
     138                 :            : 
     139         [ +  + ]:        340 :     if (n_args > 2) {
     140                 :            :         // offset arg provided
     141                 :         28 :         offset = mp_obj_get_int(args[2]);
     142         [ +  + ]:         28 :         if (offset < 0) {
     143                 :            :             // negative offsets are relative to the end of the buffer
     144                 :          8 :             offset = bufinfo.len + offset;
     145         [ +  + ]:          8 :             if (offset < 0) {
     146                 :          4 :                 mp_raise_ValueError(MP_ERROR_TEXT("buffer too small"));
     147                 :            :             }
     148                 :            :         }
     149                 :         24 :         p += offset;
     150                 :            :     }
     151                 :        336 :     byte *p_base = p;
     152                 :            : 
     153                 :            :     // Check that the input buffer is big enough to unpack all the values
     154         [ +  + ]:        336 :     if (p + total_sz > end_p) {
     155                 :         12 :         mp_raise_ValueError(MP_ERROR_TEXT("buffer too small"));
     156                 :            :     }
     157                 :            : 
     158         [ +  + ]:        708 :     for (size_t i = 0; i < num_items;) {
     159                 :        384 :         mp_uint_t cnt = 1;
     160         [ +  + ]:        384 :         if (unichar_isdigit(*fmt)) {
     161                 :         44 :             cnt = get_fmt_num(&fmt);
     162                 :            :         }
     163                 :        384 :         mp_obj_t item;
     164         [ +  + ]:        384 :         if (*fmt == 'x') {
     165                 :          8 :             p += cnt;
     166         [ +  + ]:        376 :         } else if (*fmt == 's') {
     167                 :         20 :             item = mp_obj_new_bytes(p, cnt);
     168                 :         20 :             p += cnt;
     169                 :         20 :             res->items[i++] = item;
     170                 :            :         } else {
     171         [ +  + ]:        728 :             while (cnt--) {
     172                 :        372 :                 item = mp_binary_get_val(fmt_type, *fmt, p_base, &p);
     173                 :        372 :                 res->items[i++] = item;
     174                 :            :             }
     175                 :            :         }
     176                 :        384 :         fmt++;
     177                 :            :     }
     178                 :        324 :     return MP_OBJ_FROM_PTR(res);
     179                 :            : }
     180                 :            : MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_unpack_from_obj, 2, 3, struct_unpack_from);
     181                 :            : 
     182                 :            : // This function assumes there is enough room in p to store all the values
     183                 :        412 : static void struct_pack_into_internal(mp_obj_t fmt_in, byte *p, size_t n_args, const mp_obj_t *args) {
     184                 :        412 :     const char *fmt = mp_obj_str_get_str(fmt_in);
     185                 :        412 :     char fmt_type = get_fmt_type(&fmt);
     186                 :            : 
     187                 :        412 :     byte *p_base = p;
     188                 :        412 :     size_t i;
     189         [ +  + ]:        886 :     for (i = 0; i < n_args;) {
     190                 :        476 :         mp_uint_t cnt = 1;
     191         [ +  + ]:        476 :         if (*fmt == '\0') {
     192                 :            :             // more arguments given than used by format string; CPython raises struct.error here
     193                 :            :             break;
     194                 :            :         }
     195         [ +  + ]:        474 :         if (unichar_isdigit(*fmt)) {
     196                 :         44 :             cnt = get_fmt_num(&fmt);
     197                 :            :         }
     198                 :            : 
     199         [ +  + ]:        474 :         if (*fmt == 'x') {
     200                 :          8 :             memset(p, 0, cnt);
     201                 :          8 :             p += cnt;
     202         [ +  + ]:        466 :         } else if (*fmt == 's') {
     203                 :         20 :             mp_buffer_info_t bufinfo;
     204                 :         20 :             mp_get_buffer_raise(args[i++], &bufinfo, MP_BUFFER_READ);
     205                 :         20 :             mp_uint_t to_copy = cnt;
     206         [ +  + ]:         20 :             if (bufinfo.len < to_copy) {
     207                 :          4 :                 to_copy = bufinfo.len;
     208                 :            :             }
     209                 :         20 :             memcpy(p, bufinfo.buf, to_copy);
     210                 :         20 :             memset(p + to_copy, 0, cnt - to_copy);
     211                 :         20 :             p += cnt;
     212                 :            :         } else {
     213                 :            :             // If we run out of args then we just finish; CPython would raise struct.error
     214   [ +  +  +  + ]:        904 :             while (cnt-- && i < n_args) {
     215                 :        458 :                 mp_binary_set_val(fmt_type, *fmt, args[i++], p_base, &p);
     216                 :            :             }
     217                 :            :         }
     218                 :        474 :         fmt++;
     219                 :            :     }
     220                 :        412 : }
     221                 :            : 
     222                 :        412 : static mp_obj_t struct_pack(size_t n_args, const mp_obj_t *args) {
     223                 :            :     // TODO: "The arguments must match the values required by the format exactly."
     224                 :        412 :     mp_int_t size = MP_OBJ_SMALL_INT_VALUE(struct_calcsize(args[0]));
     225                 :        396 :     vstr_t vstr;
     226                 :        396 :     vstr_init_len(&vstr, size);
     227                 :        396 :     byte *p = (byte *)vstr.buf;
     228                 :        396 :     memset(p, 0, size);
     229                 :        396 :     struct_pack_into_internal(args[0], p, n_args - 1, &args[1]);
     230                 :        396 :     return mp_obj_new_bytes_from_vstr(&vstr);
     231                 :            : }
     232                 :            : MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_pack_obj, 1, MP_OBJ_FUN_ARGS_MAX, struct_pack);
     233                 :            : 
     234                 :         36 : static mp_obj_t struct_pack_into(size_t n_args, const mp_obj_t *args) {
     235                 :         36 :     mp_buffer_info_t bufinfo;
     236                 :         36 :     mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE);
     237                 :         36 :     mp_int_t offset = mp_obj_get_int(args[2]);
     238         [ +  + ]:         36 :     if (offset < 0) {
     239                 :            :         // negative offsets are relative to the end of the buffer
     240                 :          8 :         offset = (mp_int_t)bufinfo.len + offset;
     241         [ +  + ]:          8 :         if (offset < 0) {
     242                 :          4 :             mp_raise_ValueError(MP_ERROR_TEXT("buffer too small"));
     243                 :            :         }
     244                 :            :     }
     245                 :         32 :     byte *p = (byte *)bufinfo.buf;
     246                 :         32 :     byte *end_p = &p[bufinfo.len];
     247                 :         32 :     p += offset;
     248                 :            : 
     249                 :            :     // Check that the output buffer is big enough to hold all the values
     250                 :         32 :     mp_int_t sz = MP_OBJ_SMALL_INT_VALUE(struct_calcsize(args[0]));
     251         [ +  + ]:         28 :     if (p + sz > end_p) {
     252                 :         12 :         mp_raise_ValueError(MP_ERROR_TEXT("buffer too small"));
     253                 :            :     }
     254                 :            : 
     255                 :         16 :     struct_pack_into_internal(args[0], p, n_args - 3, &args[3]);
     256                 :         16 :     return mp_const_none;
     257                 :            : }
     258                 :            : MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_pack_into_obj, 3, MP_OBJ_FUN_ARGS_MAX, struct_pack_into);
     259                 :            : 
     260                 :            : static const mp_rom_map_elem_t mp_module_struct_globals_table[] = {
     261                 :            :     { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_struct) },
     262                 :            :     { MP_ROM_QSTR(MP_QSTR_calcsize), MP_ROM_PTR(&struct_calcsize_obj) },
     263                 :            :     { MP_ROM_QSTR(MP_QSTR_pack), MP_ROM_PTR(&struct_pack_obj) },
     264                 :            :     { MP_ROM_QSTR(MP_QSTR_pack_into), MP_ROM_PTR(&struct_pack_into_obj) },
     265                 :            :     { MP_ROM_QSTR(MP_QSTR_unpack), MP_ROM_PTR(&struct_unpack_from_obj) },
     266                 :            :     { MP_ROM_QSTR(MP_QSTR_unpack_from), MP_ROM_PTR(&struct_unpack_from_obj) },
     267                 :            : };
     268                 :            : 
     269                 :            : static MP_DEFINE_CONST_DICT(mp_module_struct_globals, mp_module_struct_globals_table);
     270                 :            : 
     271                 :            : const mp_obj_module_t mp_module_struct = {
     272                 :            :     .base = { &mp_type_module },
     273                 :            :     .globals = (mp_obj_dict_t *)&mp_module_struct_globals,
     274                 :            : };
     275                 :            : 
     276                 :            : MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_struct, mp_module_struct);
     277                 :            : 
     278                 :            : #endif

Generated by: LCOV version 1.15-5-g462f71d