summaryrefslogtreecommitdiff
path: root/py/modstruct.c
diff options
context:
space:
mode:
authorJeff Epler <jepler@gmail.com>2020-09-12 13:57:31 -0500
committerJeff Epler <jepler@gmail.com>2020-09-12 14:07:23 -0500
commit54d97251fe2dd4939652a186bf703885e654b4d1 (patch)
treedaa39c435669778a4c1b02dbc4c47e087fd80db6 /py/modstruct.c
parentb24d3b886af1d4fbbe481961a3b3215fcc0ba799 (diff)
modstruct: Improve compliance with python3
While checking whether we can enable -Wimplicit-fallthrough, I encountered a diagnostic in mp_binary_set_val_array_from_int which led to discovering the following bug: ``` >>> struct.pack("xb", 3) b'\x03\x03' ``` That is, the next value (3) was used as the value of a padding byte, while standard Python always fills "x" bytes with zeros. I initially thought this had to do with the unintentional fallthrough, but it doesn't. Instead, this code would relate to an array.array with a typecode of padding ('x'), which is ALSO not desktop Python compliant: ``` >>> array.array('x', (1, 2, 3)) array('x', [1, 0, 0]) ``` Possibly this is dead code that used to be shared between struct-setting and array-setting, but it no longer is. I also discovered that the argument list length for struct.pack and struct.pack_into were not checked, and that the length of binary data passed to array.array was not checked to be a multiple of the element size. I have corrected all of these to conform more closely to standard Python and revised some tests where necessary. Some tests for micropython-specific behavior that does not conform to standard Python and is not present in CircuitPython was deleted outright.
Diffstat (limited to 'py/modstruct.c')
-rw-r--r--py/modstruct.c17
1 files changed, 10 insertions, 7 deletions
diff --git a/py/modstruct.c b/py/modstruct.c
index fe766a4de..7675de275 100644
--- a/py/modstruct.c
+++ b/py/modstruct.c
@@ -183,16 +183,21 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_unpack_from_obj, 2, 3, struct_unpack_
// This function assumes there is enough room in p to store all the values
STATIC void struct_pack_into_internal(mp_obj_t fmt_in, byte *p, size_t n_args, const mp_obj_t *args) {
+ size_t size;
+ size_t count = calc_size_items(mp_obj_str_get_str(fmt_in), &size);
+ if (count != n_args) {
+#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
+ mp_raise_ValueError(NULL);
+#else
+ mp_raise_ValueError_varg(translate("pack expected %d items for packing (got %d)"), count, n_args);
+#endif
+ }
const char *fmt = mp_obj_str_get_str(fmt_in);
char fmt_type = get_fmt_type(&fmt);
size_t i;
for (i = 0; i < n_args;) {
mp_uint_t cnt = 1;
- if (*fmt == '\0') {
- // more arguments given than used by format string; CPython raises struct.error here
- break;
- }
if (unichar_isdigit(*fmt)) {
cnt = get_fmt_num(&fmt);
}
@@ -208,8 +213,7 @@ STATIC void struct_pack_into_internal(mp_obj_t fmt_in, byte *p, size_t n_args, c
memset(p + to_copy, 0, cnt - to_copy);
p += cnt;
} else {
- // If we run out of args then we just finish; CPython would raise struct.error
- while (cnt-- && i < n_args) {
+ while (cnt--) {
mp_binary_set_val(fmt_type, *fmt, args[i], &p);
// Pad bytes don't have a corresponding argument.
if (*fmt != 'x') {
@@ -222,7 +226,6 @@ STATIC void struct_pack_into_internal(mp_obj_t fmt_in, byte *p, size_t n_args, c
}
STATIC mp_obj_t struct_pack(size_t n_args, const mp_obj_t *args) {
- // TODO: "The arguments must match the values required by the format exactly."
mp_int_t size = MP_OBJ_SMALL_INT_VALUE(struct_calcsize(args[0]));
vstr_t vstr;
vstr_init_len(&vstr, size);