Numworks Epsilon  1.4.1
Graphing Calculator Operating System
modstruct.c
Go to the documentation of this file.
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 STATIC char get_fmt_type(const char **fmt) {
56  char t = **fmt;
57  switch (t) {
58  case '!':
59  t = '>';
60  break;
61  case '@':
62  case '=':
63  case '<':
64  case '>':
65  break;
66  default:
67  return '@';
68  }
69  // Skip type char
70  (*fmt)++;
71  return t;
72 }
73 
74 STATIC mp_uint_t get_fmt_num(const char **p) {
75  const char *num = *p;
76  uint len = 1;
77  while (unichar_isdigit(*++num)) {
78  len++;
79  }
81  *p = num;
82  return val;
83 }
84 
85 STATIC size_t calc_size_items(const char *fmt, size_t *total_sz) {
86  char fmt_type = get_fmt_type(&fmt);
87  size_t total_cnt = 0;
88  size_t size;
89  for (size = 0; *fmt; fmt++) {
90  mp_uint_t cnt = 1;
91  if (unichar_isdigit(*fmt)) {
92  cnt = get_fmt_num(&fmt);
93  }
94 
95  if (*fmt == 's') {
96  total_cnt += 1;
97  size += cnt;
98  } else {
99  total_cnt += cnt;
100  mp_uint_t align;
101  size_t sz = mp_binary_get_size(fmt_type, *fmt, &align);
102  while (cnt--) {
103  // Apply alignment
104  size = (size + align - 1) & ~(align - 1);
105  size += sz;
106  }
107  }
108  }
109  *total_sz = size;
110  return total_cnt;
111 }
112 
113 STATIC mp_obj_t struct_calcsize(mp_obj_t fmt_in) {
114  const char *fmt = mp_obj_str_get_str(fmt_in);
115  size_t size;
116  calc_size_items(fmt, &size);
117  return MP_OBJ_NEW_SMALL_INT(size);
118 }
119 MP_DEFINE_CONST_FUN_OBJ_1(struct_calcsize_obj, struct_calcsize);
120 
121 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  const char *fmt = mp_obj_str_get_str(args[0]);
127  size_t total_sz;
128  size_t num_items = calc_size_items(fmt, &total_sz);
129  char fmt_type = get_fmt_type(&fmt);
131  mp_buffer_info_t bufinfo;
132  mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ);
133  byte *p = bufinfo.buf;
134  byte *end_p = &p[bufinfo.len];
135  mp_int_t offset = 0;
136 
137  if (n_args > 2) {
138  // offset arg provided
139  offset = mp_obj_get_int(args[2]);
140  if (offset < 0) {
141  // negative offsets are relative to the end of the buffer
142  offset = bufinfo.len + offset;
143  if (offset < 0) {
144  mp_raise_ValueError("buffer too small");
145  }
146  }
147  p += offset;
148  }
149 
150  // Check that the input buffer is big enough to unpack all the values
151  if (p + total_sz > end_p) {
152  mp_raise_ValueError("buffer too small");
153  }
154 
155  for (size_t i = 0; i < num_items;) {
156  mp_uint_t cnt = 1;
157  if (unichar_isdigit(*fmt)) {
158  cnt = get_fmt_num(&fmt);
159  }
160  mp_obj_t item;
161  if (*fmt == 's') {
162  item = mp_obj_new_bytes(p, cnt);
163  p += cnt;
164  res->items[i++] = item;
165  } else {
166  while (cnt--) {
167  item = mp_binary_get_val(fmt_type, *fmt, &p);
168  res->items[i++] = item;
169  }
170  }
171  fmt++;
172  }
173  return MP_OBJ_FROM_PTR(res);
174 }
175 MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_unpack_from_obj, 2, 3, struct_unpack_from);
176 
177 // This function assumes there is enough room in p to store all the values
178 STATIC void struct_pack_into_internal(mp_obj_t fmt_in, byte *p, size_t n_args, const mp_obj_t *args) {
179  const char *fmt = mp_obj_str_get_str(fmt_in);
180  char fmt_type = get_fmt_type(&fmt);
181 
182  size_t i;
183  for (i = 0; i < n_args;) {
184  mp_uint_t cnt = 1;
185  if (*fmt == '\0') {
186  // more arguments given than used by format string; CPython raises struct.error here
187  break;
188  }
189  if (unichar_isdigit(*fmt)) {
190  cnt = get_fmt_num(&fmt);
191  }
192 
193  if (*fmt == 's') {
194  mp_buffer_info_t bufinfo;
195  mp_get_buffer_raise(args[i++], &bufinfo, MP_BUFFER_READ);
196  mp_uint_t to_copy = cnt;
197  if (bufinfo.len < to_copy) {
198  to_copy = bufinfo.len;
199  }
200  memcpy(p, bufinfo.buf, to_copy);
201  memset(p + to_copy, 0, cnt - to_copy);
202  p += cnt;
203  } else {
204  // If we run out of args then we just finish; CPython would raise struct.error
205  while (cnt-- && i < n_args) {
206  mp_binary_set_val(fmt_type, *fmt, args[i++], &p);
207  }
208  }
209  fmt++;
210  }
211 }
212 
213 STATIC mp_obj_t struct_pack(size_t n_args, const mp_obj_t *args) {
214  // TODO: "The arguments must match the values required by the format exactly."
215  mp_int_t size = MP_OBJ_SMALL_INT_VALUE(struct_calcsize(args[0]));
216  vstr_t vstr;
217  vstr_init_len(&vstr, size);
218  byte *p = (byte*)vstr.buf;
219  memset(p, 0, size);
220  struct_pack_into_internal(args[0], p, n_args - 1, &args[1]);
221  return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
222 }
223 MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_pack_obj, 1, MP_OBJ_FUN_ARGS_MAX, struct_pack);
224 
225 STATIC mp_obj_t struct_pack_into(size_t n_args, const mp_obj_t *args) {
226  mp_buffer_info_t bufinfo;
228  mp_int_t offset = mp_obj_get_int(args[2]);
229  if (offset < 0) {
230  // negative offsets are relative to the end of the buffer
231  offset = (mp_int_t)bufinfo.len + offset;
232  if (offset < 0) {
233  mp_raise_ValueError("buffer too small");
234  }
235  }
236  byte *p = (byte *)bufinfo.buf;
237  byte *end_p = &p[bufinfo.len];
238  p += offset;
239 
240  // Check that the output buffer is big enough to hold all the values
241  mp_int_t sz = MP_OBJ_SMALL_INT_VALUE(struct_calcsize(args[0]));
242  if (p + sz > end_p) {
243  mp_raise_ValueError("buffer too small");
244  }
245 
246  struct_pack_into_internal(args[0], p, n_args - 3, &args[3]);
247  return mp_const_none;
248 }
249 MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_pack_into_obj, 3, MP_OBJ_FUN_ARGS_MAX, struct_pack_into);
250 
251 STATIC const mp_rom_map_elem_t mp_module_struct_globals_table[] = {
252  { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ustruct) },
253  { MP_ROM_QSTR(MP_QSTR_calcsize), MP_ROM_PTR(&struct_calcsize_obj) },
254  { MP_ROM_QSTR(MP_QSTR_pack), MP_ROM_PTR(&struct_pack_obj) },
255  { MP_ROM_QSTR(MP_QSTR_pack_into), MP_ROM_PTR(&struct_pack_into_obj) },
256  { MP_ROM_QSTR(MP_QSTR_unpack), MP_ROM_PTR(&struct_unpack_from_obj) },
257  { MP_ROM_QSTR(MP_QSTR_unpack_from), MP_ROM_PTR(&struct_unpack_from_obj) },
258 };
259 
260 STATIC MP_DEFINE_CONST_DICT(mp_module_struct_globals, mp_module_struct_globals_table);
261 
263  .base = { &mp_type_module },
264  .globals = (mp_obj_dict_t*)&mp_module_struct_globals,
265 };
266 
267 #endif
intptr_t mp_int_t
Definition: mpconfigport.h:73
#define MP_BUFFER_WRITE
Definition: obj.h:455
uintptr_t mp_uint_t
Definition: mpconfigport.h:74
Definition: misc.h:142
#define MP_BUFFER_READ
Definition: obj.h:454
void * memset(void *b, int c, size_t len)
Definition: memset.c:3
void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte **ptr)
Definition: binary.c:252
mp_obj_t mp_obj_new_tuple(size_t n, const mp_obj_t *items)
Definition: objtuple.c:235
#define mp_const_none
Definition: obj.h:614
#define MP_DEFINE_CONST_DICT(dict_name, table_name)
Definition: obj.h:317
void vstr_init_len(vstr_t *vstr, size_t len)
Definition: vstr.c:52
char * buf
Definition: misc.h:145
#define MP_ROM_QSTR(q)
Definition: obj.h:241
#define MP_OBJ_FROM_PTR(p)
Definition: obj.h:233
size_t mp_binary_get_size(char struct_type, char val_type, mp_uint_t *palign)
Definition: binary.c:44
#define MP_ROM_PTR(p)
Definition: obj.h:242
const mp_obj_module_t mp_module_ustruct
const mp_obj_type_t mp_type_bytes
Definition: objstr.c:1964
size_t len
Definition: obj.h:447
mp_obj_base_t base
Definition: obj.h:814
mp_int_t mp_obj_get_int(mp_const_obj_t arg)
Definition: obj.c:225
bool unichar_isdigit(unichar c)
Definition: unicode.c:142
#define STATIC
Definition: mpconfig.h:1178
#define MP_OBJ_SMALL_INT_VALUE(o)
Definition: obj.h:86
mp_obj_t items[]
Definition: objtuple.h:34
#define MP_DEFINE_CONST_FUN_OBJ_1(obj_name, fun_name)
Definition: obj.h:285
#define MP_OBJ_NEW_SMALL_INT(small_int)
Definition: obj.h:87
#define NULL
Definition: stddef.h:4
mp_obj_t mp_obj_new_bytes(const byte *data, size_t len)
Definition: objstr.c:2046
args
Definition: i18n.py:175
#define MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(obj_name, n_args_min, n_args_max, fun_name)
Definition: obj.h:297
unsigned char byte
Definition: misc.h:37
mp_obj_t mp_obj_new_str_from_vstr(const mp_obj_type_t *type, vstr_t *vstr)
Definition: objstr.c:1998
NORETURN void mp_raise_ValueError(const char *msg)
Definition: runtime.c:1456
void mp_get_buffer_raise(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags)
Definition: obj.c:524
const char * mp_obj_str_get_str(mp_obj_t self_in)
Definition: objstr.c:2095
#define MP_OBJ_TO_PTR(o)
Definition: obj.h:228
const mp_obj_type_t mp_type_module
Definition: objmodule.c:94
#define MP_OBJ_FUN_ARGS_MAX
Definition: obj.h:793
mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, mp_lexer_t *lex)
Definition: parsenum.c:49
mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte **ptr)
Definition: binary.c:187
uint64_t mp_obj_t
Definition: obj.h:39
void * memcpy(void *dst, const void *src, size_t n)
Definition: memcpy.c:3
void * buf
Definition: obj.h:446
unsigned int uint
Definition: misc.h:38