Skip to content

Commit

Permalink
py/objint.c: Code review of int_from_bytes().
Browse files Browse the repository at this point in the history
Support signed param:
result = int.int_from_bytes(bytearray(),
order='big'|'little', signed=False|True)

Signed-off-by: Ihor Nehrutsa <[email protected]>
  • Loading branch information
IhorNehrutsa committed Feb 23, 2025
1 parent 8987b39 commit effe6d8
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 3 deletions.
4 changes: 2 additions & 2 deletions ports/esp32/mpconfigport.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@
#define MICROPY_ENABLE_GC (1)
#define MICROPY_STACK_CHECK_MARGIN (1024)
#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1)
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)
#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL)
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_LONGLONG) // (MICROPY_LONGINT_IMPL_MPZ)
#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL+1)
#define MICROPY_WARNINGS (1)
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
#define MICROPY_STREAMS_POSIX_API (1)
Expand Down
100 changes: 100 additions & 0 deletions py/objint.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
#include <math.h>
#endif

#define debug_printf(...) mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, "\n"); // mp_printf(&mp_plat_print, " | func:%s line:%d at %s\n", __FUNCTION__, __LINE__, __FILE__);
#define _debug_printf(...) mp_printf(&mp_plat_print, __VA_ARGS__);

// This dispatcher function is expected to be independent of the implementation of long int
static mp_obj_t mp_obj_int_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
(void)type_in;
Expand Down Expand Up @@ -387,6 +390,7 @@ mp_obj_t mp_obj_int_binary_op_extra_cases(mp_binary_op_t op, mp_obj_t lhs_in, mp
return MP_OBJ_NULL; // op not supported
}

/*
// this is a classmethod
static mp_obj_t int_from_bytes(size_t n_args, const mp_obj_t *args) {
// TODO: Support signed param (assumes signed=False at the moment)
Expand Down Expand Up @@ -416,6 +420,102 @@ static mp_obj_t int_from_bytes(size_t n_args, const mp_obj_t *args) {
}
return mp_obj_new_int_from_uint(value);
}
*/

int calc_size(mp_longint_impl_t x) {
int size = 0;
if (x < 0) {
x = -x;
}
do {
x >>= 8;
size++;
} while (x);
return size;
}

void *reverce_memcpy(void *dest, const void *src, size_t len) {
char *d = (char *)dest + len - 1;
const char *s = src;
while (len--) {
*d-- = *s++;
}
return dest;
}

// this is a classmethod
// result = int.int_from_bytes(bytearray(), order='big', signed=False)
static mp_obj_t int_from_bytes(size_t n_args, const mp_obj_t *args) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ);
bool big_endian = (n_args < 3) || (args[2] != MP_OBJ_NEW_QSTR(MP_QSTR_little));
bool signd = (n_args > 3) && mp_obj_is_true(args[3]);

// debug_printf("mp_obj_is_bool(args[3])=%d", mp_obj_is_bool(args[3]));
// debug_printf("n_args=%d, MP_SMALL_INT_MAX=%d, MP_SMALL_INT_MIN=%d", n_args, MP_SMALL_INT_MAX, MP_SMALL_INT_MIN);
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
if ((1ULL << (bufinfo.len * 8 - 2)) > (MP_SMALL_INT_MAX + 1)) {
debug_printf("//1 Result will overflow a small-int so construct a big-int");
debug_printf("big_endian:%d signed:%d bufinfo.len:%d", big_endian, signd, bufinfo.len);
return mp_obj_int_from_bytes_impl(big_endian, signd, bufinfo.len, bufinfo.buf);
}
#endif

union {
mp_int_t ival;
mp_uint_t uval;
byte buf[sizeof(mp_uint_t)];
} result = {0};

if (bufinfo.len > sizeof(result)) {
mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("small-int too large"));
bufinfo.len = sizeof(result);
}

if (big_endian) {
reverce_memcpy(&result.uval, bufinfo.buf, bufinfo.len);
} else { // little endian
memcpy(&result.uval, bufinfo.buf, bufinfo.len);
}
if (signd) {
if ((sizeof(result) > bufinfo.len) && (result.buf[bufinfo.len - 1] & 0x80)) {
// Sign propagation
// x = 2
// x.to_bytes(1, 'little', True) -> b'\x02'
// x.to_bytes(4, 'little', True) -> b'\x02\x00\x00\x00'
// x = -2
// x.to_bytes(1, 'little', True) -> b'\xFE'
// x.to_bytes(4, 'little', True) -> b'\xFE\xFF\xFF\xFF'
debug_printf("result=0x%08X", result.uval);
for (int i = 0; i < sizeof(result); i++) {
_debug_printf("\\%02X", result.buf[i]);
}
memset(result.buf + bufinfo.len, 0xFF, sizeof(result) - bufinfo.len);
debug_printf("\nresult=0x%08X", result.uval);
for (int i = 0; i < sizeof(result); i++) {
_debug_printf("\\%02X", result.buf[i]);
}
debug_printf("");
}
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
// debug_printf("big_endian:%d signed:%d bufinfo.len:%d sizeof(result.ival):%d result.ival:%ld", big_endian, signd, bufinfo.len, sizeof(result.ival), result.ival);
if (!MP_SMALL_INT_FITS(result.ival)) {
debug_printf("//2 Result will overflow a small-int so construct a big-int");
return mp_obj_int_from_bytes_impl(big_endian, signd, bufinfo.len, bufinfo.buf);
}
#endif
return mp_obj_new_int(result.ival);
} else {
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
// debug_printf("big_endian:%d signed:%d bufinfo.len:%d sizeof(result.uval):%d result.uval:%ld", big_endian, signd, bufinfo.len, sizeof(result.uval), result.uval);
if (!MP_SMALL_UINT_FITS(result.uval)) {
debug_printf("//3 Result will overflow a small-int so construct a big-int");
return mp_obj_int_from_bytes_impl(big_endian, signd, bufinfo.len, bufinfo.buf);
}
#endif
return mp_obj_new_int_from_uint(result.uval);
}
}

static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_from_bytes_fun_obj, 2, 4, int_from_bytes);
static MP_DEFINE_CONST_CLASSMETHOD_OBJ(int_from_bytes_obj, MP_ROM_PTR(&int_from_bytes_fun_obj));
Expand Down
4 changes: 3 additions & 1 deletion py/objint.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,15 @@ char *mp_obj_int_formatted(char **buf, size_t *buf_size, size_t *fmt_size, mp_co
char *mp_obj_int_formatted_impl(char **buf, size_t *buf_size, size_t *fmt_size, mp_const_obj_t self_in,
int base, const char *prefix, char base_char, char comma);
mp_int_t mp_obj_int_hash(mp_obj_t self_in);
mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf);
mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, bool signd, size_t len, const byte *buf);
// Returns true if 'self_in' fit into 'len' bytes of 'buf' without overflowing, 'buf' is truncated otherwise.
bool mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, size_t len, byte *buf);
int mp_obj_int_sign(mp_obj_t self_in);
mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in);
mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in);
mp_obj_t mp_obj_int_binary_op_extra_cases(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in);
mp_obj_t mp_obj_int_pow3(mp_obj_t base, mp_obj_t exponent, mp_obj_t modulus);
void *reverce_memcpy(void *dest, const void *src, size_t len);
int calc_size(mp_longint_impl_t x);

#endif // MICROPY_INCLUDED_PY_OBJINT_H
45 changes: 45 additions & 0 deletions py/objint_longlong.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
const mp_obj_int_t mp_sys_maxsize_obj = {{&mp_type_int}, MP_SSIZE_MAX};
#endif

/*
mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf) {
int delta = 1;
if (!big_endian) {
Expand All @@ -56,6 +57,50 @@ mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf
}
return mp_obj_new_int_from_ll(value);
}
*/
#define debug_printf(...) mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, "\n"); // mp_printf(&mp_plat_print, " | func:%s line:%d at %s\n", __FUNCTION__, __LINE__, __FILE__);
#define _debug_printf(...) mp_printf(&mp_plat_print, __VA_ARGS__);

mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, bool signd, size_t len, const byte *buf) {
union {
mp_longint_impl_t val;
byte buf[sizeof(mp_longint_impl_t)];
} result = {0};

if (len > sizeof(result)) {
len = sizeof(result);
mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("long int overflow"));
}
if (big_endian) {
reverce_memcpy(&result, buf, len);
} else { // little endian
memcpy(&result, buf, len);
}
if (signd) {
if ((sizeof(result) > len) && (result.buf[len - 1] & 0x80)) {
// Sign propagation
// x = 2
// x.to_bytes(1, 'little', True) -> b'\x02'
// x.to_bytes(4, 'little', True) -> b'\x02\x00\x00\x00'
// x = -2
// x.to_bytes(1, 'little', True) -> b'\xFE'
// x.to_bytes(4, 'little', True) -> b'\xFE\xFF\xFF\xFF'
debug_printf("result=0x%08X", result.val);
for(int i = 0; i < sizeof(result); i++) {
_debug_printf("\\%02X", result.buf[i]);
}
memset(result.buf + len, 0xFF, sizeof(result) - len);
debug_printf("\nresult=0x%08X", result.val);
for(int i = 0; i < sizeof(result); i++) {
_debug_printf("\\%02X", result.buf[i]);
}
debug_printf("");
}
return mp_obj_new_int_from_ll(result.val);
} else {
return mp_obj_new_int_from_ull(result.val);
}
}

bool mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, size_t len, byte *buf) {
assert(mp_obj_is_exact_type(self_in, &mp_type_int));
Expand Down
1 change: 1 addition & 0 deletions py/smallint.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
#endif

#define MP_SMALL_INT_MAX ((mp_int_t)(~(MP_SMALL_INT_MIN)))
#define MP_SMALL_UINT_FITS(n) (((n) & ~MP_SMALL_INT_POSITIVE_MASK) == 0)

// https://stackoverflow.com/a/4589384/1976323
// Number of bits in inttype_MAX, or in any (1<<k)-1 where 0 <= k < 2040
Expand Down

0 comments on commit effe6d8

Please sign in to comment.