Skip to content

Commit 850e655

Browse files
authored
Merge pull request #2625 from dhalbert/to_bytes-signed
Implement to_bytes(..., signed=True)
2 parents 52d96ca + c592bd6 commit 850e655

File tree

4 files changed

+44
-14
lines changed

4 files changed

+44
-14
lines changed

py/objint.c

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -490,38 +490,51 @@ STATIC mp_obj_t int_from_bytes(size_t n_args, const mp_obj_t *args) {
490490
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_from_bytes_fun_obj, 3, 4, int_from_bytes);
491491
STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(int_from_bytes_obj, MP_ROM_PTR(&int_from_bytes_fun_obj));
492492

493-
STATIC mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *args) {
494-
// TODO: Support signed param (assumes signed=False)
495-
(void)n_args;
496-
497-
mp_int_t len = mp_obj_get_int(args[1]);
493+
STATIC mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
494+
enum { ARG_length, ARG_byteorder, ARG_signed };
495+
static const mp_arg_t allowed_args[] = {
496+
{ MP_QSTR_length, MP_ARG_REQUIRED | MP_ARG_INT },
497+
{ MP_QSTR_byteorder, MP_ARG_REQUIRED | MP_ARG_OBJ },
498+
{ MP_QSTR_signed, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
499+
};
500+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
501+
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
502+
503+
mp_int_t len = args[ARG_length].u_int;
498504
if (len < 0) {
499505
mp_raise_ValueError(NULL);
500506
}
501-
bool big_endian = args[2] != MP_OBJ_NEW_QSTR(MP_QSTR_little);
507+
508+
mp_obj_t self = pos_args[0];
509+
bool big_endian = args[ARG_byteorder].u_obj != MP_OBJ_NEW_QSTR(MP_QSTR_little);
510+
bool signed_ = args[ARG_signed].u_bool;
502511

503512
vstr_t vstr;
504513
vstr_init_len(&vstr, len);
505514
byte *data = (byte*)vstr.buf;
506515
memset(data, 0, len);
507516

508517
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
509-
if (!MP_OBJ_IS_SMALL_INT(args[0])) {
510-
mp_obj_int_buffer_overflow_check(args[0], len, false);
511-
mp_obj_int_to_bytes_impl(args[0], big_endian, len, data);
518+
if (!MP_OBJ_IS_SMALL_INT(self)) {
519+
mp_obj_int_buffer_overflow_check(self, len, signed_);
520+
mp_obj_int_to_bytes_impl(self, big_endian, len, data);
512521
} else
513522
#endif
514523
{
515-
mp_int_t val = MP_OBJ_SMALL_INT_VALUE(args[0]);
524+
mp_int_t val = MP_OBJ_SMALL_INT_VALUE(self);
516525
// Small int checking is separate, to be fast.
517-
mp_small_int_buffer_overflow_check(val, len, false);
526+
mp_small_int_buffer_overflow_check(val, len, signed_);
518527
size_t l = MIN((size_t)len, sizeof(val));
528+
if (val < 0) {
529+
// Sign extend negative numbers.
530+
memset(data, -1, len);
531+
}
519532
mp_binary_set_int(l, big_endian, data + (big_endian ? (len - l) : 0), val);
520533
}
521534

522535
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
523536
}
524-
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_to_bytes_obj, 3, 4, int_to_bytes);
537+
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(int_to_bytes_obj, 3, int_to_bytes);
525538

526539
STATIC const mp_rom_map_elem_t int_locals_dict_table[] = {
527540
{ MP_ROM_QSTR(MP_QSTR_from_bytes), MP_ROM_PTR(&int_from_bytes_obj) },

tests/basics/int_bytes.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
print((10).to_bytes(1, "little"))
2+
print((-10).to_bytes(1, "little", signed=True))
23
# Test fitting in length that's not a power of two.
34
print((0x10000).to_bytes(3, 'little'))
45
print((111111).to_bytes(4, "little"))
6+
print((-111111).to_bytes(4, "little", signed=True))
57
print((100).to_bytes(10, "little"))
8+
print((-100).to_bytes(10, "little", signed=True))
69

710
# check that extra zero bytes don't change the internal int value
811
print(int.from_bytes(bytes(20), "little") == 0)
912
print(int.from_bytes(b"\x01" + bytes(20), "little") == 1)
1013

1114
# big-endian conversion
1215
print((10).to_bytes(1, "big"))
16+
print((-10).to_bytes(1, "big", signed=True))
1317
print((100).to_bytes(10, "big"))
18+
print((-100).to_bytes(10, "big", signed=True))
1419
print(int.from_bytes(b"\0\0\0\0\0\0\0\0\0\x01", "big"))
1520
print(int.from_bytes(b"\x01\0", "big"))
1621

@@ -26,8 +31,13 @@
2631
except OverflowError:
2732
print("OverflowError")
2833

29-
# negative numbers should raise an error
34+
# negative numbers should raise an error if signed=False
3035
try:
3136
(-256).to_bytes(2, "little")
3237
except OverflowError:
3338
print("OverflowError")
39+
40+
try:
41+
(-256).to_bytes(2, "little", signed=False)
42+
except OverflowError:
43+
print("OverflowError")

tests/basics/int_bytes_intbig.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
skip_if.no_bigint()
33

44
print((2**64).to_bytes(9, "little"))
5+
print((-2**64).to_bytes(9, "little", signed=True))
56
print((2**64).to_bytes(9, "big"))
7+
print((-2**64).to_bytes(9, "big", signed=True))
68

79
b = bytes(range(20))
810

@@ -22,8 +24,12 @@
2224
except OverflowError:
2325
print("OverflowError")
2426

25-
# negative numbers should raise an error
27+
# negative numbers should raise an error if signed=False
2628
try:
2729
(-2**64).to_bytes(9, "little")
2830
except OverflowError:
2931
print("OverflowError")
32+
try:
33+
(-2**64).to_bytes(9, "little", signed=False)
34+
except OverflowError:
35+
print("OverflowError")

tests/basics/int_longint_bytes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
skip_if.no_bigint()
44

55
print((2**64).to_bytes(9, "little"))
6+
print((-2**64).to_bytes(9, "little", signed=True))
67
print(int.from_bytes(b"\x00\x01\0\0\0\0\0\0", "little"))
78
print(int.from_bytes(b"\x01\0\0\0\0\0\0\0", "little"))
89
print(int.from_bytes(b"\x00\x01\0\0\0\0\0\0", "little"))

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