Skip to content

Commit e2444b1

Browse files
Merge pull request RustPython#1429 from lntuition/fix-int-with-byte
Fix error in int.from_bytes and int.to_bytes
2 parents 557d3aa + 8336bc9 commit e2444b1

File tree

2 files changed

+89
-48
lines changed

2 files changed

+89
-48
lines changed

tests/snippets/ints.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,18 +147,51 @@
147147
# type byte, signed, implied base
148148
assert int(b' -0XFF ', base=0) == -255
149149

150-
151150
assert int.from_bytes(b'\x00\x10', 'big') == 16
152151
assert int.from_bytes(b'\x00\x10', 'little') == 4096
152+
assert int.from_bytes(b'\x00\x10', byteorder='big') == 16
153+
assert int.from_bytes(b'\x00\x10', byteorder='little') == 4096
154+
assert int.from_bytes(bytes=b'\x00\x10', byteorder='big') == 16
155+
assert int.from_bytes(bytes=b'\x00\x10', byteorder='little') == 4096
156+
153157
assert int.from_bytes(b'\xfc\x00', 'big', signed=True) == -1024
154158
assert int.from_bytes(b'\xfc\x00', 'big', signed=False) == 64512
159+
assert int.from_bytes(b'\xfc\x00', byteorder='big', signed=True) == -1024
160+
assert int.from_bytes(b'\xfc\x00', byteorder='big', signed=False) == 64512
161+
assert int.from_bytes(bytes=b'\xfc\x00', byteorder='big', signed=True) == -1024
162+
assert int.from_bytes(bytes=b'\xfc\x00', byteorder='big', signed=False) == 64512
163+
164+
with assert_raises(ValueError):
165+
int.from_bytes(b'\x00\x10', 'something')
155166

156167
assert (1024).to_bytes(4, 'big') == b'\x00\x00\x04\x00'
157-
assert (1024).to_bytes(2, 'little', signed=True) == b'\x00\x04'
168+
assert (1024).to_bytes(2, 'little') == b'\x00\x04'
169+
assert (1024).to_bytes(4, byteorder='big') == b'\x00\x00\x04\x00'
170+
assert (1024).to_bytes(2, byteorder='little') == b'\x00\x04'
171+
assert (1024).to_bytes(length=4, byteorder='big') == b'\x00\x00\x04\x00'
172+
assert (1024).to_bytes(length=2, byteorder='little') == b'\x00\x04'
173+
158174
assert (-1024).to_bytes(4, 'big', signed=True) == b'\xff\xff\xfc\x00'
159175
assert (-1024).to_bytes(4, 'little', signed=True) == b'\x00\xfc\xff\xff'
176+
160177
assert (2147483647).to_bytes(8, 'big', signed=False) == b'\x00\x00\x00\x00\x7f\xff\xff\xff'
161178
assert (-2147483648).to_bytes(8, 'little', signed=True) == b'\x00\x00\x00\x80\xff\xff\xff\xff'
179+
assert (2147483647).to_bytes(8, byteorder='big', signed=False) == b'\x00\x00\x00\x00\x7f\xff\xff\xff'
180+
assert (-2147483648).to_bytes(8, byteorder='little', signed=True) == b'\x00\x00\x00\x80\xff\xff\xff\xff'
181+
assert (2147483647).to_bytes(length=8, byteorder='big', signed=False) == b'\x00\x00\x00\x00\x7f\xff\xff\xff'
182+
assert (-2147483648).to_bytes(length=8, byteorder='little', signed=True) == b'\x00\x00\x00\x80\xff\xff\xff\xff'
183+
184+
with assert_raises(ValueError):
185+
(1024).to_bytes(4, 'something')
186+
187+
with assert_raises(OverflowError):
188+
(-1024).to_bytes(4, 'big')
189+
190+
with assert_raises(OverflowError):
191+
(1024).to_bytes(10000000000000000000000, 'big')
192+
193+
with assert_raises(OverflowError):
194+
(1024).to_bytes(1, 'big')
162195

163196
with assert_raises(ValueError):
164197
# check base first

vm/src/obj/objint.rs

Lines changed: 54 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use num_integer::Integer;
66
use num_traits::{Num, One, Pow, Signed, ToPrimitive, Zero};
77

88
use crate::format::FormatSpec;
9-
use crate::function::{KwArgs, OptionalArg, PyFuncArgs};
9+
use crate::function::{OptionalArg, PyFuncArgs};
1010
use crate::obj::objtype::PyClassRef;
1111
use crate::pyhash;
1212
use crate::pyobject::{
@@ -15,6 +15,7 @@ use crate::pyobject::{
1515
};
1616
use crate::vm::VirtualMachine;
1717

18+
use super::objbool::IntoPyBool;
1819
use super::objbyteinner::PyByteInner;
1920
use super::objbytes::PyBytes;
2021
use super::objint;
@@ -578,29 +579,21 @@ impl PyInt {
578579

579580
#[pymethod]
580581
#[allow(clippy::match_bool)]
581-
fn from_bytes(
582-
bytes: PyByteInner,
583-
byteorder: PyStringRef,
584-
kwargs: KwArgs,
585-
vm: &VirtualMachine,
586-
) -> PyResult<BigInt> {
587-
let mut signed = false;
588-
for (key, value) in kwargs.into_iter() {
589-
if key == "signed" {
590-
signed = match_class!(match value {
591-
b @ PyInt => !b.as_bigint().is_zero(),
592-
_ => false,
593-
});
594-
}
595-
}
596-
let x = match byteorder.as_str() {
582+
fn from_bytes(args: IntFromByteArgs, vm: &VirtualMachine) -> PyResult<BigInt> {
583+
let signed = if let OptionalArg::Present(signed) = args.signed {
584+
signed.to_bool()
585+
} else {
586+
false
587+
};
588+
589+
let x = match args.byteorder.as_str() {
597590
"big" => match signed {
598-
true => BigInt::from_signed_bytes_be(&bytes.elements),
599-
false => BigInt::from_bytes_be(Sign::Plus, &bytes.elements),
591+
true => BigInt::from_signed_bytes_be(&args.bytes.elements),
592+
false => BigInt::from_bytes_be(Sign::Plus, &args.bytes.elements),
600593
},
601594
"little" => match signed {
602-
true => BigInt::from_signed_bytes_le(&bytes.elements),
603-
false => BigInt::from_bytes_le(Sign::Plus, &bytes.elements),
595+
true => BigInt::from_signed_bytes_le(&args.bytes.elements),
596+
false => BigInt::from_bytes_le(Sign::Plus, &args.bytes.elements),
604597
},
605598
_ => {
606599
return Err(
@@ -610,36 +603,30 @@ impl PyInt {
610603
};
611604
Ok(x)
612605
}
606+
613607
#[pymethod]
614608
#[allow(clippy::match_bool)]
615-
fn to_bytes(
616-
&self,
617-
length: PyIntRef,
618-
byteorder: PyStringRef,
619-
kwargs: KwArgs,
620-
vm: &VirtualMachine,
621-
) -> PyResult<PyBytes> {
622-
let mut signed = false;
609+
fn to_bytes(&self, args: IntToByteArgs, vm: &VirtualMachine) -> PyResult<PyBytes> {
610+
let signed = if let OptionalArg::Present(signed) = args.signed {
611+
signed.to_bool()
612+
} else {
613+
false
614+
};
615+
623616
let value = self.as_bigint();
624-
for (key, value) in kwargs.into_iter() {
625-
if key == "signed" {
626-
signed = match_class!(match value {
627-
b @ PyInt => !b.as_bigint().is_zero(),
628-
_ => false,
629-
});
630-
}
631-
}
632617
if value.sign() == Sign::Minus && !signed {
633618
return Err(vm.new_overflow_error("can't convert negative int to unsigned".to_string()));
634619
}
635-
let byte_len;
636-
if let Some(temp) = length.as_bigint().to_usize() {
637-
byte_len = temp;
620+
621+
let byte_len = if let Some(byte_len) = args.length.as_bigint().to_usize() {
622+
byte_len
638623
} else {
639-
return Err(vm.new_value_error("length parameter is illegal".to_string()));
640-
}
624+
return Err(
625+
vm.new_overflow_error("Python int too large to convert to C ssize_t".to_string())
626+
);
627+
};
641628

642-
let mut origin_bytes = match byteorder.as_str() {
629+
let mut origin_bytes = match args.byteorder.as_str() {
643630
"big" => match signed {
644631
true => value.to_signed_bytes_be(),
645632
false => value.to_bytes_be().1,
@@ -654,17 +641,19 @@ impl PyInt {
654641
);
655642
}
656643
};
644+
657645
let origin_len = origin_bytes.len();
658646
if origin_len > byte_len {
659-
return Err(vm.new_value_error("int too big to convert".to_string()));
647+
return Err(vm.new_overflow_error("int too big to convert".to_string()));
660648
}
661649

662650
let mut append_bytes = match value.sign() {
663651
Sign::Minus => vec![255u8; byte_len - origin_len],
664652
_ => vec![0u8; byte_len - origin_len],
665653
};
654+
666655
let mut bytes = vec![];
667-
match byteorder.as_str() {
656+
match args.byteorder.as_str() {
668657
"big" => {
669658
bytes = append_bytes;
670659
bytes.append(&mut origin_bytes);
@@ -675,7 +664,6 @@ impl PyInt {
675664
}
676665
_ => (),
677666
}
678-
679667
Ok(PyBytes::new(bytes))
680668
}
681669
#[pyproperty]
@@ -735,6 +723,26 @@ fn int_new(cls: PyClassRef, options: IntOptions, vm: &VirtualMachine) -> PyResul
735723
PyInt::new(options.get_int_value(vm)?).into_ref_with_type(vm, cls)
736724
}
737725

726+
#[derive(FromArgs)]
727+
struct IntFromByteArgs {
728+
#[pyarg(positional_or_keyword)]
729+
bytes: PyByteInner,
730+
#[pyarg(positional_or_keyword)]
731+
byteorder: PyStringRef,
732+
#[pyarg(keyword_only, optional = true)]
733+
signed: OptionalArg<IntoPyBool>,
734+
}
735+
736+
#[derive(FromArgs)]
737+
struct IntToByteArgs {
738+
#[pyarg(positional_or_keyword)]
739+
length: PyIntRef,
740+
#[pyarg(positional_or_keyword)]
741+
byteorder: PyStringRef,
742+
#[pyarg(keyword_only, optional = true)]
743+
signed: OptionalArg<IntoPyBool>,
744+
}
745+
738746
// Casting function:
739747
pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: &BigInt) -> PyResult<BigInt> {
740748
let base_u32 = match base.to_u32() {

0 commit comments

Comments
 (0)