From 2559e1395700caa3d3ffd21f3f9178d0b90004bf Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 30 Oct 2014 02:43:53 +0200 Subject: [PATCH] moductypes: Make sure we can apply .sizeof() to all aggregate types. Before, sizeof() could be applied to a structure field only if that field was itself a structure. Now it can be applied to PTR and ARRAY fields too. It's not possible to apply it to scalar fields though, because as soon as scalar field (int or float) is dereferenced, its value is converted into Python int/float value, and all original type info is lost. Moreover, we allow sizeof of type definitions too, and there int is used to represent (scalar) types. So, we have ambiguity what int may be - either dereferenced scalar structure field, or encoded scalar type. So, rather throw an error if user tries to apply sizeof() to int. --- extmod/moductypes.c | 101 +++++++++++++++++------------ tests/extmod/uctypes_sizeof.py | 26 ++++++++ tests/extmod/uctypes_sizeof.py.exp | 4 ++ 3 files changed, 90 insertions(+), 41 deletions(-) create mode 100644 tests/extmod/uctypes_sizeof.py create mode 100644 tests/extmod/uctypes_sizeof.py.exp diff --git a/extmod/moductypes.c b/extmod/moductypes.c index bc18247656..6010e77aa8 100644 --- a/extmod/moductypes.c +++ b/extmod/moductypes.c @@ -106,6 +106,7 @@ enum { #define GET_SCALAR_SIZE(val_type) (1 << ((val_type) >> 1)) #define VALUE_MASK(type_nbits) ~((int)0x80000000 >> type_nbits) +// "struct" in uctypes context means "structural", i.e. aggregate, type. STATIC const mp_obj_type_t uctypes_struct_type; typedef struct _mp_obj_uctypes_struct_t { @@ -153,6 +154,10 @@ STATIC void uctypes_struct_print(void (*print)(void *env, const char *fmt, ...), print(env, "", typen, self->addr); } +// Get size of any type descriptor +STATIC mp_uint_t uctypes_struct_size(mp_obj_t desc_in, mp_uint_t *max_field_size); + +// Get size of scalar type descriptor static inline mp_uint_t uctypes_struct_scalar_size(int val_type) { if (val_type == FLOAT32) { return 4; @@ -161,11 +166,60 @@ static inline mp_uint_t uctypes_struct_scalar_size(int val_type) { } } +// Get size of aggregate type descriptor +STATIC mp_uint_t uctypes_struct_agg_size(mp_obj_tuple_t *t, mp_uint_t *max_field_size) { + mp_uint_t total_size = 0; + + mp_int_t offset_ = MP_OBJ_SMALL_INT_VALUE(t->items[0]); + mp_uint_t agg_type = GET_TYPE(offset_, AGG_TYPE_BITS); + + switch (agg_type) { + case STRUCT: + return uctypes_struct_size(t->items[1], max_field_size); + case PTR: + if (sizeof(void*) > *max_field_size) { + *max_field_size = sizeof(void*); + } + return sizeof(void*); + case ARRAY: { + mp_int_t arr_sz = MP_OBJ_SMALL_INT_VALUE(t->items[1]); + uint val_type = GET_TYPE(arr_sz, VAL_TYPE_BITS); + arr_sz &= VALUE_MASK(VAL_TYPE_BITS); + mp_uint_t item_s; + if (t->len == 2) { + // Elements of array are scalar + item_s = GET_SCALAR_SIZE(val_type); + if (item_s > *max_field_size) { + *max_field_size = item_s; + } + } else { + // Elements of array are aggregates + item_s = uctypes_struct_size(t->items[2], max_field_size); + } + + return item_s * arr_sz; + } + default: + assert(0); + } + + return total_size; +} + STATIC mp_uint_t uctypes_struct_size(mp_obj_t desc_in, mp_uint_t *max_field_size) { mp_obj_dict_t *d = desc_in; mp_uint_t total_size = 0; if (!MP_OBJ_IS_TYPE(desc_in, &mp_type_dict)) { + if (MP_OBJ_IS_TYPE(desc_in, &mp_type_tuple)) { + return uctypes_struct_agg_size((mp_obj_tuple_t*)desc_in, max_field_size); + } else if (MP_OBJ_IS_SMALL_INT(desc_in)) { + // We allow sizeof on both type definitions and structures/structure fields, + // but scalar structure field is lowered into native Python int, so all + // type info is lost. So, we cannot say if it's scalar type description, + // or such lowered scalar. + nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "Cannot unambiguously get sizeof scalar")); + } syntax_error(); } @@ -189,48 +243,10 @@ STATIC mp_uint_t uctypes_struct_size(mp_obj_t desc_in, mp_uint_t *max_field_size } mp_obj_tuple_t *t = (mp_obj_tuple_t*)v; mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(t->items[0]); - mp_uint_t agg_type = GET_TYPE(offset, AGG_TYPE_BITS); offset &= VALUE_MASK(AGG_TYPE_BITS); - - switch (agg_type) { - case STRUCT: { - mp_uint_t s = uctypes_struct_size(t->items[1], max_field_size); - if (offset + s > total_size) { - total_size = offset + s; - } - break; - } - case PTR: { - if (offset + sizeof(void*) > total_size) { - total_size = offset + sizeof(void*); - } - if (sizeof(void*) > *max_field_size) { - *max_field_size = sizeof(void*); - } - break; - } - case ARRAY: { - mp_int_t arr_sz = MP_OBJ_SMALL_INT_VALUE(t->items[1]); - uint val_type = GET_TYPE(arr_sz, VAL_TYPE_BITS); - arr_sz &= VALUE_MASK(VAL_TYPE_BITS); - mp_uint_t item_s; - if (t->len == 2) { - item_s = GET_SCALAR_SIZE(val_type); - if (item_s > *max_field_size) { - *max_field_size = item_s; - } - } else { - item_s = uctypes_struct_size(t->items[2], max_field_size); - } - - mp_uint_t byte_sz = item_s * arr_sz; - if (offset + byte_sz > total_size) { - total_size = offset + byte_sz; - } - break; - } - default: - assert(0); + mp_uint_t s = uctypes_struct_agg_size(t, max_field_size); + if (offset + s > total_size) { + total_size = offset + s; } } } @@ -243,7 +259,10 @@ STATIC mp_uint_t uctypes_struct_size(mp_obj_t desc_in, mp_uint_t *max_field_size STATIC mp_obj_t uctypes_struct_sizeof(mp_obj_t obj_in) { mp_uint_t max_field_size = 0; + // We can apply sizeof either to structure definition (a dict) + // or to instantiated structure if (MP_OBJ_IS_TYPE(obj_in, &uctypes_struct_type)) { + // Extract structure definition mp_obj_uctypes_struct_t *obj = obj_in; obj_in = obj->desc; } diff --git a/tests/extmod/uctypes_sizeof.py b/tests/extmod/uctypes_sizeof.py new file mode 100644 index 0000000000..f6551a7382 --- /dev/null +++ b/tests/extmod/uctypes_sizeof.py @@ -0,0 +1,26 @@ +import uctypes + +desc = { + # arr is array at offset 0, of UINT8 elements, array size is 2 + "arr": (uctypes.ARRAY | 0, uctypes.UINT8 | 2), + # arr2 is array at offset 0, size 2, of structures defined recursively + "arr2": (uctypes.ARRAY | 0, 2, {"b": uctypes.UINT8 | 0}), + "arr3": (uctypes.ARRAY | 2, uctypes.UINT16 | 2), +} + +data = bytearray(b"01234567") + +S = uctypes.struct(desc, uctypes.addressof(data), uctypes.LITTLE_ENDIAN) + +print(uctypes.sizeof(S.arr)) +assert uctypes.sizeof(S.arr) == 2 + +print(uctypes.sizeof(S.arr2)) +assert uctypes.sizeof(S.arr2) == 2 + +print(uctypes.sizeof(S.arr3)) + +try: + print(uctypes.sizeof(S.arr3[0])) +except TypeError: + print("TypeError") diff --git a/tests/extmod/uctypes_sizeof.py.exp b/tests/extmod/uctypes_sizeof.py.exp new file mode 100644 index 0000000000..d1e81238b4 --- /dev/null +++ b/tests/extmod/uctypes_sizeof.py.exp @@ -0,0 +1,4 @@ +2 +2 +4 +TypeError