Skip to content

Commit f3403bb

Browse files
committed
py/objint.c: Code review of int.from_bytes().
Support signed param: result = int.from_bytes(bytearray(), order='big'|'little', signed=False|True) Signed-off-by: Ihor Nehrutsa <Ihor.Nehrutsa@gmail.com>
1 parent 8987b39 commit f3403bb

File tree

7 files changed

+127
-6
lines changed

7 files changed

+127
-6
lines changed

ports/esp32/mpconfigport.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@
6161
#define MICROPY_ENABLE_GC (1)
6262
#define MICROPY_STACK_CHECK_MARGIN (1024)
6363
#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1)
64-
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)
65-
#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL)
64+
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_LONGLONG) // (MICROPY_LONGINT_IMPL_MPZ) // (MICROPY_LONGINT_IMPL_NONE) //
65+
#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL + 1)
6666
#define MICROPY_WARNINGS (1)
6767
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
6868
#define MICROPY_STREAMS_POSIX_API (1)

py/objint.c

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
#include <math.h>
4040
#endif
4141

42+
#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__);
43+
#define _debug_printf(...) // mp_printf(&mp_plat_print, __VA_ARGS__);
44+
4245
// This dispatcher function is expected to be independent of the implementation of long int
4346
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) {
4447
(void)type_in;
@@ -386,7 +389,7 @@ mp_obj_t mp_obj_int_binary_op_extra_cases(mp_binary_op_t op, mp_obj_t lhs_in, mp
386389
}
387390
return MP_OBJ_NULL; // op not supported
388391
}
389-
392+
/*
390393
// this is a classmethod
391394
static mp_obj_t int_from_bytes(size_t n_args, const mp_obj_t *args) {
392395
// TODO: Support signed param (assumes signed=False at the moment)
@@ -416,6 +419,79 @@ static mp_obj_t int_from_bytes(size_t n_args, const mp_obj_t *args) {
416419
}
417420
return mp_obj_new_int_from_uint(value);
418421
}
422+
*/
423+
424+
void *reverce_memcpy(void *dest, const void *src, size_t len) {
425+
char *d = (char *)dest + len - 1;
426+
const char *s = src;
427+
while (len--) {
428+
*d-- = *s++;
429+
}
430+
return dest;
431+
}
432+
433+
mp_obj_t mp_obj_integer_from_bytes_impl(const bool big_endian, const bool signd, const size_t buf_len, const byte *buf) {
434+
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
435+
if ((1ULL << (buf_len * 8 - 2)) > (MP_SMALL_INT_MAX + 1)) {
436+
debug_printf("//1 Result will overflow a small-int so construct a big-int");
437+
debug_printf("big_endian:%d signed:%d buf_len:%d", big_endian, signd, buf_len);
438+
return mp_obj_int_from_bytes_impl(big_endian, signd, buf_len, buf);
439+
}
440+
#endif
441+
442+
union {
443+
mp_int_t value;
444+
byte buf[sizeof(mp_uint_t)];
445+
} result = {0};
446+
447+
if (big_endian) {
448+
reverce_memcpy(&result.value, buf, sizeof(result.value) < buf_len ? sizeof(result.value) : buf_len);
449+
} else { // little endian
450+
memcpy(&result.value, buf, sizeof(result.value) < buf_len ? sizeof(result.value) : buf_len);
451+
}
452+
453+
if ((signd) && (sizeof(result.value) > buf_len) && (result.buf[buf_len - 1] & 0x80)) {
454+
// Sign propagation
455+
// x = 2
456+
// x.to_bytes(1, 'little', True) -> b'\x02'
457+
// x.to_bytes(4, 'little', True) -> b'\x02\x00\x00\x00'
458+
// x = -2
459+
// x.to_bytes(1, 'little', True) -> b'\xFE'
460+
// x.to_bytes(4, 'little', True) -> b'\xFE\xFF\xFF\xFF'
461+
debug_printf("result=0x%08X", result.value);
462+
for (unsigned int i = 0; i < sizeof(result.value); i++) {
463+
_debug_printf("\\%02X", result.buf[i]);
464+
}
465+
memset(result.buf + buf_len, 0xFF, sizeof(result.value) - buf_len);
466+
debug_printf("\nresult=0x%08X", result.value);
467+
for (unsigned int i = 0; i < sizeof(result.value); i++) {
468+
_debug_printf("\\%02X", result.buf[i]);
469+
}
470+
debug_printf("");
471+
}
472+
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
473+
// debug_printf("big_endian:%d signed:%d buf_len:%d sizeof(result.value):%d result.value:%ld", big_endian, signd, buf_len, sizeof(result.value), result.value);
474+
if (!MP_SMALL_INT_FITS(result.value)) {
475+
debug_printf("//2 Result will overflow a small-int so construct a big-int");
476+
return mp_obj_int_from_bytes_impl(big_endian, signd, buf_len, buf);
477+
}
478+
#endif
479+
return mp_obj_new_int(result.value);
480+
}
481+
482+
// this is a classmethod
483+
// result = int.from_bytes(bytearray(), order='big', signed=False)
484+
static mp_obj_t int_from_bytes(size_t n_args, const mp_obj_t *args) {
485+
debug_printf("mp_obj_is_bool(args[3])=%d", mp_obj_is_bool(args[3]));
486+
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);
487+
// get the buffer info
488+
mp_buffer_info_t bufinfo;
489+
mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ);
490+
bool big_endian = n_args < 3 || args[2] != MP_OBJ_NEW_QSTR(MP_QSTR_little);
491+
bool signd = (n_args > 3) && mp_obj_is_true(args[3]);
492+
493+
return mp_obj_integer_from_bytes_impl(big_endian, signd, bufinfo.len, bufinfo.buf);
494+
}
419495

420496
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_from_bytes_fun_obj, 2, 4, int_from_bytes);
421497
static MP_DEFINE_CONST_CLASSMETHOD_OBJ(int_from_bytes_obj, MP_ROM_PTR(&int_from_bytes_fun_obj));

py/objint.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,15 @@ char *mp_obj_int_formatted(char **buf, size_t *buf_size, size_t *fmt_size, mp_co
5454
char *mp_obj_int_formatted_impl(char **buf, size_t *buf_size, size_t *fmt_size, mp_const_obj_t self_in,
5555
int base, const char *prefix, char base_char, char comma);
5656
mp_int_t mp_obj_int_hash(mp_obj_t self_in);
57-
mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf);
57+
mp_obj_t mp_obj_integer_from_bytes_impl(bool big_endian, bool signd, size_t len, const byte *buf);
58+
mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, bool signd, size_t len, const byte *buf);
5859
// Returns true if 'self_in' fit into 'len' bytes of 'buf' without overflowing, 'buf' is truncated otherwise.
5960
bool mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, size_t len, byte *buf);
6061
int mp_obj_int_sign(mp_obj_t self_in);
6162
mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in);
6263
mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in);
6364
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);
6465
mp_obj_t mp_obj_int_pow3(mp_obj_t base, mp_obj_t exponent, mp_obj_t modulus);
66+
void *reverce_memcpy(void *dest, const void *src, size_t len);
6567

6668
#endif // MICROPY_INCLUDED_PY_OBJINT_H

py/objint_longlong.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
const mp_obj_int_t mp_sys_maxsize_obj = {{&mp_type_int}, MP_SSIZE_MAX};
4444
#endif
4545

46+
/*
4647
mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf) {
4748
int delta = 1;
4849
if (!big_endian) {
@@ -56,6 +57,47 @@ mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf
5657
}
5758
return mp_obj_new_int_from_ll(value);
5859
}
60+
*/
61+
#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__);
62+
#define _debug_printf(...) // mp_printf(&mp_plat_print, __VA_ARGS__);
63+
64+
mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, bool signd, size_t len, const byte *buf) {
65+
union {
66+
mp_longint_impl_t value;
67+
byte buf[sizeof(mp_longint_impl_t)];
68+
} result = {0};
69+
70+
if (len > sizeof(result.value)) {
71+
mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("long int overflow"));
72+
}
73+
74+
if (big_endian) {
75+
reverce_memcpy(&result, buf, len);
76+
} else { // little endian
77+
memcpy(&result, buf, len);
78+
}
79+
80+
if ((signd) && (sizeof(sizeof(result.value)) > len) && (result.buf[len - 1] & 0x80)) {
81+
// Sign propagation
82+
// x = 2
83+
// x.to_bytes(1, 'little', True) -> b'\x02'
84+
// x.to_bytes(4, 'little', True) -> b'\x02\x00\x00\x00'
85+
// x = -2
86+
// x.to_bytes(1, 'little', True) -> b'\xFE'
87+
// x.to_bytes(4, 'little', True) -> b'\xFE\xFF\xFF\xFF'
88+
debug_printf("result=0x%08X", result.value);
89+
for (unsigned int i = 0; i < sizeof(sizeof(result.value)); i++) {
90+
_debug_printf("\\%02X", result.buf[i]);
91+
}
92+
memset(result.buf + len, 0xFF, sizeof(sizeof(result.value)) - len);
93+
debug_printf("\nresult=0x%08X", result.value);
94+
for (unsigned int i = 0; i < sizeof(sizeof(result.value)); i++) {
95+
_debug_printf("\\%02X", result.buf[i]);
96+
}
97+
debug_printf("");
98+
}
99+
return mp_obj_new_int_from_ll(result.value);
100+
}
59101

60102
bool mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, size_t len, byte *buf) {
61103
assert(mp_obj_is_exact_type(self_in, &mp_type_int));

py/objint_mpz.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ char *mp_obj_int_formatted_impl(char **buf, size_t *buf_size, size_t *fmt_size,
106106
return str;
107107
}
108108

109-
mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf) {
109+
mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, bool signd, size_t len, const byte *buf) {
110110
mp_obj_int_t *o = mp_obj_int_new_mpz();
111111
mpz_set_from_bytes(&o->mpz, big_endian, len, buf);
112112
return MP_OBJ_FROM_PTR(o);

py/smallint.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
#endif
6161

6262
#define MP_SMALL_INT_MAX ((mp_int_t)(~(MP_SMALL_INT_MIN)))
63+
#define MP_SMALL_UINT_FITS(n) (((n) & ~MP_SMALL_INT_POSITIVE_MASK) == 0)
6364

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

tests/basics/int_bytes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
# check that extra zero bytes don't change the internal int value
1111
print(int.from_bytes(bytes(20), "little") == 0)
12-
print(int.from_bytes(b"\x01" + bytes(20), "little") == 1)
12+
print(int.from_bytes(b"\x01" + bytes(7), "little") == 1)
1313

1414
# big-endian conversion
1515
print((10).to_bytes(1, "big"))

0 commit comments

Comments
 (0)
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