@@ -132,22 +132,45 @@ STATIC mp_obj_t struct_calcsize(mp_obj_t fmt_in) {
132132}
133133MP_DEFINE_CONST_FUN_OBJ_1 (struct_calcsize_obj , struct_calcsize );
134134
135- STATIC mp_obj_t struct_unpack (mp_obj_t fmt_in , mp_obj_t data_in ) {
136- // TODO: "The buffer must contain exactly the amount of data required by the format (len(bytes) must equal calcsize(fmt))."
137- const char * fmt = mp_obj_str_get_str (fmt_in );
135+ STATIC mp_obj_t struct_unpack_from (size_t n_args , const mp_obj_t * args ) {
136+ // unpack requires that the buffer be exactly the right size.
137+ // unpack_from requires that the buffer be "big enough".
138+ // Since we implement unpack and unpack_from using the same function
139+ // we relax the "exact" requirement, and only implement "big enough".
140+ const char * fmt = mp_obj_str_get_str (args [0 ]);
138141 char fmt_type = get_fmt_type (& fmt );
139- uint size = calcsize_items (fmt );
140- mp_obj_tuple_t * res = MP_OBJ_TO_PTR (mp_obj_new_tuple (size , NULL ));
142+ uint num_items = calcsize_items (fmt );
143+ mp_obj_tuple_t * res = MP_OBJ_TO_PTR (mp_obj_new_tuple (num_items , NULL ));
141144 mp_buffer_info_t bufinfo ;
142- mp_get_buffer_raise (data_in , & bufinfo , MP_BUFFER_READ );
145+ mp_get_buffer_raise (args [ 1 ] , & bufinfo , MP_BUFFER_READ );
143146 byte * p = bufinfo .buf ;
147+ byte * end_p = & p [bufinfo .len ];
148+ mp_int_t offset = 0 ;
144149
145- for (uint i = 0 ; i < size ;) {
150+ if (n_args > 2 ) {
151+ // offset arg provided
152+ offset = mp_obj_get_int (args [2 ]);
153+ if (offset < 0 ) {
154+ // negative offsets are relative to the end of the buffer
155+ offset = bufinfo .len + offset ;
156+ if (offset < 0 ) {
157+ nlr_raise (mp_obj_new_exception_msg (& mp_type_ValueError , "buffer too small" ));
158+ }
159+ }
160+ p += offset ;
161+ }
162+
163+ for (uint i = 0 ; i < num_items ;) {
164+ if (* fmt == '\0' ) {
165+ break ;
166+ }
146167 mp_uint_t sz = 1 ;
147168 if (unichar_isdigit (* fmt )) {
148169 sz = get_fmt_num (& fmt );
149170 }
150-
171+ if (p + sz > end_p ) {
172+ nlr_raise (mp_obj_new_exception_msg (& mp_type_ValueError , "buffer too small" ));
173+ }
151174 mp_obj_t item ;
152175 if (* fmt == 's' ) {
153176 item = mp_obj_new_bytes (p , sz );
@@ -163,23 +186,24 @@ STATIC mp_obj_t struct_unpack(mp_obj_t fmt_in, mp_obj_t data_in) {
163186 }
164187 return MP_OBJ_FROM_PTR (res );
165188}
166- MP_DEFINE_CONST_FUN_OBJ_2 ( struct_unpack_obj , struct_unpack );
189+ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN ( struct_unpack_from_obj , 2 , 3 , struct_unpack_from );
167190
168- STATIC mp_obj_t struct_pack (size_t n_args , const mp_obj_t * args ) {
169- // TODO: "The arguments must match the values required by the format exactly."
170- const char * fmt = mp_obj_str_get_str (args [0 ]);
191+ STATIC void struct_pack_into_internal (mp_obj_t fmt_in , byte * p , byte * end_p , size_t n_args , const mp_obj_t * args ) {
192+ const char * fmt = mp_obj_str_get_str (fmt_in );
171193 char fmt_type = get_fmt_type (& fmt );
172- mp_int_t size = MP_OBJ_SMALL_INT_VALUE (struct_calcsize (args [0 ]));
173- vstr_t vstr ;
174- vstr_init_len (& vstr , size );
175- byte * p = (byte * )vstr .buf ;
176- memset (p , 0 , size );
177194
178- for (mp_uint_t i = 1 ; i < n_args ;) {
195+ size_t i ;
196+ for (i = 0 ; i < n_args ;) {
179197 mp_uint_t sz = 1 ;
198+ if (* fmt == '\0' ) {
199+ break ;
200+ }
180201 if (unichar_isdigit (* fmt )) {
181202 sz = get_fmt_num (& fmt );
182203 }
204+ if (p + sz > end_p ) {
205+ nlr_raise (mp_obj_new_exception_msg (& mp_type_ValueError , "buffer too small" ));
206+ }
183207
184208 if (* fmt == 's' ) {
185209 mp_buffer_info_t bufinfo ;
@@ -198,16 +222,48 @@ STATIC mp_obj_t struct_pack(size_t n_args, const mp_obj_t *args) {
198222 }
199223 fmt ++ ;
200224 }
225+ }
201226
227+ STATIC mp_obj_t struct_pack (size_t n_args , const mp_obj_t * args ) {
228+ // TODO: "The arguments must match the values required by the format exactly."
229+ mp_int_t size = MP_OBJ_SMALL_INT_VALUE (struct_calcsize (args [0 ]));
230+ vstr_t vstr ;
231+ vstr_init_len (& vstr , size );
232+ byte * p = (byte * )vstr .buf ;
233+ memset (p , 0 , size );
234+ byte * end_p = & p [size ];
235+ struct_pack_into_internal (args [0 ], p , end_p , n_args - 1 , & args [1 ]);
202236 return mp_obj_new_str_from_vstr (& mp_type_bytes , & vstr );
203237}
204238MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (struct_pack_obj , 1 , MP_OBJ_FUN_ARGS_MAX , struct_pack );
205239
240+ STATIC mp_obj_t struct_pack_into (size_t n_args , const mp_obj_t * args ) {
241+ mp_buffer_info_t bufinfo ;
242+ mp_get_buffer_raise (args [1 ], & bufinfo , MP_BUFFER_WRITE );
243+ mp_int_t offset = mp_obj_get_int (args [2 ]);
244+ if (offset < 0 ) {
245+ // negative offsets are relative to the end of the buffer
246+ offset = (mp_int_t )bufinfo .len + offset ;
247+ if (offset < 0 ) {
248+ nlr_raise (mp_obj_new_exception_msg (& mp_type_ValueError , "buffer too small" ));
249+ }
250+ }
251+ byte * p = (byte * )bufinfo .buf ;
252+ byte * end_p = & p [bufinfo .len ];
253+ p += offset ;
254+
255+ struct_pack_into_internal (args [0 ], p , end_p , n_args - 3 , & args [3 ]);
256+ 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+
206260STATIC const mp_rom_map_elem_t mp_module_struct_globals_table [] = {
207261 { MP_ROM_QSTR (MP_QSTR___name__ ), MP_ROM_QSTR (MP_QSTR_ustruct ) },
208262 { MP_ROM_QSTR (MP_QSTR_calcsize ), MP_ROM_PTR (& struct_calcsize_obj ) },
209263 { MP_ROM_QSTR (MP_QSTR_pack ), MP_ROM_PTR (& struct_pack_obj ) },
210- { MP_ROM_QSTR (MP_QSTR_unpack ), MP_ROM_PTR (& struct_unpack_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 ) },
211267};
212268
213269STATIC MP_DEFINE_CONST_DICT (mp_module_struct_globals , mp_module_struct_globals_table );
0 commit comments