Skip to content

Commit 9530743

Browse files
committed
py: Enable struct/binary-helper to parse q and Q sized ints.
Addresses issue adafruit#848.
1 parent 6eae861 commit 9530743

File tree

8 files changed

+51
-16
lines changed

8 files changed

+51
-16
lines changed

py/binary.c

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "misc.h"
3535
#include "qstr.h"
3636
#include "obj.h"
37+
#include "smallint.h"
3738
#include "binary.h"
3839

3940
// Helpers to work with binary-encoded data
@@ -136,7 +137,10 @@ mp_obj_t mp_binary_get_val_array(char typecode, void *p, int index) {
136137
return MP_OBJ_NEW_SMALL_INT(val);
137138
}
138139

139-
mp_int_t mp_binary_get_int(mp_uint_t size, bool is_signed, bool big_endian, byte *p) {
140+
// The long long type is guaranteed to hold at least 64 bits, and size is at
141+
// most 8 (for q and Q), so we will always be able to parse the given data
142+
// and fit it into a long long.
143+
long long mp_binary_get_int(mp_uint_t size, bool is_signed, bool big_endian, byte *p) {
140144
int delta;
141145
if (!big_endian) {
142146
delta = -1;
@@ -145,7 +149,7 @@ mp_int_t mp_binary_get_int(mp_uint_t size, bool is_signed, bool big_endian, byte
145149
delta = 1;
146150
}
147151

148-
mp_int_t val = 0;
152+
long long val = 0;
149153
if (is_signed && *p & 0x80) {
150154
val = -1;
151155
}
@@ -175,16 +179,25 @@ mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte **ptr) {
175179
}
176180
*ptr = p + size;
177181

178-
mp_int_t val = mp_binary_get_int(size, is_signed(val_type), (struct_type == '>'), p);
182+
long long val = mp_binary_get_int(size, is_signed(val_type), (struct_type == '>'), p);
179183

180184
if (val_type == 'O') {
181-
return (mp_obj_t)val;
185+
return (mp_obj_t)(mp_uint_t)val;
182186
} else if (val_type == 'S') {
183-
return mp_obj_new_str((char*)val, strlen((char*)val), false);
187+
const char *s_val = (const char*)(mp_uint_t)val;
188+
return mp_obj_new_str(s_val, strlen(s_val), false);
184189
} else if (is_signed(val_type)) {
185-
return mp_obj_new_int(val);
190+
if ((long long)MP_SMALL_INT_MIN <= val && val <= (long long)MP_SMALL_INT_MAX) {
191+
return mp_obj_new_int((mp_int_t)val);
192+
} else {
193+
return mp_obj_new_int_from_ll(val);
194+
}
186195
} else {
187-
return mp_obj_new_int_from_uint(val);
196+
if ((unsigned long long)val <= (unsigned long long)MP_SMALL_INT_MAX) {
197+
return mp_obj_new_int_from_uint((mp_uint_t)val);
198+
} else {
199+
return mp_obj_new_int_from_ull(val);
200+
}
188201
}
189202
}
190203

py/binary.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,5 @@ void mp_binary_set_val_array(char typecode, void *p, int index, mp_obj_t val_in)
3434
void mp_binary_set_val_array_from_int(char typecode, void *p, int index, mp_int_t val);
3535
mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte **ptr);
3636
void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte **ptr);
37-
mp_int_t mp_binary_get_int(mp_uint_t size, bool is_signed, bool big_endian, byte *p);
37+
long long mp_binary_get_int(mp_uint_t size, bool is_signed, bool big_endian, byte *p);
3838
void mp_binary_set_int(mp_uint_t val_sz, bool big_endian, byte *p, byte *val_ptr);

py/mpz.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -589,9 +589,9 @@ mpz_t *mpz_from_int(mp_int_t val) {
589589
return z;
590590
}
591591

592-
mpz_t *mpz_from_ll(long long val) {
592+
mpz_t *mpz_from_ll(long long val, bool is_signed) {
593593
mpz_t *z = mpz_zero();
594-
mpz_set_from_ll(z, val);
594+
mpz_set_from_ll(z, val, is_signed);
595595
return z;
596596
}
597597

@@ -668,11 +668,11 @@ void mpz_set_from_int(mpz_t *z, mp_int_t val) {
668668
}
669669
}
670670

671-
void mpz_set_from_ll(mpz_t *z, long long val) {
671+
void mpz_set_from_ll(mpz_t *z, long long val, bool is_signed) {
672672
mpz_need_dig(z, MPZ_NUM_DIG_FOR_LL);
673673

674674
unsigned long long uval;
675-
if (val < 0) {
675+
if (is_signed && val < 0) {
676676
z->neg = 1;
677677
uval = -val;
678678
} else {

py/mpz.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,15 @@ void mpz_deinit(mpz_t *z);
7171

7272
mpz_t *mpz_zero();
7373
mpz_t *mpz_from_int(mp_int_t i);
74-
mpz_t *mpz_from_ll(long long i);
74+
mpz_t *mpz_from_ll(long long i, bool is_signed);
7575
mpz_t *mpz_from_str(const char *str, mp_uint_t len, bool neg, mp_uint_t base);
7676
void mpz_free(mpz_t *z);
7777

7878
mpz_t *mpz_clone(const mpz_t *src);
7979

8080
void mpz_set(mpz_t *dest, const mpz_t *src);
8181
void mpz_set_from_int(mpz_t *z, mp_int_t src);
82-
void mpz_set_from_ll(mpz_t *z, long long i);
82+
void mpz_set_from_ll(mpz_t *z, long long i, bool is_signed);
8383
mp_uint_t mpz_set_from_str(mpz_t *z, const char *str, mp_uint_t len, bool neg, mp_uint_t base);
8484

8585
bool mpz_is_zero(const mpz_t *z);

py/obj.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ mp_obj_t mp_obj_new_int(mp_int_t value);
369369
mp_obj_t mp_obj_new_int_from_uint(mp_uint_t value);
370370
mp_obj_t mp_obj_new_int_from_str_len(const char **str, mp_uint_t len, bool neg, mp_uint_t base);
371371
mp_obj_t mp_obj_new_int_from_ll(long long val); // this must return a multi-precision integer object (or raise an overflow exception)
372+
mp_obj_t mp_obj_new_int_from_ull(unsigned long long val); // this must return a multi-precision integer object (or raise an overflow exception)
372373
mp_obj_t mp_obj_new_str(const char* data, mp_uint_t len, bool make_qstr_if_not_already);
373374
mp_obj_t mp_obj_new_bytes(const byte* data, mp_uint_t len);
374375
#if MICROPY_PY_BUILTINS_FLOAT

py/objint.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,12 @@ mp_obj_t mp_obj_new_int_from_ll(long long val) {
246246
return mp_const_none;
247247
}
248248

249+
// This is called when an integer larger than a SMALL_INT is needed (although val might still fit in a SMALL_INT)
250+
mp_obj_t mp_obj_new_int_from_ull(unsigned long long val) {
251+
nlr_raise(mp_obj_new_exception_msg(&mp_type_OverflowError, "small int overflow"));
252+
return mp_const_none;
253+
}
254+
249255
mp_obj_t mp_obj_new_int_from_uint(mp_uint_t value) {
250256
// SMALL_INT accepts only signed numbers, of one bit less size
251257
// then word size, which totals 2 bits less for unsigned numbers.

py/objint_longlong.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,16 @@ mp_obj_t mp_obj_new_int_from_ll(long long val) {
178178
return o;
179179
}
180180

181-
mp_obj_t mp_obj_new_int_from_str_len(const char **str, uint len, bool neg, uint base) {
181+
mp_obj_t mp_obj_new_int_from_ull(unsigned long long val) {
182+
// TODO raise an exception if the unsigned long long won't fit
183+
assert(val >> (sizeof(unsigned long long) * 8 - 1) == 0);
184+
mp_obj_int_t *o = m_new_obj(mp_obj_int_t);
185+
o->base.type = &mp_type_int;
186+
o->val = val;
187+
return o;
188+
}
189+
190+
mp_obj_t mp_obj_new_int_from_str_len(const char **str, mp_uint_t len, bool neg, mp_uint_t base) {
182191
// TODO this does not honor the given length of the string, but it all cases it should anyway be null terminated
183192
// TODO check overflow
184193
mp_obj_int_t *o = m_new_obj(mp_obj_int_t);

py/objint_mpz.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,13 @@ mp_obj_t mp_obj_new_int(mp_int_t value) {
279279

280280
mp_obj_t mp_obj_new_int_from_ll(long long val) {
281281
mp_obj_int_t *o = mp_obj_int_new_mpz();
282-
mpz_set_from_ll(&o->mpz, val);
282+
mpz_set_from_ll(&o->mpz, val, true);
283+
return o;
284+
}
285+
286+
mp_obj_t mp_obj_new_int_from_ull(unsigned long long val) {
287+
mp_obj_int_t *o = mp_obj_int_new_mpz();
288+
mpz_set_from_ll(&o->mpz, val, false);
283289
return o;
284290
}
285291

0 commit comments

Comments
 (0)