From 9359a0cd052f509ac6de5c2f9d506c737834a726 Mon Sep 17 00:00:00 2001 From: lntuition Date: Sat, 28 Sep 2019 16:33:28 +0900 Subject: [PATCH 1/5] Add testcase for int.from_bytes --- tests/snippets/ints.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/snippets/ints.py b/tests/snippets/ints.py index fe3d046f9..8ac27369b 100644 --- a/tests/snippets/ints.py +++ b/tests/snippets/ints.py @@ -147,11 +147,22 @@ assert int('10', base=0) == 10 # type byte, signed, implied base assert int(b' -0XFF ', base=0) == -255 - assert int.from_bytes(b'\x00\x10', 'big') == 16 assert int.from_bytes(b'\x00\x10', 'little') == 4096 +assert int.from_bytes(b'\x00\x10', byteorder='big') == 16 +assert int.from_bytes(b'\x00\x10', byteorder='little') == 4096 +assert int.from_bytes(bytes=b'\x00\x10', byteorder='big') == 16 +assert int.from_bytes(bytes=b'\x00\x10', byteorder='little') == 4096 + assert int.from_bytes(b'\xfc\x00', 'big', signed=True) == -1024 assert int.from_bytes(b'\xfc\x00', 'big', signed=False) == 64512 +assert int.from_bytes(b'\xfc\x00', byteorder='big', signed=True) == -1024 +assert int.from_bytes(b'\xfc\x00', byteorder='big', signed=False) == 64512 +assert int.from_bytes(bytes=b'\xfc\x00', byteorder='big', signed=True) == -1024 +assert int.from_bytes(bytes=b'\xfc\x00', byteorder='big', signed=False) == 64512 + +with assert_raises(ValueError): + int.from_bytes(b'\x00\x10', 'something') assert (1024).to_bytes(4, 'big') == b'\x00\x00\x04\x00' assert (1024).to_bytes(2, 'little', signed=True) == b'\x00\x04' From 33131ab79b82b951dfb2ca4a30c57b2d73f34e72 Mon Sep 17 00:00:00 2001 From: lntuition Date: Sat, 28 Sep 2019 16:29:42 +0900 Subject: [PATCH 2/5] Fix argument keyword error in int.from_bytes Add IntFromByteOptions struct to fix int argument keyword error in int.from_bytes Fixed: #1427 --- vm/src/obj/objint.rs | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index b9ea0872c..e19c4f9c7 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -15,6 +15,7 @@ use crate::pyobject::{ }; use crate::vm::VirtualMachine; +use super::objbool::IntoPyBool; use super::objbyteinner::PyByteInner; use super::objbytes::PyBytes; use super::objint; @@ -578,29 +579,21 @@ impl PyInt { #[pymethod] #[allow(clippy::match_bool)] - fn from_bytes( - bytes: PyByteInner, - byteorder: PyStringRef, - kwargs: KwArgs, - vm: &VirtualMachine, - ) -> PyResult { - let mut signed = false; - for (key, value) in kwargs.into_iter() { - if key == "signed" { - signed = match_class!(match value { - b @ PyInt => !b.as_bigint().is_zero(), - _ => false, - }); - } - } - let x = match byteorder.as_str() { + fn from_bytes(options: IntFromByteOptions, vm: &VirtualMachine) -> PyResult { + let signed = if let OptionalArg::Present(signed) = options.signed { + signed.to_bool() + } else { + false + }; + + let x = match options.byteorder.as_str() { "big" => match signed { - true => BigInt::from_signed_bytes_be(&bytes.elements), - false => BigInt::from_bytes_be(Sign::Plus, &bytes.elements), + true => BigInt::from_signed_bytes_be(&options.bytes.elements), + false => BigInt::from_bytes_be(Sign::Plus, &options.bytes.elements), }, "little" => match signed { - true => BigInt::from_signed_bytes_le(&bytes.elements), - false => BigInt::from_bytes_le(Sign::Plus, &bytes.elements), + true => BigInt::from_signed_bytes_le(&options.bytes.elements), + false => BigInt::from_bytes_le(Sign::Plus, &options.bytes.elements), }, _ => { return Err( @@ -735,6 +728,16 @@ fn int_new(cls: PyClassRef, options: IntOptions, vm: &VirtualMachine) -> PyResul PyInt::new(options.get_int_value(vm)?).into_ref_with_type(vm, cls) } +#[derive(FromArgs)] +struct IntFromByteOptions { + #[pyarg(positional_or_keyword)] + bytes: PyByteInner, + #[pyarg(positional_or_keyword)] + byteorder: PyStringRef, + #[pyarg(keyword_only, optional = true)] + signed: OptionalArg, +} + // Casting function: pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: &BigInt) -> PyResult { let base_u32 = match base.to_u32() { From 5a277adafb112a4909e8f474a33d015307202fca Mon Sep 17 00:00:00 2001 From: lntuition Date: Sat, 28 Sep 2019 18:22:23 +0900 Subject: [PATCH 3/5] Add testcase for int.to_bytes --- tests/snippets/ints.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tests/snippets/ints.py b/tests/snippets/ints.py index 8ac27369b..97535ad77 100644 --- a/tests/snippets/ints.py +++ b/tests/snippets/ints.py @@ -165,11 +165,33 @@ with assert_raises(ValueError): int.from_bytes(b'\x00\x10', 'something') assert (1024).to_bytes(4, 'big') == b'\x00\x00\x04\x00' -assert (1024).to_bytes(2, 'little', signed=True) == b'\x00\x04' +assert (1024).to_bytes(2, 'little') == b'\x00\x04' +assert (1024).to_bytes(4, byteorder='big') == b'\x00\x00\x04\x00' +assert (1024).to_bytes(2, byteorder='little') == b'\x00\x04' +assert (1024).to_bytes(length=4, byteorder='big') == b'\x00\x00\x04\x00' +assert (1024).to_bytes(length=2, byteorder='little') == b'\x00\x04' + assert (-1024).to_bytes(4, 'big', signed=True) == b'\xff\xff\xfc\x00' assert (-1024).to_bytes(4, 'little', signed=True) == b'\x00\xfc\xff\xff' + assert (2147483647).to_bytes(8, 'big', signed=False) == b'\x00\x00\x00\x00\x7f\xff\xff\xff' assert (-2147483648).to_bytes(8, 'little', signed=True) == b'\x00\x00\x00\x80\xff\xff\xff\xff' +assert (2147483647).to_bytes(8, byteorder='big', signed=False) == b'\x00\x00\x00\x00\x7f\xff\xff\xff' +assert (-2147483648).to_bytes(8, byteorder='little', signed=True) == b'\x00\x00\x00\x80\xff\xff\xff\xff' +assert (2147483647).to_bytes(length=8, byteorder='big', signed=False) == b'\x00\x00\x00\x00\x7f\xff\xff\xff' +assert (-2147483648).to_bytes(length=8, byteorder='little', signed=True) == b'\x00\x00\x00\x80\xff\xff\xff\xff' + +with assert_raises(ValueError): + (1024).to_bytes(4, 'something') + +with assert_raises(OverflowError): + (-1024).to_bytes(4, 'big') + +with assert_raises(OverflowError): + (1024).to_bytes(10000000000000000000000, 'big') + +with assert_raises(OverflowError): + (1024).to_bytes(1, 'big') with assert_raises(ValueError): # check base first From 0700a9e81dcea311253e5a7d72beccf3c7df4cda Mon Sep 17 00:00:00 2001 From: lntuition Date: Sat, 28 Sep 2019 17:58:27 +0900 Subject: [PATCH 4/5] Fix argument keyword error in int.to_bytes Add IntToByteOptions struct to fix int argument keyword error in int.to_bytes Also fix some ValueError to OverflowError like cPython --- vm/src/obj/objint.rs | 59 ++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index e19c4f9c7..0c58ccca2 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -6,7 +6,7 @@ use num_integer::Integer; use num_traits::{Num, One, Pow, Signed, ToPrimitive, Zero}; use crate::format::FormatSpec; -use crate::function::{KwArgs, OptionalArg, PyFuncArgs}; +use crate::function::{OptionalArg, PyFuncArgs}; use crate::obj::objtype::PyClassRef; use crate::pyhash; use crate::pyobject::{ @@ -603,36 +603,30 @@ impl PyInt { }; Ok(x) } + #[pymethod] #[allow(clippy::match_bool)] - fn to_bytes( - &self, - length: PyIntRef, - byteorder: PyStringRef, - kwargs: KwArgs, - vm: &VirtualMachine, - ) -> PyResult { - let mut signed = false; + fn to_bytes(&self, options: IntToByteOptions, vm: &VirtualMachine) -> PyResult { + let signed = if let OptionalArg::Present(signed) = options.signed { + signed.to_bool() + } else { + false + }; + let value = self.as_bigint(); - for (key, value) in kwargs.into_iter() { - if key == "signed" { - signed = match_class!(match value { - b @ PyInt => !b.as_bigint().is_zero(), - _ => false, - }); - } - } if value.sign() == Sign::Minus && !signed { return Err(vm.new_overflow_error("can't convert negative int to unsigned".to_string())); } - let byte_len; - if let Some(temp) = length.as_bigint().to_usize() { - byte_len = temp; - } else { - return Err(vm.new_value_error("length parameter is illegal".to_string())); - } - let mut origin_bytes = match byteorder.as_str() { + let byte_len = if let Some(byte_len) = options.length.as_bigint().to_usize() { + byte_len + } else { + return Err( + vm.new_overflow_error("Python int too large to convert to C ssize_t".to_string()) + ); + }; + + let mut origin_bytes = match options.byteorder.as_str() { "big" => match signed { true => value.to_signed_bytes_be(), false => value.to_bytes_be().1, @@ -647,17 +641,19 @@ impl PyInt { ); } }; + let origin_len = origin_bytes.len(); if origin_len > byte_len { - return Err(vm.new_value_error("int too big to convert".to_string())); + return Err(vm.new_overflow_error("int too big to convert".to_string())); } let mut append_bytes = match value.sign() { Sign::Minus => vec![255u8; byte_len - origin_len], _ => vec![0u8; byte_len - origin_len], }; + let mut bytes = vec![]; - match byteorder.as_str() { + match options.byteorder.as_str() { "big" => { bytes = append_bytes; bytes.append(&mut origin_bytes); @@ -668,7 +664,6 @@ impl PyInt { } _ => (), } - Ok(PyBytes::new(bytes)) } #[pyproperty] @@ -738,6 +733,16 @@ struct IntFromByteOptions { signed: OptionalArg, } +#[derive(FromArgs)] +struct IntToByteOptions { + #[pyarg(positional_or_keyword)] + length: PyIntRef, + #[pyarg(positional_or_keyword)] + byteorder: PyStringRef, + #[pyarg(keyword_only, optional = true)] + signed: OptionalArg, +} + // Casting function: pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: &BigInt) -> PyResult { let base_u32 = match base.to_u32() { From 8336bc9524bcea71e35aa4223d83a0db097d9f40 Mon Sep 17 00:00:00 2001 From: lntuition Date: Sun, 29 Sep 2019 12:27:39 +0900 Subject: [PATCH 5/5] Rename struct and arguments of int.from_bytes and int.to_bytes --- vm/src/obj/objint.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 0c58ccca2..0fd01b078 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -579,21 +579,21 @@ impl PyInt { #[pymethod] #[allow(clippy::match_bool)] - fn from_bytes(options: IntFromByteOptions, vm: &VirtualMachine) -> PyResult { - let signed = if let OptionalArg::Present(signed) = options.signed { + fn from_bytes(args: IntFromByteArgs, vm: &VirtualMachine) -> PyResult { + let signed = if let OptionalArg::Present(signed) = args.signed { signed.to_bool() } else { false }; - let x = match options.byteorder.as_str() { + let x = match args.byteorder.as_str() { "big" => match signed { - true => BigInt::from_signed_bytes_be(&options.bytes.elements), - false => BigInt::from_bytes_be(Sign::Plus, &options.bytes.elements), + true => BigInt::from_signed_bytes_be(&args.bytes.elements), + false => BigInt::from_bytes_be(Sign::Plus, &args.bytes.elements), }, "little" => match signed { - true => BigInt::from_signed_bytes_le(&options.bytes.elements), - false => BigInt::from_bytes_le(Sign::Plus, &options.bytes.elements), + true => BigInt::from_signed_bytes_le(&args.bytes.elements), + false => BigInt::from_bytes_le(Sign::Plus, &args.bytes.elements), }, _ => { return Err( @@ -606,8 +606,8 @@ impl PyInt { #[pymethod] #[allow(clippy::match_bool)] - fn to_bytes(&self, options: IntToByteOptions, vm: &VirtualMachine) -> PyResult { - let signed = if let OptionalArg::Present(signed) = options.signed { + fn to_bytes(&self, args: IntToByteArgs, vm: &VirtualMachine) -> PyResult { + let signed = if let OptionalArg::Present(signed) = args.signed { signed.to_bool() } else { false @@ -618,7 +618,7 @@ impl PyInt { return Err(vm.new_overflow_error("can't convert negative int to unsigned".to_string())); } - let byte_len = if let Some(byte_len) = options.length.as_bigint().to_usize() { + let byte_len = if let Some(byte_len) = args.length.as_bigint().to_usize() { byte_len } else { return Err( @@ -626,7 +626,7 @@ impl PyInt { ); }; - let mut origin_bytes = match options.byteorder.as_str() { + let mut origin_bytes = match args.byteorder.as_str() { "big" => match signed { true => value.to_signed_bytes_be(), false => value.to_bytes_be().1, @@ -653,7 +653,7 @@ impl PyInt { }; let mut bytes = vec![]; - match options.byteorder.as_str() { + match args.byteorder.as_str() { "big" => { bytes = append_bytes; bytes.append(&mut origin_bytes); @@ -724,7 +724,7 @@ fn int_new(cls: PyClassRef, options: IntOptions, vm: &VirtualMachine) -> PyResul } #[derive(FromArgs)] -struct IntFromByteOptions { +struct IntFromByteArgs { #[pyarg(positional_or_keyword)] bytes: PyByteInner, #[pyarg(positional_or_keyword)] @@ -734,7 +734,7 @@ struct IntFromByteOptions { } #[derive(FromArgs)] -struct IntToByteOptions { +struct IntToByteArgs { #[pyarg(positional_or_keyword)] length: PyIntRef, #[pyarg(positional_or_keyword)]