blob: 114135dfd12c57a89bf6de9223a9a60a470d2be4 [file] [log] [blame]
Damien George04b91472014-05-03 23:27:38 +01001/*
2 * This file is part of the Micro Python project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2013, 2014 Damien P. George
Paul Sokolovskyda9f0922014-05-13 08:44:45 +03007 * Copyright (c) 2014 Paul Sokolovsky
Damien George04b91472014-05-03 23:27:38 +01008 *
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
Paul Sokolovskye9db8402014-04-10 03:45:38 +030028#include <assert.h>
29#include <string.h>
Damien George51dfcb42015-01-01 20:27:54 +000030
31#include "py/builtin.h"
32#include "py/objtuple.h"
33#include "py/binary.h"
34#include "py/parsenum.h"
Paul Sokolovskye9db8402014-04-10 03:45:38 +030035
Damien Georgeee3fd462014-05-24 23:03:12 +010036#if MICROPY_PY_STRUCT
Paul Sokolovskye9db8402014-04-10 03:45:38 +030037
Paul Sokolovsky62798832014-06-02 16:04:26 +030038/*
39 This module implements most of character typecodes from CPython, with
40 some extensions:
41
42 O - (Pointer to) an arbitrary Python object. This is useful for callback
43 data, etc. Note that you must keep reference to passed object in
44 your Python application, otherwise it may be garbage-collected,
45 and then when you get back this value from callback it may be
46 invalid (and lead to crash).
47 S - Pointer to a string (returned as a Python string). Note the
48 difference from "Ns", - the latter says "in this place of structure
49 is character data of up to N bytes length", while "S" means
50 "in this place of a structure is a pointer to zero-terminated
51 character data".
52 */
53
Paul Sokolovskye9db8402014-04-10 03:45:38 +030054STATIC char get_fmt_type(const char **fmt) {
55 char t = **fmt;
56 switch (t) {
57 case '!':
58 t = '>';
59 break;
60 case '@':
61 case '=':
62 case '<':
63 case '>':
64 break;
65 default:
66 return '@';
67 }
68 // Skip type char
69 (*fmt)++;
70 return t;
71}
72
Damien George40f3c022014-07-03 13:25:24 +010073STATIC mp_uint_t get_fmt_num(const char **p) {
Paul Sokolovskydf94b712014-05-12 23:45:50 +030074 const char *num = *p;
75 uint len = 1;
76 while (unichar_isdigit(*++num)) {
77 len++;
78 }
Damien George40f3c022014-07-03 13:25:24 +010079 mp_uint_t val = (mp_uint_t)MP_OBJ_SMALL_INT_VALUE(mp_parse_num_integer(*p, len, 10));
Paul Sokolovskydf94b712014-05-12 23:45:50 +030080 *p = num;
81 return val;
82}
83
Paul Sokolovskye9db8402014-04-10 03:45:38 +030084STATIC uint calcsize_items(const char *fmt) {
Paul Sokolovskydf94b712014-05-12 23:45:50 +030085 uint cnt = 0;
86 while (*fmt) {
87 // TODO supports size spec only for "s"
88 if (!unichar_isdigit(*fmt++)) {
89 cnt++;
90 }
91 }
92 return cnt;
Paul Sokolovskye9db8402014-04-10 03:45:38 +030093}
94
95STATIC mp_obj_t struct_calcsize(mp_obj_t fmt_in) {
96 const char *fmt = mp_obj_str_get_str(fmt_in);
97 char fmt_type = get_fmt_type(&fmt);
Damien George40f3c022014-07-03 13:25:24 +010098 mp_uint_t size;
Paul Sokolovskye9db8402014-04-10 03:45:38 +030099 for (size = 0; *fmt; fmt++) {
Damien George4abff752014-08-30 14:59:21 +0100100 mp_uint_t align = 1;
Damien George40f3c022014-07-03 13:25:24 +0100101 mp_uint_t cnt = 1;
Paul Sokolovskydf94b712014-05-12 23:45:50 +0300102 if (unichar_isdigit(*fmt)) {
103 cnt = get_fmt_num(&fmt);
104 }
105 if (cnt > 1) {
106 // TODO: count spec support only for string len
107 assert(*fmt == 's');
108 }
109
Damien George40f3c022014-07-03 13:25:24 +0100110 mp_uint_t sz;
Paul Sokolovskydf94b712014-05-12 23:45:50 +0300111 if (*fmt == 's') {
112 sz = cnt;
113 } else {
Damien George40f3c022014-07-03 13:25:24 +0100114 sz = (mp_uint_t)mp_binary_get_size(fmt_type, *fmt, &align);
Paul Sokolovskydf94b712014-05-12 23:45:50 +0300115 }
Paul Sokolovskye9db8402014-04-10 03:45:38 +0300116 // TODO
Damien George40f3c022014-07-03 13:25:24 +0100117 assert(sz != (mp_uint_t)-1);
Paul Sokolovsky1355cf42014-04-19 01:25:49 +0300118 // Apply alignment
119 size = (size + align - 1) & ~(align - 1);
Paul Sokolovskye9db8402014-04-10 03:45:38 +0300120 size += sz;
121 }
122 return MP_OBJ_NEW_SMALL_INT(size);
123}
124MP_DEFINE_CONST_FUN_OBJ_1(struct_calcsize_obj, struct_calcsize);
125
126STATIC mp_obj_t struct_unpack(mp_obj_t fmt_in, mp_obj_t data_in) {
127 // TODO: "The buffer must contain exactly the amount of data required by the format (len(bytes) must equal calcsize(fmt))."
128 const char *fmt = mp_obj_str_get_str(fmt_in);
129 char fmt_type = get_fmt_type(&fmt);
Paul Sokolovskye9db8402014-04-10 03:45:38 +0300130 uint size = calcsize_items(fmt);
131 mp_obj_tuple_t *res = mp_obj_new_tuple(size, NULL);
Damien George57a4b4f2014-04-18 22:29:21 +0100132 mp_buffer_info_t bufinfo;
Damien Georgeb11b85a2014-04-18 22:59:24 +0100133 mp_get_buffer_raise(data_in, &bufinfo, MP_BUFFER_READ);
Paul Sokolovskye9db8402014-04-10 03:45:38 +0300134 byte *p = bufinfo.buf;
135
136 for (uint i = 0; i < size; i++) {
Damien George40f3c022014-07-03 13:25:24 +0100137 mp_uint_t sz = 1;
Paul Sokolovskydf94b712014-05-12 23:45:50 +0300138 if (unichar_isdigit(*fmt)) {
139 sz = get_fmt_num(&fmt);
140 }
141 if (sz > 1) {
142 // TODO: size spec support only for string len
143 assert(*fmt == 's');
144 }
145 mp_obj_t item;
146 if (*fmt == 's') {
147 item = mp_obj_new_bytes(p, sz);
148 p += sz;
149 fmt++;
150 } else {
151 item = mp_binary_get_val(fmt_type, *fmt++, &p);
152 }
Paul Sokolovskye9db8402014-04-10 03:45:38 +0300153 res->items[i] = item;
154 }
155 return res;
156}
157MP_DEFINE_CONST_FUN_OBJ_2(struct_unpack_obj, struct_unpack);
158
Damien George9336ee32014-10-06 15:05:35 +0000159STATIC mp_obj_t struct_pack(mp_uint_t n_args, const mp_obj_t *args) {
Paul Sokolovsky62044602014-04-19 03:13:15 +0300160 // TODO: "The arguments must match the values required by the format exactly."
161 const char *fmt = mp_obj_str_get_str(args[0]);
162 char fmt_type = get_fmt_type(&fmt);
Damien George42f3de92014-10-03 17:44:14 +0000163 mp_int_t size = MP_OBJ_SMALL_INT_VALUE(struct_calcsize(args[0]));
Paul Sokolovsky62044602014-04-19 03:13:15 +0300164 byte *p;
165 mp_obj_t res = mp_obj_str_builder_start(&mp_type_bytes, size, &p);
166 memset(p, 0, size);
167
Damien George9336ee32014-10-06 15:05:35 +0000168 for (mp_uint_t i = 1; i < n_args; i++) {
Damien George40f3c022014-07-03 13:25:24 +0100169 mp_uint_t sz = 1;
Paul Sokolovskydf94b712014-05-12 23:45:50 +0300170 if (unichar_isdigit(*fmt)) {
171 sz = get_fmt_num(&fmt);
172 }
173 if (sz > 1) {
174 // TODO: size spec support only for string len
175 assert(*fmt == 's');
176 }
177
178 if (*fmt == 's') {
179 mp_buffer_info_t bufinfo;
180 mp_get_buffer_raise(args[i], &bufinfo, MP_BUFFER_READ);
Damien George40f3c022014-07-03 13:25:24 +0100181 mp_uint_t to_copy = sz;
Paul Sokolovskydf94b712014-05-12 23:45:50 +0300182 if (bufinfo.len < to_copy) {
183 to_copy = bufinfo.len;
184 }
185 memcpy(p, bufinfo.buf, to_copy);
186 memset(p + to_copy, 0, sz - to_copy);
187 p += sz;
188 fmt++;
189 } else {
190 mp_binary_set_val(fmt_type, *fmt++, args[i], &p);
191 }
Paul Sokolovsky62044602014-04-19 03:13:15 +0300192 }
193 return res;
194}
Paul Sokolovsky147c80b2014-05-11 22:50:27 +0300195MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_pack_obj, 1, MP_OBJ_FUN_ARGS_MAX, struct_pack);
Paul Sokolovsky62044602014-04-19 03:13:15 +0300196
Paul Sokolovskye9db8402014-04-10 03:45:38 +0300197STATIC const mp_map_elem_t mp_module_struct_globals_table[] = {
198 { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_struct) },
199 { MP_OBJ_NEW_QSTR(MP_QSTR_calcsize), (mp_obj_t)&struct_calcsize_obj },
Paul Sokolovsky62044602014-04-19 03:13:15 +0300200 { MP_OBJ_NEW_QSTR(MP_QSTR_pack), (mp_obj_t)&struct_pack_obj },
Paul Sokolovskye9db8402014-04-10 03:45:38 +0300201 { MP_OBJ_NEW_QSTR(MP_QSTR_unpack), (mp_obj_t)&struct_unpack_obj },
202};
203
Damien George3b603f22014-11-29 14:39:27 +0000204STATIC MP_DEFINE_CONST_DICT(mp_module_struct_globals, mp_module_struct_globals_table);
Paul Sokolovskye9db8402014-04-10 03:45:38 +0300205
206const mp_obj_module_t mp_module_struct = {
207 .base = { &mp_type_module },
208 .name = MP_QSTR_struct,
209 .globals = (mp_obj_dict_t*)&mp_module_struct_globals,
210};
211
212#endif