Skip to content

Commit 271d18e

Browse files
committed
py: Support conversion of bignum to bytes.
This gets int.to_bytes working for bignum, and also struct.pack with 'q' and 'Q' args on 32-bit machines. Addresses issue micropython#1155.
1 parent 7c8b4c1 commit 271d18e

File tree

9 files changed

+89
-15
lines changed

9 files changed

+89
-15
lines changed

py/binary.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
#include "py/binary.h"
3434
#include "py/smallint.h"
35+
#include "py/objint.h"
3536

3637
// Helpers to work with binary-encoded data
3738

@@ -282,10 +283,13 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte **
282283
}
283284
#endif
284285
default:
285-
// we handle large ints here by calling the truncated accessor
286+
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
286287
if (MP_OBJ_IS_TYPE(val_in, &mp_type_int)) {
287-
val = mp_obj_int_get_truncated(val_in);
288-
} else {
288+
mp_obj_int_to_bytes_impl(val_in, struct_type == '>', size, p);
289+
return;
290+
} else
291+
#endif
292+
{
289293
val = mp_obj_get_int(val_in);
290294
}
291295
}

py/mpz.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1425,6 +1425,40 @@ bool mpz_as_uint_checked(const mpz_t *i, mp_uint_t *value) {
14251425
return true;
14261426
}
14271427

1428+
// writes at most len bytes to buf (so buf should be zeroed before calling)
1429+
void mpz_as_bytes(const mpz_t *z, bool big_endian, mp_uint_t len, byte *buf) {
1430+
byte *b = buf;
1431+
if (big_endian) {
1432+
b += len;
1433+
}
1434+
mpz_dig_t *zdig = z->dig;
1435+
int bits = 0;
1436+
mpz_dbl_dig_t d = 0;
1437+
mpz_dbl_dig_t carry = 1;
1438+
for (mp_uint_t zlen = z->len; zlen > 0; --zlen) {
1439+
bits += DIG_SIZE;
1440+
d = (d << DIG_SIZE) | *zdig++;
1441+
for (; bits >= 8; bits -= 8, d >>= 8) {
1442+
mpz_dig_t val = d;
1443+
if (z->neg) {
1444+
d = (~d & 0xff) + carry;
1445+
carry = d >> 8;
1446+
}
1447+
if (big_endian) {
1448+
*--b = val;
1449+
if (b == buf) {
1450+
return;
1451+
}
1452+
} else {
1453+
*b++ = val;
1454+
if (b == buf + len) {
1455+
return;
1456+
}
1457+
}
1458+
}
1459+
}
1460+
}
1461+
14281462
#if MICROPY_PY_BUILTINS_FLOAT
14291463
mp_float_t mpz_as_float(const mpz_t *i) {
14301464
mp_float_t val = 0;

py/mpz.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ void mpz_divmod_inpl(mpz_t *dest_quo, mpz_t *dest_rem, const mpz_t *lhs, const m
125125
mp_int_t mpz_hash(const mpz_t *z);
126126
bool mpz_as_int_checked(const mpz_t *z, mp_int_t *value);
127127
bool mpz_as_uint_checked(const mpz_t *z, mp_uint_t *value);
128+
void mpz_as_bytes(const mpz_t *z, bool big_endian, mp_uint_t len, byte *buf);
128129
#if MICROPY_PY_BUILTINS_FLOAT
129130
mp_float_t mpz_as_float(const mpz_t *z);
130131
#endif

py/objint.c

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "py/objstr.h"
3636
#include "py/runtime0.h"
3737
#include "py/runtime.h"
38+
#include "py/binary.h"
3839

3940
#if MICROPY_PY_BUILTINS_FLOAT
4041
#include <math.h>
@@ -398,26 +399,25 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_from_bytes_fun_obj, 2, 3, int_fro
398399
STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(int_from_bytes_obj, (const mp_obj_t)&int_from_bytes_fun_obj);
399400

400401
STATIC mp_obj_t int_to_bytes(mp_uint_t n_args, const mp_obj_t *args) {
401-
// TODO: Support long ints
402402
// TODO: Support byteorder param (assumes 'little')
403403
// TODO: Support signed param (assumes signed=False)
404404
(void)n_args;
405405

406-
mp_int_t val = mp_obj_int_get_checked(args[0]);
407406
mp_uint_t len = MP_OBJ_SMALL_INT_VALUE(args[1]);
408407

409408
vstr_t vstr;
410409
vstr_init_len(&vstr, len);
411410
byte *data = (byte*)vstr.buf;
412411
memset(data, 0, len);
413412

414-
if (MP_ENDIANNESS_LITTLE) {
415-
memcpy(data, &val, len < sizeof(mp_int_t) ? len : sizeof(mp_int_t));
416-
} else {
417-
while (len--) {
418-
*data++ = val;
419-
val >>= 8;
420-
}
413+
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
414+
if (!MP_OBJ_IS_SMALL_INT(args[0])) {
415+
mp_obj_int_to_bytes_impl(args[0], false, len, data);
416+
} else
417+
#endif
418+
{
419+
mp_int_t val = MP_OBJ_SMALL_INT_VALUE(args[0]);
420+
mp_binary_set_int(MIN((size_t)len, sizeof(val)), false, data, val);
421421
}
422422

423423
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);

py/objint.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ char *mp_obj_int_formatted(char **buf, mp_uint_t *buf_size, mp_uint_t *fmt_size,
5656
char *mp_obj_int_formatted_impl(char **buf, mp_uint_t *buf_size, mp_uint_t *fmt_size, mp_const_obj_t self_in,
5757
int base, const char *prefix, char base_char, char comma);
5858
mp_int_t mp_obj_int_hash(mp_obj_t self_in);
59+
void mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, mp_uint_t len, byte *buf);
5960
bool mp_obj_int_is_positive(mp_obj_t self_in);
6061
mp_obj_t mp_obj_int_abs(mp_obj_t self_in);
6162
mp_obj_t mp_obj_int_unary_op(mp_uint_t op, mp_obj_t o_in);

py/objint_longlong.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,24 @@ mp_int_t mp_obj_int_hash(mp_obj_t self_in) {
6363
return self->val;
6464
}
6565

66+
void mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, mp_uint_t len, byte *buf) {
67+
assert(MP_OBJ_IS_TYPE(self_in, &mp_type_int));
68+
mp_obj_int_t *self = self_in;
69+
long long val = self->val;
70+
if (big_endian) {
71+
byte *b = buf + len;
72+
while (b > buf) {
73+
*--b = val;
74+
val >>= 8;
75+
}
76+
} else {
77+
for (; len > 0; --len) {
78+
*buf++ = val;
79+
val >>= 8;
80+
}
81+
}
82+
}
83+
6684
bool mp_obj_int_is_positive(mp_obj_t self_in) {
6785
if (MP_OBJ_IS_SMALL_INT(self_in)) {
6886
return MP_OBJ_SMALL_INT_VALUE(self_in) >= 0;

py/objint_mpz.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ mp_int_t mp_obj_int_hash(mp_obj_t self_in) {
104104
return mpz_hash(&self->mpz);
105105
}
106106

107+
void mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, mp_uint_t len, byte *buf) {
108+
assert(MP_OBJ_IS_TYPE(self_in, &mp_type_int));
109+
mp_obj_int_t *self = self_in;
110+
mpz_as_bytes(&self->mpz, big_endian, len, buf);
111+
}
112+
107113
bool mp_obj_int_is_positive(mp_obj_t self_in) {
108114
if (MP_OBJ_IS_SMALL_INT(self_in)) {
109115
return MP_OBJ_SMALL_INT_VALUE(self_in) >= 0;

tests/basics/int_bytes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
print((10).to_bytes(1, "little"))
22
print((111111).to_bytes(4, "little"))
33
print((100).to_bytes(10, "little"))
4+
print((2**64).to_bytes(9, "little"))
45
print(int.from_bytes(b"\x00\x01\0\0\0\0\0\0", "little"))
56
print(int.from_bytes(b"\x01\0\0\0\0\0\0\0", "little"))
67
print(int.from_bytes(b"\x00\x01\0\0\0\0\0\0", "little"))

tests/basics/struct1.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,18 @@
3030
print(struct.pack("<I", 2**32 - 1))
3131
print(struct.pack("<I", 0xffffffff))
3232

33-
# fails on 32-bit machine
34-
#print(struct.pack("<Q", 2**64 - 1))
35-
#print(struct.pack("<Q", 0xffffffffffffffff))
33+
# long long ints
34+
print(struct.pack("<Q", 2**64 - 1))
35+
print(struct.pack("<Q", 0xffffffffffffffff))
36+
print(struct.pack("<q", -1))
37+
print(struct.pack("<Q", 1234567890123456789))
38+
print(struct.pack("<q", -1234567890123456789))
39+
print(struct.pack(">Q", 1234567890123456789))
40+
print(struct.pack(">q", -1234567890123456789))
41+
print(struct.unpack("<Q", b"\x12\x34\x56\x78\x90\x12\x34\x56"))
42+
print(struct.unpack(">Q", b"\x12\x34\x56\x78\x90\x12\x34\x56"))
43+
print(struct.unpack("<q", b"\x12\x34\x56\x78\x90\x12\x34\xf6"))
44+
print(struct.unpack(">q", b"\xf2\x34\x56\x78\x90\x12\x34\x56"))
3645

3746
# check maximum unpack
3847
print(struct.unpack("<I", b"\xff\xff\xff\xff"))

0 commit comments

Comments
 (0)