Skip to content

py/objint,py/binary: Add int.to_bytes(signed) parameter, add common overflow checks. #16311

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 15 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 81 additions & 15 deletions extmod/moductypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,12 @@ enum {
#define TYPE2SMALLINT(x, nbits) ((((int)x) << (32 - nbits)) >> 1)
#define GET_TYPE(x, nbits) (((x) >> (31 - nbits)) & ((1 << nbits) - 1))
// Bit 0 is "is_signed"
#define GET_SCALAR_SIZE(val_type) (1 << ((val_type) >> 1))
#define GET_SCALAR_SIZE(val_type) (1 << (((val_type) & 7) >> 1))
#define VALUE_MASK(type_nbits) ~((int)0x80000000 >> type_nbits)

#define INT_TYPE_IS_SIGNED(TYPE) ((TYPE) & 1)
#define INT_TYPE_TO_UNSIGNED(TYPE) ((TYPE) & 6)

#define IS_SCALAR_ARRAY(tuple_desc) ((tuple_desc)->len == 2)
// We cannot apply the below to INT8, as their range [-128, 127]
#define IS_SCALAR_ARRAY_OF_BYTES(tuple_desc) (GET_TYPE(MP_OBJ_SMALL_INT_VALUE((tuple_desc)->items[1]), VAL_TYPE_BITS) == UINT8)
Expand Down Expand Up @@ -137,7 +140,7 @@ static inline mp_uint_t uctypes_struct_scalar_size(int val_type) {
if (val_type == FLOAT32) {
return 4;
} else {
return GET_SCALAR_SIZE(val_type & 7);
return GET_SCALAR_SIZE(val_type);
}
}

Expand Down Expand Up @@ -305,6 +308,12 @@ static inline mp_uint_t get_aligned_basic(uint val_type, void *p) {
return 0;
}

#if MICROPY_PREVIEW_VERSION_2
static void raise_overflow_exception(void) {
mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("value would truncate"));
}
#endif

static inline void set_aligned_basic(uint val_type, void *p, mp_uint_t v) {
switch (val_type) {
case UINT8:
Expand Down Expand Up @@ -361,7 +370,58 @@ static void set_aligned(uint val_type, void *p, mp_int_t index, mp_obj_t val) {
return;
}
#endif

// Special case where mp_int_t can't hold the target type, fall through
if (sizeof(mp_int_t) < 8 && (val_type == INT64 || val_type == UINT64)) {
// Doesn't offer atomic store semantics, but should at least try
set_unaligned(val_type, (void *)&((uint64_t *)p)[index], MP_ENDIANNESS_BIG, val);
return;
}

#if MICROPY_PREVIEW_VERSION_2
// V2 raises exception if setting int will truncate
mp_int_t v;
bool ok = mp_obj_get_int_maybe(val, &v);
if (ok) {
switch (val_type) {
case UINT8:
ok = (v == (uint8_t)v);
break;
case INT8:
ok = (v == (int8_t)v);
break;
case UINT16:
ok = (v == (uint16_t)v);
break;
case INT16:
ok = (v == (int16_t)v);
break;
case UINT32:
ok = (v == (uint32_t)v);
break;
case INT32:
ok = (v == (int32_t)v);
break;
case UINT64:
assert(sizeof(mp_int_t) == 8);
ok = v >= 0;
break;
case INT64:
assert(sizeof(mp_int_t) == 8);
break;
default:
assert(0);
ok = false;
}
if (!ok) {
raise_overflow_exception();
}
}

#else
mp_int_t v = mp_obj_get_int_truncated(val);
#endif

switch (val_type) {
case UINT8:
((uint8_t *)p)[index] = (uint8_t)v;
Expand All @@ -383,12 +443,8 @@ static void set_aligned(uint val_type, void *p, mp_int_t index, mp_obj_t val) {
return;
case INT64:
case UINT64:
if (sizeof(mp_int_t) == 8) {
((uint64_t *)p)[index] = (uint64_t)v;
} else {
// TODO: Doesn't offer atomic store semantics, but should at least try
set_unaligned(val_type, (void *)&((uint64_t *)p)[index], MP_ENDIANNESS_BIG, val);
}
assert(sizeof(mp_int_t) == 8);
((uint64_t *)p)[index] = (uint64_t)v;
return;
default:
assert(0);
Expand Down Expand Up @@ -430,29 +486,39 @@ static mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set
offset &= (1 << OFFSET_BITS) - 1;
mp_uint_t val;
if (self->flags == LAYOUT_NATIVE) {
val = get_aligned_basic(val_type & 6, self->addr + offset);
val = get_aligned_basic(INT_TYPE_TO_UNSIGNED(val_type), self->addr + offset);
} else {
val = mp_binary_get_int(GET_SCALAR_SIZE(val_type & 7), val_type & 1, self->flags, self->addr + offset);
val = mp_binary_get_int(GET_SCALAR_SIZE(val_type), INT_TYPE_IS_SIGNED(val_type),
self->flags, self->addr + offset);
}
if (set_val == MP_OBJ_NULL) {
val >>= bit_offset;
val &= (1 << bit_len) - 1;
// TODO: signed
assert((val_type & 1) == 0);
assert(!INT_TYPE_IS_SIGNED(val_type));
return mp_obj_new_int(val);
} else {
mp_uint_t set_val_int = (mp_uint_t)mp_obj_get_int(set_val);
mp_uint_t mask = (1 << bit_len) - 1;
mp_uint_t set_val_int;

#if MICROPY_PREVIEW_VERSION_2
if (!mp_obj_get_int_maybe(set_val, (mp_int_t *)&set_val_int) || (set_val_int & mask) != set_val_int) {
raise_overflow_exception();
}
#else
set_val_int = (mp_uint_t)mp_obj_get_int(set_val);
#endif

set_val_int &= mask;
set_val_int <<= bit_offset;
mask <<= bit_offset;
val = (val & ~mask) | set_val_int;

if (self->flags == LAYOUT_NATIVE) {
set_aligned_basic(val_type & 6, self->addr + offset, val);
set_aligned_basic(INT_TYPE_TO_UNSIGNED(val_type), self->addr + offset, val);
} else {
mp_binary_set_int(GET_SCALAR_SIZE(val_type & 7), self->flags == LAYOUT_BIG_ENDIAN,
self->addr + offset, val);
size_t item_size = GET_SCALAR_SIZE(val_type);
mp_binary_set_int(item_size, self->addr + offset, item_size, val, self->flags == LAYOUT_BIG_ENDIAN);
}
return set_val; // just !MP_OBJ_NULL
}
Expand Down
4 changes: 2 additions & 2 deletions ports/stm32/adc.c
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ static mp_obj_t adc_read_timed(mp_obj_t self_in, mp_obj_t buf_in, mp_obj_t freq_
if (typesize == 1) {
value >>= 4;
}
mp_binary_set_val_array_from_int(bufinfo.typecode, bufinfo.buf, index, value);
mp_binary_set_val_array(bufinfo.typecode, bufinfo.buf, index, MP_OBJ_NEW_SMALL_INT(value));
}

// turn the ADC off
Expand Down Expand Up @@ -803,7 +803,7 @@ static mp_obj_t adc_read_timed_multi(mp_obj_t adc_array_in, mp_obj_t buf_array_i
if (typesize == 1) {
value >>= 4;
}
mp_binary_set_val_array_from_int(bufinfo.typecode, bufptrs[array_index], elem_index, value);
mp_binary_set_val_array(bufinfo.typecode, bufptrs[array_index], elem_index, MP_OBJ_NEW_SMALL_INT(value));
}
}

Expand Down
13 changes: 0 additions & 13 deletions ports/unix/coverage.c
Original file line number Diff line number Diff line change
Expand Up @@ -627,19 +627,6 @@ static mp_obj_t extra_coverage(void) {
mp_printf(&mp_plat_print, "%s\n", buf2);
}

// binary
{
mp_printf(&mp_plat_print, "# binary\n");

// call function with float and double typecodes
float far[1];
double dar[1];
mp_binary_set_val_array_from_int('f', far, 0, 123);
mp_printf(&mp_plat_print, "%.0f\n", (double)far[0]);
mp_binary_set_val_array_from_int('d', dar, 0, 456);
mp_printf(&mp_plat_print, "%.0lf\n", dar[0]);
}

// VM
{
mp_printf(&mp_plat_print, "# VM\n");
Expand Down
4 changes: 2 additions & 2 deletions ports/unix/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -480,8 +480,8 @@ int main(int argc, char **argv) {

// Define a reasonable stack limit to detect stack overflow.
mp_uint_t stack_size = 40000 * (sizeof(void *) / 4);
#if defined(__arm__) && !defined(__thumb2__)
// ARM (non-Thumb) architectures require more stack.
#if (defined(__arm__) && !defined(__thumb2__)) || defined(_MSC_VER)
// ARM (non-Thumb) architectures require more stack, as does Windows
stack_size *= 2;
#endif

Expand Down
2 changes: 1 addition & 1 deletion ports/unix/modffi.c
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ static unsigned long long ffi_get_int_value(mp_obj_t o) {
return MP_OBJ_SMALL_INT_VALUE(o);
} else {
unsigned long long res;
mp_obj_int_to_bytes_impl(o, MP_ENDIANNESS_BIG, sizeof(res), (byte *)&res);
mp_obj_int_to_bytes(o, sizeof(res), (byte *)&res, MP_ENDIANNESS_BIG, false, false);
return res;
}
}
Expand Down
111 changes: 31 additions & 80 deletions py/binary.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@
#define alignof(type) offsetof(struct { char c; type t; }, t)
#endif

// MicroPython V1.x truncates integers when writing into arrays,
// MicroPython V2 will raise OverflowError in these cases, same as CPython
#define OVERFLOW_CHECKS MICROPY_PREVIEW_VERSION_2

size_t mp_binary_get_size(char struct_type, char val_type, size_t *palign) {
size_t size = 0;
int align = 1;
Expand Down Expand Up @@ -370,7 +374,21 @@ mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte *
}
}

void mp_binary_set_int(size_t val_sz, bool big_endian, byte *dest, mp_uint_t val) {
void mp_binary_set_int(size_t dest_sz, byte *dest, size_t val_sz, mp_uint_t val, bool big_endian) {
if (dest_sz > val_sz) {
// zero/sign extension if needed
int c = ((mp_int_t)val < 0) ? 0xff : 0x00;
memset(dest, c, dest_sz);

// big endian: write val_sz bytes at end of 'dest'
if (big_endian) {
dest += dest_sz - val_sz;
}
} else if (dest_sz < val_sz) {
// truncate 'val' into 'dest'
val_sz = dest_sz;
}

if (MP_ENDIANNESS_LITTLE && !big_endian) {
memcpy(dest, &val, val_sz);
} else if (MP_ENDIANNESS_BIG && big_endian) {
Expand Down Expand Up @@ -434,34 +452,21 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p
val = fp_dp.i64;
} else {
int be = struct_type == '>';
mp_binary_set_int(sizeof(uint32_t), be, p, fp_dp.i32[MP_ENDIANNESS_BIG ^ be]);
mp_binary_set_int(sizeof(uint32_t), p, sizeof(uint32_t), fp_dp.i32[MP_ENDIANNESS_BIG ^ be], be);
// Now fall through and copy the second word, below
p += sizeof(uint32_t);
size = sizeof(uint32_t);
val = fp_dp.i32[MP_ENDIANNESS_LITTLE ^ be];
}
break;
}
#endif
default:
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
if (mp_obj_is_exact_type(val_in, &mp_type_int)) {
mp_obj_int_to_bytes_impl(val_in, struct_type == '>', size, p);
return;
}
#endif

val = mp_obj_get_int(val_in);
// zero/sign extend if needed
if (MP_BYTES_PER_OBJ_WORD < 8 && size > sizeof(val)) {
int c = (mp_int_t)val < 0 ? 0xff : 0x00;
memset(p, c, size);
if (struct_type == '>') {
p += size - sizeof(val);
}
}
break;
mp_obj_int_to_bytes(val_in, size, p, struct_type == '>', is_signed(val_type), OVERFLOW_CHECKS);
return;
}

mp_binary_set_int(MIN((size_t)size, sizeof(val)), struct_type == '>', p, val);
mp_binary_set_int(size, p, sizeof(val), val, struct_type == '>');
}

void mp_binary_set_val_array(char typecode, void *p, size_t index, mp_obj_t val_in) {
Expand All @@ -478,65 +483,11 @@ void mp_binary_set_val_array(char typecode, void *p, size_t index, mp_obj_t val_
case 'O':
((mp_obj_t *)p)[index] = val_in;
break;
default:
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
if (mp_obj_is_exact_type(val_in, &mp_type_int)) {
size_t size = mp_binary_get_size('@', typecode, NULL);
mp_obj_int_to_bytes_impl(val_in, MP_ENDIANNESS_BIG,
size, (uint8_t *)p + index * size);
return;
}
#endif
mp_binary_set_val_array_from_int(typecode, p, index, mp_obj_get_int(val_in));
}
}

void mp_binary_set_val_array_from_int(char typecode, void *p, size_t index, mp_int_t val) {
switch (typecode) {
case 'b':
((signed char *)p)[index] = val;
break;
case BYTEARRAY_TYPECODE:
case 'B':
((unsigned char *)p)[index] = val;
break;
case 'h':
((short *)p)[index] = val;
break;
case 'H':
((unsigned short *)p)[index] = val;
break;
case 'i':
((int *)p)[index] = val;
break;
case 'I':
((unsigned int *)p)[index] = val;
break;
case 'l':
((long *)p)[index] = val;
break;
case 'L':
((unsigned long *)p)[index] = val;
break;
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
case 'q':
((long long *)p)[index] = val;
break;
case 'Q':
((unsigned long long *)p)[index] = val;
break;
#endif
#if MICROPY_PY_BUILTINS_FLOAT
case 'f':
((float *)p)[index] = (float)val;
break;
case 'd':
((double *)p)[index] = (double)val;
break;
#endif
// Extension to CPython: array of pointers
case 'P':
((void **)p)[index] = (void *)(uintptr_t)val;
break;
default: {
size_t size = mp_binary_get_size('@', typecode, NULL);
p = (uint8_t *)p + index * size;
mp_obj_int_to_bytes(val_in, size, p, MP_ENDIANNESS_BIG, is_signed(typecode), OVERFLOW_CHECKS);
return;
}
}
}
3 changes: 1 addition & 2 deletions py/binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,9 @@
size_t mp_binary_get_size(char struct_type, char val_type, size_t *palign);
mp_obj_t mp_binary_get_val_array(char typecode, void *p, size_t index);
void mp_binary_set_val_array(char typecode, void *p, size_t index, mp_obj_t val_in);
void mp_binary_set_val_array_from_int(char typecode, void *p, size_t index, mp_int_t val);
mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte **ptr);
void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p_base, byte **ptr);
long long mp_binary_get_int(size_t size, bool is_signed, bool big_endian, const byte *src);
void mp_binary_set_int(size_t val_sz, bool big_endian, byte *dest, mp_uint_t val);
void mp_binary_set_int(size_t dest_sz, byte *dest, size_t val_sz, mp_uint_t val, bool big_endian);

#endif // MICROPY_INCLUDED_PY_BINARY_H
Loading
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy