From c550360be5f931763f7a44f4f1d35de95377a557 Mon Sep 17 00:00:00 2001 From: jgirardet Date: Sat, 13 Apr 2019 08:39:29 +0200 Subject: [PATCH 01/26] add is_bytes_like add bytes.count wthout slice --- tests/snippets/bytes.py | 8 ++++++++ vm/src/obj/objbyteinner.rs | 27 +++++++++++++++++++++++++++ vm/src/obj/objbytes.rs | 12 +++++++++++- vm/src/obj/objmemory.rs | 7 +++++++ 4 files changed, 53 insertions(+), 1 deletion(-) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index c496bfeffb..64cc003d35 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -162,3 +162,11 @@ with assertRaises(TypeError): b"b".center(2, b"ba") b"kok".center(5, bytearray(b"x")) + +# count +assert b"azeazerazeazopia".count(b"aze") == 3 +assert b"azeazerazeazopia".count(b"az") == 4 +assert b"azeazerazeazopia".count(b"a") == 5 +assert b"123456789".count(b"") == 10 +assert b"azeazerazeazopia".count(bytearray(b"aze")) == 3 +assert b"azeazerazeazopia".count(memoryview(b"aze")) == 3 diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index d0a63633ac..73808f533f 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -19,6 +19,7 @@ use num_traits::ToPrimitive; use super::objbytearray::{get_value as get_value_bytearray, PyByteArray}; use super::objbytes::PyBytes; +use super::objmemory::PyMemoryView; #[derive(Debug, Default, Clone)] pub struct PyByteInner { @@ -386,6 +387,23 @@ impl PyByteInner { res } + + pub fn count(&self, sub: Vec) -> usize { + if sub.is_empty() { + return self.len() + 1; + } + + let mut total: usize = 0; + for (n, i) in self.elements.iter().enumerate() { + if n + sub.len() <= self.len() + && *i == sub[0] + && &self.elements[n..n + sub.len()] == sub.as_slice() + { + total += 1; + } + } + total + } } pub fn is_byte(obj: &PyObjectRef) -> Option> { @@ -395,3 +413,12 @@ pub fn is_byte(obj: &PyObjectRef) -> Option> { j @ PyByteArray => Some(get_value_bytearray(&j.as_object()).to_vec()), _ => None) } + +pub fn is_bytes_like(obj: &PyObjectRef) -> Option> { + match_class!(obj.clone(), + + i @ PyBytes => Some(i.get_value().to_vec()), + j @ PyByteArray => Some(get_value_bytearray(&j.as_object()).to_vec()), + k @ PyMemoryView => Some(k.get_obj_value().unwrap()), + _ => None) +} diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 0556de0535..fd3fad7580 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -7,7 +7,7 @@ use std::ops::Deref; use crate::function::OptionalArg; use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; -use super::objbyteinner::{is_byte, PyByteInner}; +use super::objbyteinner::{is_byte, is_bytes_like, PyByteInner}; use super::objiter; use super::objslice::PySlice; use super::objtype::PyClassRef; @@ -269,6 +269,16 @@ impl PyBytesRef { obj => {Err(vm.new_type_error(format!("{} cannot be interpreted as an integer", obj)))} ) } + + #[pymethod(name = "count")] + fn count(self, width: PyObjectRef, vm: &VirtualMachine) -> PyResult { + match is_bytes_like(&width) { + Some(value) => Ok(vm.new_int(self.inner.count(value))), + None => { + Err(vm.new_type_error(format!("a bytes-like object is required, not {}", width))) + } + } + } } #[derive(Debug)] diff --git a/vm/src/obj/objmemory.rs b/vm/src/obj/objmemory.rs index cfe44dea38..a0e0613f97 100644 --- a/vm/src/obj/objmemory.rs +++ b/vm/src/obj/objmemory.rs @@ -1,3 +1,4 @@ +use crate::obj::objbyteinner::is_byte; use crate::obj::objtype::PyClassRef; use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; @@ -9,6 +10,12 @@ pub struct PyMemoryView { obj: PyObjectRef, } +impl PyMemoryView { + pub fn get_obj_value(&self) -> Option> { + is_byte(&self.obj) + } +} + impl PyValue for PyMemoryView { fn class(vm: &VirtualMachine) -> PyClassRef { vm.ctx.memoryview_type() From 00cf5248147deb8df6fd0ddc0950270141790e1e Mon Sep 17 00:00:00 2001 From: jgirardet Date: Sun, 14 Apr 2019 21:33:53 +0200 Subject: [PATCH 02/26] add slice capability to bytes. add trait IsByte --- tests/snippets/bytes.py | 11 ++++++++ vm/src/obj/objbyteinner.rs | 57 +++++++++++++++++++++++++++++++++----- vm/src/obj/objbytes.rs | 21 +++++++++----- 3 files changed, 75 insertions(+), 14 deletions(-) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index 64cc003d35..10de0d9005 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -170,3 +170,14 @@ assert b"123456789".count(b"") == 10 assert b"azeazerazeazopia".count(bytearray(b"aze")) == 3 assert b"azeazerazeazopia".count(memoryview(b"aze")) == 3 +assert b"azeazerazeazopia".count(memoryview(b"aze"),1,9 ) == 1 +assert b"azeazerazeazopia".count(b"aze", None, None) == 3 +assert b"azeazerazeazopia".count(b"aze", 2, None) == 2 +assert b"azeazerazeazopia".count(b"aze", 2) == 2 +assert b"azeazerazeazopia".count(b"aze", None, 7) == 2 +assert b"azeazerazeazopia".count(b"aze", None, 7) == 2 +assert b"azeazerazeazopia".count(b"aze", 2, 7) == 1 +assert b"azeazerazeazopia".count(b"aze", -13, -10) == 1 +assert b"azeazerazeazopia".count(b"aze", 1, 10000) == 2 +with assertRaises(ValueError): + b"ilj".count(355) diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index 73808f533f..1d72bf209a 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -20,6 +20,7 @@ use num_traits::ToPrimitive; use super::objbytearray::{get_value as get_value_bytearray, PyByteArray}; use super::objbytes::PyBytes; use super::objmemory::PyMemoryView; +use super::objnone::PyNone; #[derive(Debug, Default, Clone)] pub struct PyByteInner { @@ -388,21 +389,52 @@ impl PyByteInner { res } - pub fn count(&self, sub: Vec) -> usize { + pub fn count( + &self, + sub: Vec, + start: OptionalArg, + end: OptionalArg, + vm: &VirtualMachine, + ) -> Result { + let start = if let OptionalArg::Present(st) = start { + match_class!(st, + i @ PyInt => {Some(i.as_bigint().clone())}, + _obj @ PyNone => None, + _=> {return Err(vm.new_type_error("slice indices must be integers or None or have an __index__ method".to_string()));} + ) + } else { + None + }; + let end = if let OptionalArg::Present(e) = end { + match_class!(e, + i @ PyInt => {Some(i.as_bigint().clone())}, + _obj @ PyNone => None, + _=> {return Err(vm.new_type_error("slice indices must be integers or None or have an __index__ method".to_string()));} + ) + } else { + None + }; + + let range = self.elements.get_slice_range(&start, &end); + if sub.is_empty() { - return self.len() + 1; + return Ok(self.len() + 1); } let mut total: usize = 0; - for (n, i) in self.elements.iter().enumerate() { - if n + sub.len() <= self.len() - && *i == sub[0] - && &self.elements[n..n + sub.len()] == sub.as_slice() + let mut i_start = range.start; + let i_end = range.end; + + for i in self.elements.do_slice(range) { + if i_start + sub.len() <= i_end + && i == sub[0] + && &self.elements[i_start..(i_start + sub.len())] == sub.as_slice() { total += 1; } + i_start += 1; } - total + Ok(total) } } @@ -422,3 +454,14 @@ pub fn is_bytes_like(obj: &PyObjectRef) -> Option> { k @ PyMemoryView => Some(k.get_obj_value().unwrap()), _ => None) } + +pub trait IsByte: ToPrimitive { + fn is_byte(&self, vm: &VirtualMachine) -> Result { + match self.to_u8() { + Some(value) => Ok(value), + None => Err(vm.new_value_error("byte must be in range(0, 256)".to_string())), + } + } +} + +impl IsByte for BigInt {} diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index fd3fad7580..338e51cbb5 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -7,7 +7,7 @@ use std::ops::Deref; use crate::function::OptionalArg; use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; -use super::objbyteinner::{is_byte, is_bytes_like, PyByteInner}; +use super::objbyteinner::{is_byte, is_bytes_like, IsByte, PyByteInner}; use super::objiter; use super::objslice::PySlice; use super::objtype::PyClassRef; @@ -271,12 +271,19 @@ impl PyBytesRef { } #[pymethod(name = "count")] - fn count(self, width: PyObjectRef, vm: &VirtualMachine) -> PyResult { - match is_bytes_like(&width) { - Some(value) => Ok(vm.new_int(self.inner.count(value))), - None => { - Err(vm.new_type_error(format!("a bytes-like object is required, not {}", width))) - } + fn count( + self, + sub: PyObjectRef, + start: OptionalArg, + end: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult { + match is_bytes_like(&sub) { + Some(value) => Ok(vm.new_int(self.inner.count(value, start, end, vm)?)), + None => match_class!(sub, + i @ PyInt => { + Ok(vm.new_int(self.inner.count(vec![i.as_bigint().is_byte(vm)?], start, end, vm)?))}, + obj => {Err(vm.new_type_error(format!("a bytes-like object is required, not {}", obj)))}), } } } From e6d6b77698ab94e7417d5d2085d37e767f48ef4e Mon Sep 17 00:00:00 2001 From: jgirardet Date: Sun, 14 Apr 2019 21:43:54 +0200 Subject: [PATCH 03/26] fix error msg --- vm/src/obj/objbytes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 338e51cbb5..e8fdf0e568 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -283,7 +283,7 @@ impl PyBytesRef { None => match_class!(sub, i @ PyInt => { Ok(vm.new_int(self.inner.count(vec![i.as_bigint().is_byte(vm)?], start, end, vm)?))}, - obj => {Err(vm.new_type_error(format!("a bytes-like object is required, not {}", obj)))}), + obj => {Err(vm.new_type_error(format!("argument should be integer or bytes-like object, not {}", obj)))}), } } } From 5f5d5c4278ef992728a5c71d2d8efaa6836f4613 Mon Sep 17 00:00:00 2001 From: jgirardet Date: Sun, 14 Apr 2019 22:52:27 +0200 Subject: [PATCH 04/26] add bytes.join --- tests/snippets/bytes.py | 12 ++++++++++-- vm/src/obj/objbyteinner.rs | 21 ++++++++++++++++++++- vm/src/obj/objbytes.rs | 8 ++++++-- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index 10de0d9005..14fe9c2412 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -170,7 +170,7 @@ assert b"123456789".count(b"") == 10 assert b"azeazerazeazopia".count(bytearray(b"aze")) == 3 assert b"azeazerazeazopia".count(memoryview(b"aze")) == 3 -assert b"azeazerazeazopia".count(memoryview(b"aze"),1,9 ) == 1 +assert b"azeazerazeazopia".count(memoryview(b"aze"), 1, 9) == 1 assert b"azeazerazeazopia".count(b"aze", None, None) == 3 assert b"azeazerazeazopia".count(b"aze", 2, None) == 2 assert b"azeazerazeazopia".count(b"aze", 2) == 2 @@ -180,4 +180,12 @@ assert b"azeazerazeazopia".count(b"aze", -13, -10) == 1 assert b"azeazerazeazopia".count(b"aze", 1, 10000) == 2 with assertRaises(ValueError): - b"ilj".count(355) + b"ilj".count(3550) + +# join +assert ( + b"".join((b"jiljl", bytearray(b"kmoomk"), memoryview(b"aaaa"))) + == b"jiljlkmoomkaaaa" +) +with assertRaises(TypeError): + b"".join((b"km", "kl")) diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index 1d72bf209a..377e93ec5e 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -1,4 +1,4 @@ -use crate::pyobject::PyObjectRef; +use crate::pyobject::{PyIterable, PyObjectRef}; use num_bigint::BigInt; use crate::function::OptionalArg; @@ -436,6 +436,25 @@ impl PyByteInner { } Ok(total) } + + pub fn join(&self, iter: PyIterable, vm: &VirtualMachine) -> PyResult { + let mut refs = vec![]; + for (index, v) in iter.iter(vm)?.enumerate() { + let v = v?; + match is_bytes_like(&v) { + None => { + return Err(vm.new_type_error(format!( + "sequence item {}: expected a bytes-like object, {} found", + index, + &v.class().name, + ))); + } + Some(value) => refs.extend(value), + } + } + + Ok(vm.ctx.new_bytes(refs)) + } } pub fn is_byte(obj: &PyObjectRef) -> Option> { diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index e8fdf0e568..e34b4ee5ea 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -5,13 +5,12 @@ use core::cell::Cell; use std::ops::Deref; use crate::function::OptionalArg; -use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; +use crate::pyobject::{PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue}; use super::objbyteinner::{is_byte, is_bytes_like, IsByte, PyByteInner}; use super::objiter; use super::objslice::PySlice; use super::objtype::PyClassRef; - /// "bytes(iterable_of_ints) -> bytes\n\ /// bytes(string, encoding[, errors]) -> bytes\n\ /// bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer\n\ @@ -286,6 +285,11 @@ impl PyBytesRef { obj => {Err(vm.new_type_error(format!("argument should be integer or bytes-like object, not {}", obj)))}), } } + + #[pymethod(name = "join")] + fn join(self, iter: PyIterable, vm: &VirtualMachine) -> PyResult { + self.inner.join(iter, vm) + } } #[derive(Debug)] From 14658b6236cf0d34be781a3b3664bfa87091edc0 Mon Sep 17 00:00:00 2001 From: Jimmy Girardet Date: Wed, 17 Apr 2019 08:48:20 +0200 Subject: [PATCH 05/26] move is_byte to try_as_byte move trait IsByte to ByteOr refactor center refactor count fix center with negative value add test for count(integer) --- tests/snippets/bytes.py | 2 ++ vm/src/obj/objbyteinner.rs | 64 +++++++++++++++++++++++++++++++------- vm/src/obj/objbytes.rs | 40 +++--------------------- vm/src/obj/objmemory.rs | 4 +-- 4 files changed, 60 insertions(+), 50 deletions(-) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index 14fe9c2412..ec8001213e 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -162,6 +162,7 @@ with assertRaises(TypeError): b"b".center(2, b"ba") b"kok".center(5, bytearray(b"x")) +b"kok".center(-5,) # count assert b"azeazerazeazopia".count(b"aze") == 3 @@ -181,6 +182,7 @@ assert b"azeazerazeazopia".count(b"aze", 1, 10000) == 2 with assertRaises(ValueError): b"ilj".count(3550) +assert b"azeazerazeazopia".count(97) == 5 # join assert ( diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index 377e93ec5e..26f33c2e68 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -362,12 +362,45 @@ impl PyByteInner { .collect::>()) } - pub fn center(&self, width: &BigInt, fillbyte: u8, _vm: &VirtualMachine) -> Vec { - let width = width.to_usize().unwrap(); + pub fn center(&self, width_a: PyObjectRef, fillbyte: OptionalArg, vm: &VirtualMachine) -> PyResult> { + let fillbyte = if let OptionalArg::Present(v) = fillbyte { + match try_as_byte(&v) { + Some(x) => { + if x.len() == 1 { + x[0] + } else { + return Err(vm.new_type_error(format!( + "center() argument 2 must be a byte string of length 1, not {}", + &v + ))); + } + } + None => { + return Err(vm.new_type_error(format!( + "center() argument 2 must be a byte string of length 1, not {}", + &v + ))); + } + } + } else { + 32 // default is space + }; + + let width_b = match_class!(width_a, + i @PyInt => i, + obj => {return Err(vm.new_type_error(format!("{} cannot be interpreted as an integer", obj)));} + ); + + + // <0 = no change + let width = if let Some(x) = width_b.as_bigint().to_usize() { + x + } else {return Ok(self.elements.clone());}; + // adjust right et left side if width <= self.len() { - return self.elements.clone(); + return Ok(self.elements.clone()); } let diff: usize = width - self.len(); let mut ln: usize = diff / 2; @@ -386,16 +419,23 @@ impl PyByteInner { res.extend_from_slice(&self.elements[..]); res.extend_from_slice(&vec![fillbyte; rn][..]); - res + Ok(res) } pub fn count( &self, - sub: Vec, + sub: PyObjectRef, start: OptionalArg, end: OptionalArg, vm: &VirtualMachine, - ) -> Result { + ) -> PyResult { + let sub = match try_as_bytes_like(&sub) { + Some(value) => value, + None => match_class!(sub, + i @ PyInt => + vec![i.as_bigint().byte_or(vm)?], + obj => {return Err(vm.new_type_error(format!("argument should be integer or bytes-like object, not {}", obj)));}), + }; let start = if let OptionalArg::Present(st) = start { match_class!(st, i @ PyInt => {Some(i.as_bigint().clone())}, @@ -441,7 +481,7 @@ impl PyByteInner { let mut refs = vec![]; for (index, v) in iter.iter(vm)?.enumerate() { let v = v?; - match is_bytes_like(&v) { + match try_as_bytes_like(&v) { None => { return Err(vm.new_type_error(format!( "sequence item {}: expected a bytes-like object, {} found", @@ -457,7 +497,7 @@ impl PyByteInner { } } -pub fn is_byte(obj: &PyObjectRef) -> Option> { +pub fn try_as_byte(obj: &PyObjectRef) -> Option> { match_class!(obj.clone(), i @ PyBytes => Some(i.get_value().to_vec()), @@ -465,7 +505,7 @@ pub fn is_byte(obj: &PyObjectRef) -> Option> { _ => None) } -pub fn is_bytes_like(obj: &PyObjectRef) -> Option> { +pub fn try_as_bytes_like(obj: &PyObjectRef) -> Option> { match_class!(obj.clone(), i @ PyBytes => Some(i.get_value().to_vec()), @@ -474,8 +514,8 @@ pub fn is_bytes_like(obj: &PyObjectRef) -> Option> { _ => None) } -pub trait IsByte: ToPrimitive { - fn is_byte(&self, vm: &VirtualMachine) -> Result { +pub trait ByteOr: ToPrimitive { + fn byte_or(&self, vm: &VirtualMachine) -> Result { match self.to_u8() { Some(value) => Ok(value), None => Err(vm.new_value_error("byte must be in range(0, 256)".to_string())), @@ -483,4 +523,4 @@ pub trait IsByte: ToPrimitive { } } -impl IsByte for BigInt {} +impl ByteOr for BigInt {} diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index e34b4ee5ea..851c8d8ab7 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -7,7 +7,7 @@ use std::ops::Deref; use crate::function::OptionalArg; use crate::pyobject::{PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue}; -use super::objbyteinner::{is_byte, is_bytes_like, IsByte, PyByteInner}; +use super::objbyteinner::PyByteInner; use super::objiter; use super::objslice::PySlice; use super::objtype::PyClassRef; @@ -240,33 +240,7 @@ impl PyBytesRef { fillbyte: OptionalArg, vm: &VirtualMachine, ) -> PyResult { - let sym = if let OptionalArg::Present(v) = fillbyte { - match is_byte(&v) { - Some(x) => { - if x.len() == 1 { - x[0] - } else { - return Err(vm.new_type_error(format!( - "center() argument 2 must be a byte string of length 1, not {}", - &v - ))); - } - } - None => { - return Err(vm.new_type_error(format!( - "center() argument 2 must be a byte string of length 1, not {}", - &v - ))); - } - } - } else { - 32 // default is space - }; - - match_class!(width, - i @PyInt => Ok(vm.ctx.new_bytes(self.inner.center(i.as_bigint(), sym, vm))), - obj => {Err(vm.new_type_error(format!("{} cannot be interpreted as an integer", obj)))} - ) + Ok(vm.ctx.new_bytes(self.inner.center(width, fillbyte, vm)?)) } #[pymethod(name = "count")] @@ -276,14 +250,8 @@ impl PyBytesRef { start: OptionalArg, end: OptionalArg, vm: &VirtualMachine, - ) -> PyResult { - match is_bytes_like(&sub) { - Some(value) => Ok(vm.new_int(self.inner.count(value, start, end, vm)?)), - None => match_class!(sub, - i @ PyInt => { - Ok(vm.new_int(self.inner.count(vec![i.as_bigint().is_byte(vm)?], start, end, vm)?))}, - obj => {Err(vm.new_type_error(format!("argument should be integer or bytes-like object, not {}", obj)))}), - } + ) -> PyResult { + self.inner.count(sub, start, end, vm) } #[pymethod(name = "join")] diff --git a/vm/src/obj/objmemory.rs b/vm/src/obj/objmemory.rs index a0e0613f97..88040ae14a 100644 --- a/vm/src/obj/objmemory.rs +++ b/vm/src/obj/objmemory.rs @@ -1,4 +1,4 @@ -use crate::obj::objbyteinner::is_byte; +use crate::obj::objbyteinner::try_as_byte; use crate::obj::objtype::PyClassRef; use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; @@ -12,7 +12,7 @@ pub struct PyMemoryView { impl PyMemoryView { pub fn get_obj_value(&self) -> Option> { - is_byte(&self.obj) + try_as_byte(&self.obj) } } From 8a7c46da7b7a26d9ff2e522a551a1267c8be8dbc Mon Sep 17 00:00:00 2001 From: Jimmy Girardet Date: Wed, 17 Apr 2019 15:55:37 +0200 Subject: [PATCH 06/26] add endswith startswith --- tests/snippets/bytes.py | 18 +++++++++++- vm/src/obj/objbyteinner.rs | 59 +++++++++++++++++++++++++++++++++++++- vm/src/obj/objbytes.rs | 22 ++++++++++++++ vm/src/obj/objsequence.rs | 19 ++++++++++++ 4 files changed, 116 insertions(+), 2 deletions(-) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index ec8001213e..755707a766 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -162,7 +162,7 @@ with assertRaises(TypeError): b"b".center(2, b"ba") b"kok".center(5, bytearray(b"x")) -b"kok".center(-5,) +b"kok".center(-5) # count assert b"azeazerazeazopia".count(b"aze") == 3 @@ -191,3 +191,19 @@ ) with assertRaises(TypeError): b"".join((b"km", "kl")) + + +# endswith startswith +assert b"abcde".endswith(b"de") +assert b"abcde".endswith(b"") +assert not b"abcde".endswith(b"zx") +assert b"abcde".endswith(b"bc", 0, 3) +assert not b"abcde".endswith(b"bc", 2, 3) +assert b"abcde".endswith((b"c", b"de")) + +assert b"abcde".startswith(b"ab") +assert b"abcde".startswith(b"") +assert not b"abcde".startswith(b"zx") +assert b"abcde".startswith(b"cd", 2) +assert not b"abcde".startswith(b"cd", 1, 4) +assert b"abcde".startswith((b"a", b"bc")) diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index 26f33c2e68..cc4111a8b3 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -12,7 +12,8 @@ use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; use super::objint; -use super::objsequence::PySliceableSequence; +use super::objtype; +use super::objsequence::{PySliceableSequence, is_valid_slice_arg}; use crate::obj::objint::PyInt; use num_integer::Integer; use num_traits::ToPrimitive; @@ -21,6 +22,7 @@ use super::objbytearray::{get_value as get_value_bytearray, PyByteArray}; use super::objbytes::PyBytes; use super::objmemory::PyMemoryView; use super::objnone::PyNone; +use super::objsequence; #[derive(Debug, Default, Clone)] pub struct PyByteInner { @@ -495,6 +497,61 @@ impl PyByteInner { Ok(vm.ctx.new_bytes(refs)) } + + pub fn startsendswith( + &self, + arg: PyObjectRef, + start: OptionalArg, + end: OptionalArg, + endswith: bool, // true for endswith, false for startswith + vm: &VirtualMachine, + ) -> PyResult { + let suff = if objtype::isinstance(&arg, &vm.ctx.tuple_type()) { + let mut flatten = vec![]; + for v in objsequence::get_elements(&arg).to_vec() { + match try_as_bytes_like(&v) { + None => { + return Err(vm.new_type_error(format!( + "a bytes-like object is required, not {}", + &v.class().name, + ))); + } + Some(value) => flatten.extend(value), + } + } + flatten + } else { + match try_as_bytes_like(&arg) { + Some(value) => value, + None => { + return Err(vm.new_type_error(format!( + "endswith first arg must be bytes or a tuple of bytes, not {}", + arg + ))); + } + } + }; + + if suff.is_empty() { + return Ok(vm.new_bool(true)); + } + let range = self.elements.get_slice_range( + &is_valid_slice_arg(start, vm)?, + &is_valid_slice_arg(end, vm)?, + ); + + if range.end - range.start < suff.len() { + return Ok(vm.new_bool(false)); + } + + let offset = if endswith { + (range.end - suff.len())..range.end + } else { + 0..suff.len() + }; + + Ok(vm.new_bool(suff.as_slice() == &self.elements.do_slice(range)[offset])) + } } pub fn try_as_byte(obj: &PyObjectRef) -> Option> { diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 851c8d8ab7..0c6e5c4b90 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -258,6 +258,28 @@ impl PyBytesRef { fn join(self, iter: PyIterable, vm: &VirtualMachine) -> PyResult { self.inner.join(iter, vm) } + + #[pymethod(name = "endswith")] + fn endswith( + self, + suffix: PyObjectRef, + start: OptionalArg, + end: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult { + self.inner.startsendswith(suffix, start, end, true, vm) + } + + #[pymethod(name = "startswith")] + fn startswith( + self, + suffix: PyObjectRef, + start: OptionalArg, + end: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult { + self.inner.startsendswith(suffix, start, end, false, vm) + } } #[derive(Debug)] diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 5594ac8586..af20343708 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -1,3 +1,5 @@ +use crate::function::OptionalArg; +use crate::obj::objnone::PyNone; use std::cell::RefCell; use std::marker::Sized; use std::ops::{Deref, DerefMut, Range}; @@ -371,3 +373,20 @@ pub fn get_mut_elements<'a>(obj: &'a PyObjectRef) -> impl DerefMut, + vm: &VirtualMachine, +) -> Result, PyObjectRef> { + if let OptionalArg::Present(value) = arg { + match_class!(value, + i @ PyInt => Ok(Some(i.as_bigint().clone())), + _obj @ PyNone => Ok(None), + _=> {return Err(vm.new_type_error("slice indices must be integers or None or have an __index__ method".to_string()));} + // TODO: check for an __index__ method + ) + } else { + Ok(None) + } +} From bd2166789df931ea393fae0fe666eadb3e9473ed Mon Sep 17 00:00:00 2001 From: Jimmy Girardet Date: Wed, 17 Apr 2019 16:12:44 +0200 Subject: [PATCH 07/26] add bytes index and find --- tests/snippets/bytes.py | 25 +++++++++++++++++++++++++ vm/src/obj/objbyteinner.rs | 38 ++++++++++++++++++++++++++++++++++++++ vm/src/obj/objbytes.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index 755707a766..dc9a439bcd 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -207,3 +207,28 @@ assert b"abcde".startswith(b"cd", 2) assert not b"abcde".startswith(b"cd", 1, 4) assert b"abcde".startswith((b"a", b"bc")) + + +# index find +assert b"abcd".index(b"cd") == 2 +assert b"abcd".index(b"cd", 0) == 2 +assert b"abcd".index(b"cd", 1) == 2 +assert b"abcd".index(99) == 2 +with assertRaises(ValueError): + b"abcde".index(b"c", 3, 1) +with assertRaises(ValueError): + b"abcd".index(b"cdaaaaa") +with assertRaises(ValueError): + b"abcd".index(b"b", 3, 4) +with assertRaises(ValueError): + b"abcd".index(1) + + +assert b"abcd".find(b"cd") == 2 +assert b"abcd".find(b"cd", 0) == 2 +assert b"abcd".find(b"cd", 1) == 2 +assert b"abcde".find(b"c", 3, 1) == -1 +assert b"abcd".find(b"cdaaaaa") == -1 +assert b"abcd".find(b"b", 3, 4) == -1 +assert b"abcd".find(1) == -1 +assert b"abcd".find(99) == 2 diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index cc4111a8b3..5c5e97c258 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -552,6 +552,44 @@ impl PyByteInner { Ok(vm.new_bool(suff.as_slice() == &self.elements.do_slice(range)[offset])) } + + pub fn find( + &self, + sub: PyObjectRef, + start: OptionalArg, + end: OptionalArg, + vm: &VirtualMachine, + ) -> Result { + let sub = match try_as_bytes_like(&sub) { + Some(value) => value, + None => match_class!(sub, + i @ PyInt => + vec![i.as_bigint().byte_or(vm)?], + obj => {return Err(vm.new_type_error(format!("a bytes-like object is required, not {}", obj)));}), + }; + + let range = self.elements.get_slice_range( + &is_valid_slice_arg(start, vm)?, + &is_valid_slice_arg(end, vm)?, + ); + + // not allowed for this method + if range.end < range.start { + return Ok(-1isize); + } + + let start = range.start; + + let slice = &self.elements[range]; + for (n, _) in slice.iter().enumerate() { + if n + sub.len() <= slice.len() + && &slice[n..n + sub.len()] == sub.as_slice() + { + return Ok((start+n) as isize); + } + } + Ok(-1isize) + } } pub fn try_as_byte(obj: &PyObjectRef) -> Option> { diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 0c6e5c4b90..a53b371d09 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -280,6 +280,32 @@ impl PyBytesRef { ) -> PyResult { self.inner.startsendswith(suffix, start, end, false, vm) } + + #[pymethod(name = "find")] + fn find( + self, + sub: PyObjectRef, + start: OptionalArg, + end: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult { + Ok(vm.new_int(self.inner.find(sub, start, end, vm)?)) + } + + #[pymethod(name = "index")] + fn index( + self, + sub: PyObjectRef, + start: OptionalArg, + end: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult { + let res = self.inner.find(sub, start, end, vm)?; + if res == -1 { + return Err(vm.new_value_error("substring not found".to_string())); + } + Ok(vm.new_int(res)) + } } #[derive(Debug)] From ced6f15a59155f42f2278c69dd652fc4c0b2f2be Mon Sep 17 00:00:00 2001 From: Jimmy Girardet Date: Wed, 17 Apr 2019 16:16:43 +0200 Subject: [PATCH 08/26] add bytes.makertans --- tests/snippets/bytes.py | 9 +++++++++ vm/src/obj/objbyteinner.rs | 24 ++++++++++++++++++++++++ vm/src/obj/objbytes.rs | 2 ++ 3 files changed, 35 insertions(+) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index dc9a439bcd..ebd3eb877a 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -232,3 +232,12 @@ assert b"abcd".find(b"b", 3, 4) == -1 assert b"abcd".find(1) == -1 assert b"abcd".find(99) == 2 + + +# make trans +# fmt: off +assert ( + bytes.maketrans(memoryview(b"abc"), bytearray(b"zzz")) + == bytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 122, 122, 122, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255]) +) +# fmt: on diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index 5c5e97c258..c5fda16c80 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -590,6 +590,30 @@ impl PyByteInner { } Ok(-1isize) } + + pub fn maketrans(from: PyObjectRef, to: PyObjectRef, vm :&VirtualMachine) -> PyResult{ + let mut res = vec![]; + + let from = match try_as_bytes_like(&from) { + Some(value) => value, + None => {return Err(vm.new_type_error(format!("a bytes-like object is required, not {}", from)));}, + }; + + let to = match try_as_bytes_like(&to) { + Some(value) => value, + None => {return Err(vm.new_type_error(format!("a bytes-like object is required, not {}", to)));}, + }; + + for i in 0..=255 { + res.push(if let Some(position) = from.iter().position(|&x| x ==i) { + to[position] + } else { + i + }); + } + + Ok(vm.ctx.new_bytes(res)) + } } pub fn try_as_byte(obj: &PyObjectRef) -> Option> { diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index a53b371d09..da675547b3 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -62,6 +62,8 @@ pub fn init(context: &PyContext) { let bytes_type = &context.bytes_type; extend_class!(context, bytes_type, { "fromhex" => context.new_rustfunc(PyBytesRef::fromhex), + "maketrans" => context.new_rustfunc(PyByteInner::maketrans), + }); let bytesiterator_type = &context.bytesiterator_type; extend_class!(context, bytesiterator_type, { From 0b283102a6c69022a2d79219c953a3d098456658 Mon Sep 17 00:00:00 2001 From: Jimmy Girardet Date: Wed, 17 Apr 2019 17:52:45 +0200 Subject: [PATCH 09/26] add translate --- tests/snippets/bytes.py | 5 +++++ vm/src/obj/objbyteinner.rs | 30 ++++++++++++++++++++++++++++++ vm/src/obj/objbytes.rs | 10 ++++++++++ 3 files changed, 45 insertions(+) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index ebd3eb877a..602ff9000c 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -241,3 +241,8 @@ == bytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 122, 122, 122, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255]) ) # fmt: on + +# translate +assert b'hjhtuyjyujuyj'.translate(bytes.maketrans(b"hj", b"ab"), b"h") == b'btuybyubuyb' +assert b'hjhtuyjyujuyj'.translate(bytes.maketrans(b"hj", b"ab"), b"a") == b'abatuybyubuyb' +assert b'hjhtuyjyujuyj'.translate(bytes.maketrans(b"hj", b"ab")) == b'abatuybyubuyb' diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index c5fda16c80..aa35ffaa0f 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -612,6 +612,36 @@ impl PyByteInner { }); } + Ok(vm.ctx.new_bytes(res)) + } + pub fn translate(&self, table: PyObjectRef, delete: OptionalArg, vm :&VirtualMachine) -> PyResult{ + let table = match try_as_bytes_like(&table) { + Some(value) => value, + None => {return Err(vm.new_type_error(format!("a bytes-like object is required, not {}", table)));}, + }; + + if table.len() != 256 { + return Err(vm.new_value_error("translation table must be 256 characters long".to_string())); + } + + + + let delete = if let OptionalArg::Present(value) = delete { + match try_as_bytes_like(&value) { + Some(value) => value, + None => {return Err(vm.new_type_error(format!("a bytes-like object is required, not {}", value)));} + } + } else {vec![]}; + + + let mut res = vec![]; + + for i in self.elements.iter() { + if !delete.contains(&i) { + res.push(table[*i as usize]); + } + } + Ok(vm.ctx.new_bytes(res)) } } diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index da675547b3..90f7adc075 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -308,6 +308,16 @@ impl PyBytesRef { } Ok(vm.new_int(res)) } + + #[pymethod(name = "translate")] + fn translate( + self, + table: PyObjectRef, + delete: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult { + self.inner.translate(table, delete, vm) + } } #[derive(Debug)] From 8693adcd611f218189de5ef44dbe88dfcfbdc24c Mon Sep 17 00:00:00 2001 From: jgirardet Date: Thu, 18 Apr 2019 08:41:44 +0200 Subject: [PATCH 10/26] handle None case for bytes.translate --- tests/snippets/bytes.py | 2 ++ vm/src/obj/objbyteinner.rs | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index 602ff9000c..a4a32273ae 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -246,3 +246,5 @@ assert b'hjhtuyjyujuyj'.translate(bytes.maketrans(b"hj", b"ab"), b"h") == b'btuybyubuyb' assert b'hjhtuyjyujuyj'.translate(bytes.maketrans(b"hj", b"ab"), b"a") == b'abatuybyubuyb' assert b'hjhtuyjyujuyj'.translate(bytes.maketrans(b"hj", b"ab")) == b'abatuybyubuyb' +assert b'hjhtuyfjtyhuhjuyj'.translate(None, b"ht") == b'juyfjyujuyj' + diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index aa35ffaa0f..33a16a6c34 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -617,7 +617,13 @@ impl PyByteInner { pub fn translate(&self, table: PyObjectRef, delete: OptionalArg, vm :&VirtualMachine) -> PyResult{ let table = match try_as_bytes_like(&table) { Some(value) => value, - None => {return Err(vm.new_type_error(format!("a bytes-like object is required, not {}", table)));}, + None => { + match_class!(table, + + _n @ PyNone => (0..=255).collect::>(), + obj => {return Err(vm.new_type_error(format!("a bytes-like object is required, not {}", obj)));}, + ) + } }; if table.len() != 256 { From 046bd9b6d84d1112faaaaa5d6d1e8d6e8898f0e4 Mon Sep 17 00:00:00 2001 From: jgirardet Date: Thu, 18 Apr 2019 14:01:12 +0200 Subject: [PATCH 11/26] reactor bytes.center --- vm/src/obj/objbyteinner.rs | 122 +++++++++++++++++++++++-------------- 1 file changed, 77 insertions(+), 45 deletions(-) diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index 33a16a6c34..f666affc5f 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -12,8 +12,8 @@ use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; use super::objint; +use super::objsequence::{is_valid_slice_arg, PySliceableSequence}; use super::objtype; -use super::objsequence::{PySliceableSequence, is_valid_slice_arg}; use crate::obj::objint::PyInt; use num_integer::Integer; use num_traits::ToPrimitive; @@ -364,7 +364,13 @@ impl PyByteInner { .collect::>()) } - pub fn center(&self, width_a: PyObjectRef, fillbyte: OptionalArg, vm: &VirtualMachine) -> PyResult> { + fn get_center_args( + &self, + width_a: PyObjectRef, + fillbyte: OptionalArg, + fn_name: String, + vm: &VirtualMachine, + ) -> PyResult<(u8, usize)> { let fillbyte = if let OptionalArg::Present(v) = fillbyte { match try_as_byte(&v) { Some(x) => { @@ -372,39 +378,52 @@ impl PyByteInner { x[0] } else { return Err(vm.new_type_error(format!( - "center() argument 2 must be a byte string of length 1, not {}", - &v + "{}() argument 2 must be a byte string of length 1, not {}", + &fn_name, &v ))); } } None => { return Err(vm.new_type_error(format!( - "center() argument 2 must be a byte string of length 1, not {}", - &v + "{}() argument 2 must be a byte string of length 1, not {}", + &fn_name, &v ))); } } } else { - 32 // default is space + b' ' // default is space }; - let width_b = match_class!(width_a, + let width_b = match_class!(width_a, i @PyInt => i, obj => {return Err(vm.new_type_error(format!("{} cannot be interpreted as an integer", obj)));} ); - // <0 = no change let width = if let Some(x) = width_b.as_bigint().to_usize() { - x - } else {return Ok(self.elements.clone());}; - + if x <= self.len() { + 0 + } else { + x + } + } else { + 0 + }; + + let diff: usize = if width != 0 { width - self.len() } else { 0 }; + + Ok((fillbyte, diff)) + } + + pub fn center( + &self, + width_a: PyObjectRef, + fillbyte: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult> { + let fn_name = "center".to_string(); + let (fillbyte, diff) = self.get_center_args(width_a, fillbyte, fn_name, vm)?; - // adjust right et left side - if width <= self.len() { - return Ok(self.elements.clone()); - } - let diff: usize = width - self.len(); let mut ln: usize = diff / 2; let mut rn: usize = ln; @@ -434,8 +453,7 @@ impl PyByteInner { let sub = match try_as_bytes_like(&sub) { Some(value) => value, None => match_class!(sub, - i @ PyInt => - vec![i.as_bigint().byte_or(vm)?], + i @ PyInt => vec![i.as_bigint().byte_or(vm)?], obj => {return Err(vm.new_type_error(format!("argument should be integer or bytes-like object, not {}", obj)));}), }; let start = if let OptionalArg::Present(st) = start { @@ -563,8 +581,7 @@ impl PyByteInner { let sub = match try_as_bytes_like(&sub) { Some(value) => value, None => match_class!(sub, - i @ PyInt => - vec![i.as_bigint().byte_or(vm)?], + i @ PyInt => vec![i.as_bigint().byte_or(vm)?], obj => {return Err(vm.new_type_error(format!("a bytes-like object is required, not {}", obj)));}), }; @@ -582,63 +599,78 @@ impl PyByteInner { let slice = &self.elements[range]; for (n, _) in slice.iter().enumerate() { - if n + sub.len() <= slice.len() - && &slice[n..n + sub.len()] == sub.as_slice() - { - return Ok((start+n) as isize); + if n + sub.len() <= slice.len() && &slice[n..n + sub.len()] == sub.as_slice() { + return Ok((start + n) as isize); } } Ok(-1isize) } - pub fn maketrans(from: PyObjectRef, to: PyObjectRef, vm :&VirtualMachine) -> PyResult{ - let mut res = vec![]; + pub fn maketrans(from: PyObjectRef, to: PyObjectRef, vm: &VirtualMachine) -> PyResult { + let mut res = vec![]; let from = match try_as_bytes_like(&from) { Some(value) => value, - None => {return Err(vm.new_type_error(format!("a bytes-like object is required, not {}", from)));}, + None => { + return Err( + vm.new_type_error(format!("a bytes-like object is required, not {}", from)) + ); + } }; let to = match try_as_bytes_like(&to) { Some(value) => value, - None => {return Err(vm.new_type_error(format!("a bytes-like object is required, not {}", to)));}, + None => { + return Err( + vm.new_type_error(format!("a bytes-like object is required, not {}", to)) + ); + } }; for i in 0..=255 { - res.push(if let Some(position) = from.iter().position(|&x| x ==i) { + res.push(if let Some(position) = from.iter().position(|&x| x == i) { to[position] - } else { + } else { i }); } Ok(vm.ctx.new_bytes(res)) } - pub fn translate(&self, table: PyObjectRef, delete: OptionalArg, vm :&VirtualMachine) -> PyResult{ + pub fn translate( + &self, + table: PyObjectRef, + delete: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult { let table = match try_as_bytes_like(&table) { Some(value) => value, - None => { - match_class!(table, + None => match_class!(table, - _n @ PyNone => (0..=255).collect::>(), - obj => {return Err(vm.new_type_error(format!("a bytes-like object is required, not {}", obj)));}, - ) - } + _n @ PyNone => (0..=255).collect::>(), + obj => {return Err(vm.new_type_error(format!("a bytes-like object is required, not {}", obj)));}, + ), }; if table.len() != 256 { - return Err(vm.new_value_error("translation table must be 256 characters long".to_string())); + return Err( + vm.new_value_error("translation table must be 256 characters long".to_string()) + ); } - - let delete = if let OptionalArg::Present(value) = delete { - match try_as_bytes_like(&value) { + match try_as_bytes_like(&value) { Some(value) => value, - None => {return Err(vm.new_type_error(format!("a bytes-like object is required, not {}", value)));} + None => { + return Err(vm.new_type_error(format!( + "a bytes-like object is required, not {}", + value + ))); + } } - } else {vec![]}; - + } else { + vec![] + }; let mut res = vec![]; From 84d61a9212be2f23c74c7f5138f23c66d7ec3474 Mon Sep 17 00:00:00 2001 From: jgirardet Date: Thu, 18 Apr 2019 14:56:47 +0200 Subject: [PATCH 12/26] bytes.new with kwargs --- tests/snippets/bytes.py | 23 +++++++++++++++-------- vm/src/obj/objbyteinner.rs | 24 +++++++++++++++--------- vm/src/obj/objbytes.rs | 7 +++---- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index a4a32273ae..a36a9b2fde 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -6,12 +6,18 @@ assert bytes(range(4)) assert bytes(3) assert b"bla" -assert bytes("bla", "utf8") +assert bytes("bla", "utf8") == bytes("bla", encoding="utf-8") == b"bla" with assertRaises(TypeError): bytes("bla") -assert b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" == bytes(range(0,256)) -assert b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff' == bytes(range(0,256)) +assert ( + b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + == bytes(range(0, 256)) +) +assert ( + b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + == bytes(range(0, 256)) +) assert b"omkmok\Xaa" == bytes([111, 109, 107, 109, 111, 107, 92, 88, 97, 97]) @@ -243,8 +249,9 @@ # fmt: on # translate -assert b'hjhtuyjyujuyj'.translate(bytes.maketrans(b"hj", b"ab"), b"h") == b'btuybyubuyb' -assert b'hjhtuyjyujuyj'.translate(bytes.maketrans(b"hj", b"ab"), b"a") == b'abatuybyubuyb' -assert b'hjhtuyjyujuyj'.translate(bytes.maketrans(b"hj", b"ab")) == b'abatuybyubuyb' -assert b'hjhtuyfjtyhuhjuyj'.translate(None, b"ht") == b'juyfjyujuyj' - +assert b"hjhtuyjyujuyj".translate(bytes.maketrans(b"hj", b"ab"), b"h") == b"btuybyubuyb" +assert ( + b"hjhtuyjyujuyj".translate(bytes.maketrans(b"hj", b"ab"), b"a") == b"abatuybyubuyb" +) +assert b"hjhtuyjyujuyj".translate(bytes.maketrans(b"hj", b"ab")) == b"abatuybyubuyb" +assert b"hjhtuyfjtyhuhjuyj".translate(None, b"ht") == b"juyfjyujuyj" diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index f666affc5f..e65fbb9829 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -29,15 +29,19 @@ pub struct PyByteInner { pub elements: Vec, } -impl PyByteInner { - pub fn new( - val_option: OptionalArg, - enc_option: OptionalArg, - vm: &VirtualMachine, - ) -> PyResult { +#[derive(FromArgs)] +pub struct BytesNewOptions { + #[pyarg(positional_only, optional = true)] + val_option: OptionalArg, + #[pyarg(positional_or_keyword, optional = true)] + encoding: OptionalArg, +} + +impl BytesNewOptions { + pub fn get_value(self, vm: &VirtualMachine) -> PyResult { // First handle bytes(string, encoding[, errors]) - if let OptionalArg::Present(enc) = enc_option { - if let OptionalArg::Present(eval) = val_option { + if let OptionalArg::Present(enc) = self.encoding { + if let OptionalArg::Present(eval) = self.val_option { if let Ok(input) = eval.downcast::() { if let Ok(encoding) = enc.clone().downcast::() { if &encoding.value.to_lowercase() == "utf8" @@ -66,7 +70,7 @@ impl PyByteInner { } // Only one argument } else { - let value = if let OptionalArg::Present(ival) = val_option { + let value = if let OptionalArg::Present(ival) = self.val_option { match_class!(ival.clone(), i @ PyInt => { let size = objint::get_value(&i.into_object()).to_usize().unwrap(); @@ -98,7 +102,9 @@ impl PyByteInner { } } } +} +impl PyByteInner { pub fn repr(&self) -> PyResult { let mut res = String::with_capacity(self.elements.len()); for i in self.elements.iter() { diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 90f7adc075..85881a887b 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -7,7 +7,7 @@ use std::ops::Deref; use crate::function::OptionalArg; use crate::pyobject::{PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue}; -use super::objbyteinner::PyByteInner; +use super::objbyteinner::{BytesNewOptions, PyByteInner}; use super::objiter; use super::objslice::PySlice; use super::objtype::PyClassRef; @@ -77,12 +77,11 @@ impl PyBytesRef { #[pymethod(name = "__new__")] fn bytes_new( cls: PyClassRef, - val_option: OptionalArg, - enc_option: OptionalArg, + options: BytesNewOptions, vm: &VirtualMachine, ) -> PyResult { PyBytes { - inner: PyByteInner::new(val_option, enc_option, vm)?, + inner: options.get_value(vm)?, } .into_ref_with_type(vm, cls) } From 473ae241996a6abbc63036d666481bdfab1c7c37 Mon Sep 17 00:00:00 2001 From: jgirardet Date: Thu, 18 Apr 2019 17:31:58 +0200 Subject: [PATCH 13/26] use PyStringRef in new --- tests/snippets/bytes.py | 2 ++ vm/src/obj/objbyteinner.rs | 35 ++++++++++++++--------------------- vm/src/obj/objbytes.rs | 4 ++-- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index a36a9b2fde..2e6c658be0 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -9,6 +9,8 @@ assert bytes("bla", "utf8") == bytes("bla", encoding="utf-8") == b"bla" with assertRaises(TypeError): bytes("bla") +with assertRaises(TypeError): + bytes("bla", encoding = b"jilj") assert ( b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index e65fbb9829..ffd26d4835 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -7,7 +7,7 @@ use crate::vm::VirtualMachine; use crate::pyobject::{PyResult, TypeProtocol}; -use crate::obj::objstr::PyString; +use crate::obj::objstr::{PyString, PyStringRef}; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; @@ -30,37 +30,30 @@ pub struct PyByteInner { } #[derive(FromArgs)] -pub struct BytesNewOptions { +pub struct ByteInnerNewOptions { #[pyarg(positional_only, optional = true)] val_option: OptionalArg, #[pyarg(positional_or_keyword, optional = true)] - encoding: OptionalArg, + encoding: OptionalArg, } -impl BytesNewOptions { +impl ByteInnerNewOptions { pub fn get_value(self, vm: &VirtualMachine) -> PyResult { // First handle bytes(string, encoding[, errors]) if let OptionalArg::Present(enc) = self.encoding { if let OptionalArg::Present(eval) = self.val_option { if let Ok(input) = eval.downcast::() { - if let Ok(encoding) = enc.clone().downcast::() { - if &encoding.value.to_lowercase() == "utf8" - || &encoding.value.to_lowercase() == "utf-8" - // TODO: different encoding - { - return Ok(PyByteInner { - elements: input.value.as_bytes().to_vec(), - }); - } else { - return Err( - vm.new_value_error(format!("unknown encoding: {}", encoding.value)), //should be lookup error - ); - } + let encoding = enc.as_str(); + if encoding.to_lowercase() == "utf8" || encoding.to_lowercase() == "utf-8" + // TODO: different encoding + { + return Ok(PyByteInner { + elements: input.value.as_bytes().to_vec(), + }); } else { - return Err(vm.new_type_error(format!( - "bytes() argument 2 must be str, not {}", - enc.class().name - ))); + return Err( + vm.new_value_error(format!("unknown encoding: {}", encoding)), //should be lookup error + ); } } else { return Err(vm.new_type_error("encoding without a string argument".to_string())); diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 85881a887b..0607327506 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -7,7 +7,7 @@ use std::ops::Deref; use crate::function::OptionalArg; use crate::pyobject::{PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue}; -use super::objbyteinner::{BytesNewOptions, PyByteInner}; +use super::objbyteinner::{ByteInnerNewOptions, PyByteInner}; use super::objiter; use super::objslice::PySlice; use super::objtype::PyClassRef; @@ -77,7 +77,7 @@ impl PyBytesRef { #[pymethod(name = "__new__")] fn bytes_new( cls: PyClassRef, - options: BytesNewOptions, + options: ByteInnerNewOptions, vm: &VirtualMachine, ) -> PyResult { PyBytes { From 44d58f63abc3c744aa23a7a988e0916a4e540f37 Mon Sep 17 00:00:00 2001 From: jgirardet Date: Thu, 18 Apr 2019 19:03:08 +0200 Subject: [PATCH 14/26] refactor contains --- vm/src/obj/objbyteinner.rs | 27 ++++++++++++++++----------- vm/src/obj/objbytes.rs | 5 +---- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index ffd26d4835..8ca0407769 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -177,11 +177,20 @@ impl PyByteInner { elements } - pub fn contains_bytes(&self, other: &PyByteInner, vm: &VirtualMachine) -> PyResult { + pub fn contains(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { + match try_as_bytes_like(&needle) { + Some(value) => self.contains_bytes(&value, vm), + None => match_class!(needle, + i @ PyInt => self.contains_int(&i, vm), + obj => {Err(vm.new_type_error(format!("a bytes-like object is required, not {}", obj)))}), + } + } + + fn contains_bytes(&self, other: &[u8], vm: &VirtualMachine) -> PyResult { for (n, i) in self.elements.iter().enumerate() { if n + other.len() <= self.len() - && *i == other.elements[0] - && &self.elements[n..n + other.len()] == other.elements.as_slice() + && *i == other[0] + && &self.elements[n..n + other.len()] == other { return Ok(vm.new_bool(true)); } @@ -189,15 +198,11 @@ impl PyByteInner { Ok(vm.new_bool(false)) } - pub fn contains_int(&self, int: &PyInt, vm: &VirtualMachine) -> PyResult { - if let Some(int) = int.as_bigint().to_u8() { - if self.elements.contains(&int) { - Ok(vm.new_bool(true)) - } else { - Ok(vm.new_bool(false)) - } + fn contains_int(&self, int: &PyInt, vm: &VirtualMachine) -> PyResult { + if self.elements.contains(&int.as_bigint().byte_or(vm)?) { + Ok(vm.new_bool(true)) } else { - Err(vm.new_value_error("byte must be in range(0, 256)".to_string())) + Ok(vm.new_bool(false)) } } diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 0607327506..00a3987055 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -149,10 +149,7 @@ impl PyBytesRef { #[pymethod(name = "__contains__")] fn contains(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { - match_class!(needle, - bytes @ PyBytes => self.inner.contains_bytes(&bytes.inner, vm), - int @ PyInt => self.inner.contains_int(&int, vm), - obj => Err(vm.new_type_error(format!("a bytes-like object is required, not {}", obj)))) + self.inner.contains(needle, vm) } #[pymethod(name = "__getitem__")] From 4a4d163626e774cf590806c70853a7e5d8b1319b Mon Sep 17 00:00:00 2001 From: jgirardet Date: Thu, 18 Apr 2019 19:10:37 +0200 Subject: [PATCH 15/26] refactor getitem --- vm/src/obj/objbyteinner.rs | 18 ++++++++++++------ vm/src/obj/objbytes.rs | 8 ++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index 8ca0407769..7edfa7dde9 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -1,3 +1,4 @@ +use crate::obj::objslice::PySlice; use crate::pyobject::{PyIterable, PyObjectRef}; use num_bigint::BigInt; @@ -206,18 +207,23 @@ impl PyByteInner { } } - pub fn getitem_int(&self, int: &PyInt, vm: &VirtualMachine) -> PyResult { - if let Some(idx) = self.elements.get_pos(int.as_bigint().to_i32().unwrap()) { + pub fn getitem(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { + match_class!(needle, + int @ PyInt => //self.inner.getitem_int(&int, vm), + { + if let Some(idx) = self.elements.get_pos(int.as_bigint().to_i32().unwrap()) { Ok(vm.new_int(self.elements[idx])) } else { Err(vm.new_index_error("index out of range".to_string())) } - } - - pub fn getitem_slice(&self, slice: &PyObjectRef, vm: &VirtualMachine) -> PyResult { + }, + slice @ PySlice => //self.inner.getitem_slice(slice.as_object(), vm), + { Ok(vm .ctx - .new_bytes(self.elements.get_slice_items(vm, slice).unwrap())) + .new_bytes(self.elements.get_slice_items(vm, slice.as_object())?)) + }, + obj => Err(vm.new_type_error(format!("byte indices must be integers or slices, not {}", obj)))) } pub fn isalnum(&self, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 00a3987055..46bd346241 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -1,4 +1,3 @@ -use crate::obj::objint::PyInt; use crate::obj::objstr::PyString; use crate::vm::VirtualMachine; use core::cell::Cell; @@ -9,7 +8,7 @@ use crate::pyobject::{PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, Py use super::objbyteinner::{ByteInnerNewOptions, PyByteInner}; use super::objiter; -use super::objslice::PySlice; + use super::objtype::PyClassRef; /// "bytes(iterable_of_ints) -> bytes\n\ /// bytes(string, encoding[, errors]) -> bytes\n\ @@ -154,10 +153,7 @@ impl PyBytesRef { #[pymethod(name = "__getitem__")] fn getitem(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { - match_class!(needle, - int @ PyInt => self.inner.getitem_int(&int, vm), - slice @ PySlice => self.inner.getitem_slice(slice.as_object(), vm), - obj => Err(vm.new_type_error(format!("byte indices must be integers or slices, not {}", obj)))) + self.inner.getitem(needle, vm) } #[pymethod(name = "isalnum")] From ba67f3c264ded2514eed37373ca118b1f773ed7d Mon Sep 17 00:00:00 2001 From: jgirardet Date: Thu, 18 Apr 2019 19:24:10 +0200 Subject: [PATCH 16/26] refactore fromhex --- tests/snippets/bytes.py | 3 ++- vm/src/obj/objbyteinner.rs | 2 +- vm/src/obj/objbytes.rs | 13 +++---------- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index 2e6c658be0..e3ddcd51d7 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -142,7 +142,8 @@ bytes.fromhex("6Z2") except ValueError as e: str(e) == "non-hexadecimal number found in fromhex() arg at position 1" - +with assertRaises(TypeError): + bytes.fromhex(b'hhjjk') # center assert [b"koki".center(i, b"|") for i in range(3, 10)] == [ b"koki", diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index 7edfa7dde9..b3e51084a9 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -342,7 +342,7 @@ impl PyByteInner { Ok(vm.ctx.new_str(bla)) } - pub fn fromhex(string: String, vm: &VirtualMachine) -> Result, PyObjectRef> { + pub fn fromhex(string: &str, vm: &VirtualMachine) -> PyResult> { // first check for invalid character for (i, c) in string.char_indices() { if !c.is_digit(16) && !c.is_whitespace() { diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 46bd346241..773675c58a 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -1,4 +1,4 @@ -use crate::obj::objstr::PyString; +use crate::obj::objstr::PyStringRef; use crate::vm::VirtualMachine; use core::cell::Cell; use std::ops::Deref; @@ -216,15 +216,8 @@ impl PyBytesRef { self.inner.hex(vm) } - // #[pymethod(name = "fromhex")] - fn fromhex(string: PyObjectRef, vm: &VirtualMachine) -> PyResult { - match_class!(string, - s @ PyString => { - match PyByteInner::fromhex(s.to_string(), vm) { - Ok(x) => Ok(vm.ctx.new_bytes(x)), - Err(y) => Err(y)}}, - obj => Err(vm.new_type_error(format!("fromhex() argument must be str, not {}", obj ))) - ) + fn fromhex(string: PyStringRef, vm: &VirtualMachine) -> PyResult { + Ok(vm.ctx.new_bytes(PyByteInner::fromhex(string.as_str(), vm)?)) } #[pymethod(name = "center")] From 8bcdbbf21b0c782274fe80c4e48f4c25aab726f3 Mon Sep 17 00:00:00 2001 From: jgirardet Date: Thu, 18 Apr 2019 19:52:17 +0200 Subject: [PATCH 17/26] use PyInt Ref, add ljust, rjust --- tests/snippets/bytes.py | 67 ++++++++++++++++++++++++++++++++++++-- vm/src/obj/objbyteinner.rs | 47 +++++++++++++++++++++----- vm/src/obj/objbytes.rs | 23 ++++++++++++- 3 files changed, 124 insertions(+), 13 deletions(-) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index e3ddcd51d7..ecb29612a0 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -10,7 +10,7 @@ with assertRaises(TypeError): bytes("bla") with assertRaises(TypeError): - bytes("bla", encoding = b"jilj") + bytes("bla", encoding=b"jilj") assert ( b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" @@ -143,7 +143,7 @@ except ValueError as e: str(e) == "non-hexadecimal number found in fromhex() arg at position 1" with assertRaises(TypeError): - bytes.fromhex(b'hhjjk') + bytes.fromhex(b"hhjjk") # center assert [b"koki".center(i, b"|") for i in range(3, 10)] == [ b"koki", @@ -170,9 +170,70 @@ b"b".center(2, "a") with assertRaises(TypeError): b"b".center(2, b"ba") -b"kok".center(5, bytearray(b"x")) +assert b"kok".center(5, bytearray(b"x")) == b"xkokx" b"kok".center(-5) + +# ljust +assert [b"koki".ljust(i, b"|") for i in range(3, 10)] == [ + b"koki", + b"koki", + b"koki|", + b"koki||", + b"koki|||", + b"koki||||", + b"koki|||||", +] +assert [b"kok".ljust(i, b"|") for i in range(2, 10)] == [ + b"kok", + b"kok", + b"kok|", + b"kok||", + b"kok|||", + b"kok||||", + b"kok|||||", + b"kok||||||", +] + +b"kok".ljust(4) == b"kok " # " test no arg" +with assertRaises(TypeError): + b"b".ljust(2, "a") +with assertRaises(TypeError): + b"b".ljust(2, b"ba") +assert b"kok".ljust(5, bytearray(b"x")) == b"kokxx" +assert b"kok".ljust(-5) == b"kok" + +# rjust +assert [b"koki".rjust(i, b"|") for i in range(3, 10)] == [ + b"koki", + b"koki", + b"|koki", + b"||koki", + b"|||koki", + b"||||koki", + b"|||||koki", +] +assert [b"kok".rjust(i, b"|") for i in range(2, 10)] == [ + b"kok", + b"kok", + b"|kok", + b"||kok", + b"|||kok", + b"||||kok", + b"|||||kok", + b"||||||kok", +] + + +b"kok".rjust(4) == b" kok" # " test no arg" +with assertRaises(TypeError): + b"b".rjust(2, "a") +with assertRaises(TypeError): + b"b".rjust(2, b"ba") +assert b"kok".rjust(5, bytearray(b"x")) == b"xxkok" +assert b"kok".rjust(-5) == b"kok" + + # count assert b"azeazerazeazopia".count(b"aze") == 3 assert b"azeazerazeazopia".count(b"az") == 4 diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index b3e51084a9..114742e8c9 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -1,3 +1,4 @@ +use crate::obj::objint::PyIntRef; use crate::obj::objslice::PySlice; use crate::pyobject::{PyIterable, PyObjectRef}; use num_bigint::BigInt; @@ -376,7 +377,7 @@ impl PyByteInner { fn get_center_args( &self, - width_a: PyObjectRef, + width: PyIntRef, fillbyte: OptionalArg, fn_name: String, vm: &VirtualMachine, @@ -404,13 +405,8 @@ impl PyByteInner { b' ' // default is space }; - let width_b = match_class!(width_a, - i @PyInt => i, - obj => {return Err(vm.new_type_error(format!("{} cannot be interpreted as an integer", obj)));} - ); - // <0 = no change - let width = if let Some(x) = width_b.as_bigint().to_usize() { + let width = if let Some(x) = width.as_bigint().to_usize() { if x <= self.len() { 0 } else { @@ -427,12 +423,12 @@ impl PyByteInner { pub fn center( &self, - width_a: PyObjectRef, + width: PyIntRef, fillbyte: OptionalArg, vm: &VirtualMachine, ) -> PyResult> { let fn_name = "center".to_string(); - let (fillbyte, diff) = self.get_center_args(width_a, fillbyte, fn_name, vm)?; + let (fillbyte, diff) = self.get_center_args(width, fillbyte, fn_name, vm)?; let mut ln: usize = diff / 2; let mut rn: usize = ln; @@ -453,6 +449,39 @@ impl PyByteInner { Ok(res) } + pub fn ljust( + &self, + width: PyIntRef, + fillbyte: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult> { + let fn_name = "ljust".to_string(); + let (fillbyte, diff) = self.get_center_args(width, fillbyte, fn_name, vm)?; + + // merge all + let mut res = vec![]; + res.extend_from_slice(&self.elements[..]); + res.extend_from_slice(&vec![fillbyte; diff][..]); + + Ok(res) + } + + pub fn rjust( + &self, + width: PyIntRef, + fillbyte: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult> { + let fn_name = "ljust".to_string(); + let (fillbyte, diff) = self.get_center_args(width, fillbyte, fn_name, vm)?; + + // merge all + let mut res = vec![fillbyte; diff]; + res.extend_from_slice(&self.elements[..]); + + Ok(res) + } + pub fn count( &self, sub: PyObjectRef, diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 773675c58a..f9d17c4e00 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -1,3 +1,4 @@ +use crate::obj::objint::PyIntRef; use crate::obj::objstr::PyStringRef; use crate::vm::VirtualMachine; use core::cell::Cell; @@ -223,13 +224,33 @@ impl PyBytesRef { #[pymethod(name = "center")] fn center( self, - width: PyObjectRef, + width: PyIntRef, fillbyte: OptionalArg, vm: &VirtualMachine, ) -> PyResult { Ok(vm.ctx.new_bytes(self.inner.center(width, fillbyte, vm)?)) } + #[pymethod(name = "ljust")] + fn ljust( + self, + width: PyIntRef, + fillbyte: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult { + Ok(vm.ctx.new_bytes(self.inner.ljust(width, fillbyte, vm)?)) + } + + #[pymethod(name = "rjust")] + fn rjust( + self, + width: PyIntRef, + fillbyte: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult { + Ok(vm.ctx.new_bytes(self.inner.rjust(width, fillbyte, vm)?)) + } + #[pymethod(name = "count")] fn count( self, From 1669d5280f20bfbf0043595edf1643812935105c Mon Sep 17 00:00:00 2001 From: jgirardet Date: Thu, 18 Apr 2019 20:41:12 +0200 Subject: [PATCH 18/26] refactor count --- vm/src/obj/objbyteinner.rs | 83 +++++++++++++++++++++++--------------- vm/src/obj/objbytes.rs | 12 ++---- 2 files changed, 53 insertions(+), 42 deletions(-) diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index 114742e8c9..32c2f4240e 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -1,6 +1,7 @@ use crate::obj::objint::PyIntRef; use crate::obj::objslice::PySlice; use crate::pyobject::{PyIterable, PyObjectRef}; +use core::ops::Range; use num_bigint::BigInt; use crate::function::OptionalArg; @@ -99,6 +100,53 @@ impl ByteInnerNewOptions { } } +#[derive(FromArgs)] +pub struct ByteInnerFindOptions { + #[pyarg(positional_only, optional = false)] + sub: PyObjectRef, + #[pyarg(positional_only, optional = true)] + start: OptionalArg, + #[pyarg(positional_only, optional = true)] + end: OptionalArg, +} + +impl ByteInnerFindOptions { + pub fn get_value( + self, + elements: &[u8], + vm: &VirtualMachine, + ) -> PyResult<(Vec, Range)> { + let sub = match try_as_bytes_like(&self.sub.clone()) { + Some(value) => value, + None => match_class!(self.sub, + i @ PyInt => vec![i.as_bigint().byte_or(vm)?], + obj => {return Err(vm.new_type_error(format!("argument should be integer or bytes-like object, not {}", obj)));}), + }; + let start = if let OptionalArg::Present(st) = self.start { + match_class!(st, + i @ PyInt => {Some(i.as_bigint().clone())}, + _obj @ PyNone => None, + _=> {return Err(vm.new_type_error("slice indices must be integers or None or have an __index__ method".to_string()));} + ) + } else { + None + }; + let end = if let OptionalArg::Present(e) = self.end { + match_class!(e, + i @ PyInt => {Some(i.as_bigint().clone())}, + _obj @ PyNone => None, + _=> {return Err(vm.new_type_error("slice indices must be integers or None or have an __index__ method".to_string()));} + ) + } else { + None + }; + + let range = elements.to_vec().get_slice_range(&start, &end); + + Ok((sub, range)) + } +} + impl PyByteInner { pub fn repr(&self) -> PyResult { let mut res = String::with_capacity(self.elements.len()); @@ -482,39 +530,8 @@ impl PyByteInner { Ok(res) } - pub fn count( - &self, - sub: PyObjectRef, - start: OptionalArg, - end: OptionalArg, - vm: &VirtualMachine, - ) -> PyResult { - let sub = match try_as_bytes_like(&sub) { - Some(value) => value, - None => match_class!(sub, - i @ PyInt => vec![i.as_bigint().byte_or(vm)?], - obj => {return Err(vm.new_type_error(format!("argument should be integer or bytes-like object, not {}", obj)));}), - }; - let start = if let OptionalArg::Present(st) = start { - match_class!(st, - i @ PyInt => {Some(i.as_bigint().clone())}, - _obj @ PyNone => None, - _=> {return Err(vm.new_type_error("slice indices must be integers or None or have an __index__ method".to_string()));} - ) - } else { - None - }; - let end = if let OptionalArg::Present(e) = end { - match_class!(e, - i @ PyInt => {Some(i.as_bigint().clone())}, - _obj @ PyNone => None, - _=> {return Err(vm.new_type_error("slice indices must be integers or None or have an __index__ method".to_string()));} - ) - } else { - None - }; - - let range = self.elements.get_slice_range(&start, &end); + pub fn count(&self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult { + let (sub, range) = options.get_value(&self.elements, vm)?; if sub.is_empty() { return Ok(self.len() + 1); diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index f9d17c4e00..8e8393d129 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -7,7 +7,7 @@ use std::ops::Deref; use crate::function::OptionalArg; use crate::pyobject::{PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue}; -use super::objbyteinner::{ByteInnerNewOptions, PyByteInner}; +use super::objbyteinner::{ByteInnerFindOptions, ByteInnerNewOptions, PyByteInner}; use super::objiter; use super::objtype::PyClassRef; @@ -252,14 +252,8 @@ impl PyBytesRef { } #[pymethod(name = "count")] - fn count( - self, - sub: PyObjectRef, - start: OptionalArg, - end: OptionalArg, - vm: &VirtualMachine, - ) -> PyResult { - self.inner.count(sub, start, end, vm) + fn count(self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult { + self.inner.count(options, vm) } #[pymethod(name = "join")] From 56c5790745b892d731c2ebac75d4864e89f2aad3 Mon Sep 17 00:00:00 2001 From: jgirardet Date: Thu, 18 Apr 2019 21:00:33 +0200 Subject: [PATCH 19/26] reformat center, ljsut, rjust args --- tests/snippets/bytes.py | 25 ++++---- vm/src/obj/objbyteinner.rs | 119 +++++++++++++++++++------------------ vm/src/obj/objbytes.rs | 32 +++------- 3 files changed, 84 insertions(+), 92 deletions(-) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index ecb29612a0..0a3fb11abc 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -170,6 +170,8 @@ b"b".center(2, "a") with assertRaises(TypeError): b"b".center(2, b"ba") +with assertRaises(TypeError): + b"b".center(b"ba") assert b"kok".center(5, bytearray(b"x")) == b"xkokx" b"kok".center(-5) @@ -200,6 +202,8 @@ b"b".ljust(2, "a") with assertRaises(TypeError): b"b".ljust(2, b"ba") +with assertRaises(TypeError): + b"b".ljust(b"ba") assert b"kok".ljust(5, bytearray(b"x")) == b"kokxx" assert b"kok".ljust(-5) == b"kok" @@ -213,16 +217,15 @@ b"||||koki", b"|||||koki", ] -assert [b"kok".rjust(i, b"|") for i in range(2, 10)] == [ - b"kok", - b"kok", - b"|kok", - b"||kok", - b"|||kok", - b"||||kok", - b"|||||kok", - b"||||||kok", -] +assert [b"kok".rjust(i, b"|") for i in range(2, 10)] == [b'kok', + b'kok', + b'|kok', + b'||kok', + b'|||kok', + b'||||kok', + b'|||||kok', + b'||||||kok'] + b"kok".rjust(4) == b" kok" # " test no arg" @@ -230,6 +233,8 @@ b"b".rjust(2, "a") with assertRaises(TypeError): b"b".rjust(2, b"ba") +with assertRaises(TypeError): + b"b".rjust(b"ba") assert b"kok".rjust(5, bytearray(b"x")) == b"xxkok" assert b"kok".rjust(-5) == b"kok" diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index 32c2f4240e..b6e9ae49e1 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -116,7 +116,7 @@ impl ByteInnerFindOptions { elements: &[u8], vm: &VirtualMachine, ) -> PyResult<(Vec, Range)> { - let sub = match try_as_bytes_like(&self.sub.clone()) { + let sub = match try_as_bytes_like(&self.sub) { Some(value) => value, None => match_class!(self.sub, i @ PyInt => vec![i.as_bigint().byte_or(vm)?], @@ -147,6 +147,56 @@ impl ByteInnerFindOptions { } } +#[derive(FromArgs)] +pub struct ByteInnerPaddingOptions { + #[pyarg(positional_only, optional = false)] + width: PyIntRef, + #[pyarg(positional_only, optional = true)] + fillbyte: OptionalArg, +} + +impl ByteInnerPaddingOptions { + fn get_value(&self, fn_name: &str, len: usize, vm: &VirtualMachine) -> PyResult<(u8, usize)> { + let fillbyte = if let OptionalArg::Present(v) = &self.fillbyte { + match try_as_byte(&v) { + Some(x) => { + if x.len() == 1 { + x[0] + } else { + return Err(vm.new_type_error(format!( + "{}() argument 2 must be a byte string of length 1, not {}", + fn_name, &v + ))); + } + } + None => { + return Err(vm.new_type_error(format!( + "{}() argument 2 must be a byte string of length 1, not {}", + fn_name, &v + ))); + } + } + } else { + b' ' // default is space + }; + + // <0 = no change + let width = if let Some(x) = self.width.as_bigint().to_usize() { + if x <= len { + 0 + } else { + x + } + } else { + 0 + }; + + let diff: usize = if width != 0 { width - len } else { 0 }; + + Ok((fillbyte, diff)) + } +} + impl PyByteInner { pub fn repr(&self) -> PyResult { let mut res = String::with_capacity(self.elements.len()); @@ -423,60 +473,13 @@ impl PyByteInner { .collect::>()) } - fn get_center_args( - &self, - width: PyIntRef, - fillbyte: OptionalArg, - fn_name: String, - vm: &VirtualMachine, - ) -> PyResult<(u8, usize)> { - let fillbyte = if let OptionalArg::Present(v) = fillbyte { - match try_as_byte(&v) { - Some(x) => { - if x.len() == 1 { - x[0] - } else { - return Err(vm.new_type_error(format!( - "{}() argument 2 must be a byte string of length 1, not {}", - &fn_name, &v - ))); - } - } - None => { - return Err(vm.new_type_error(format!( - "{}() argument 2 must be a byte string of length 1, not {}", - &fn_name, &v - ))); - } - } - } else { - b' ' // default is space - }; - - // <0 = no change - let width = if let Some(x) = width.as_bigint().to_usize() { - if x <= self.len() { - 0 - } else { - x - } - } else { - 0 - }; - - let diff: usize = if width != 0 { width - self.len() } else { 0 }; - - Ok((fillbyte, diff)) - } - pub fn center( &self, - width: PyIntRef, - fillbyte: OptionalArg, + options: ByteInnerPaddingOptions, vm: &VirtualMachine, ) -> PyResult> { - let fn_name = "center".to_string(); - let (fillbyte, diff) = self.get_center_args(width, fillbyte, fn_name, vm)?; + // let fn_name = "center".to_string(); + let (fillbyte, diff) = options.get_value("center", self.len(), vm)?; let mut ln: usize = diff / 2; let mut rn: usize = ln; @@ -499,12 +502,11 @@ impl PyByteInner { pub fn ljust( &self, - width: PyIntRef, - fillbyte: OptionalArg, + options: ByteInnerPaddingOptions, vm: &VirtualMachine, ) -> PyResult> { - let fn_name = "ljust".to_string(); - let (fillbyte, diff) = self.get_center_args(width, fillbyte, fn_name, vm)?; + // let fn_name = "ljust".to_string(); + let (fillbyte, diff) = options.get_value("ljust", self.len(), vm)?; // merge all let mut res = vec![]; @@ -516,12 +518,11 @@ impl PyByteInner { pub fn rjust( &self, - width: PyIntRef, - fillbyte: OptionalArg, + options: ByteInnerPaddingOptions, vm: &VirtualMachine, ) -> PyResult> { - let fn_name = "ljust".to_string(); - let (fillbyte, diff) = self.get_center_args(width, fillbyte, fn_name, vm)?; + // let fn_name = "rjust".to_string(); + let (fillbyte, diff) = options.get_value("rjust", self.len(), vm)?; // merge all let mut res = vec![fillbyte; diff]; diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 8e8393d129..3c1b323c8a 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -1,4 +1,3 @@ -use crate::obj::objint::PyIntRef; use crate::obj::objstr::PyStringRef; use crate::vm::VirtualMachine; use core::cell::Cell; @@ -7,7 +6,9 @@ use std::ops::Deref; use crate::function::OptionalArg; use crate::pyobject::{PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue}; -use super::objbyteinner::{ByteInnerFindOptions, ByteInnerNewOptions, PyByteInner}; +use super::objbyteinner::{ + ByteInnerFindOptions, ByteInnerNewOptions, ByteInnerPaddingOptions, PyByteInner, +}; use super::objiter; use super::objtype::PyClassRef; @@ -222,33 +223,18 @@ impl PyBytesRef { } #[pymethod(name = "center")] - fn center( - self, - width: PyIntRef, - fillbyte: OptionalArg, - vm: &VirtualMachine, - ) -> PyResult { - Ok(vm.ctx.new_bytes(self.inner.center(width, fillbyte, vm)?)) + fn center(self, options: ByteInnerPaddingOptions, vm: &VirtualMachine) -> PyResult { + Ok(vm.ctx.new_bytes(self.inner.center(options, vm)?)) } #[pymethod(name = "ljust")] - fn ljust( - self, - width: PyIntRef, - fillbyte: OptionalArg, - vm: &VirtualMachine, - ) -> PyResult { - Ok(vm.ctx.new_bytes(self.inner.ljust(width, fillbyte, vm)?)) + fn ljust(self, options: ByteInnerPaddingOptions, vm: &VirtualMachine) -> PyResult { + Ok(vm.ctx.new_bytes(self.inner.ljust(options, vm)?)) } #[pymethod(name = "rjust")] - fn rjust( - self, - width: PyIntRef, - fillbyte: OptionalArg, - vm: &VirtualMachine, - ) -> PyResult { - Ok(vm.ctx.new_bytes(self.inner.rjust(width, fillbyte, vm)?)) + fn rjust(self, options: ByteInnerPaddingOptions, vm: &VirtualMachine) -> PyResult { + Ok(vm.ctx.new_bytes(self.inner.rjust(options, vm)?)) } #[pymethod(name = "count")] From 03997273ed630b97806ce804b3363d80cdcdb6d7 Mon Sep 17 00:00:00 2001 From: jgirardet Date: Thu, 18 Apr 2019 21:12:45 +0200 Subject: [PATCH 20/26] refactor index/find with ByteInnerOptions --- vm/src/obj/objbyteinner.rs | 21 ++------------------- vm/src/obj/objbytes.rs | 20 ++++---------------- 2 files changed, 6 insertions(+), 35 deletions(-) diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index b6e9ae49e1..6d5d491484 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -628,25 +628,8 @@ impl PyByteInner { Ok(vm.new_bool(suff.as_slice() == &self.elements.do_slice(range)[offset])) } - pub fn find( - &self, - sub: PyObjectRef, - start: OptionalArg, - end: OptionalArg, - vm: &VirtualMachine, - ) -> Result { - let sub = match try_as_bytes_like(&sub) { - Some(value) => value, - None => match_class!(sub, - i @ PyInt => vec![i.as_bigint().byte_or(vm)?], - obj => {return Err(vm.new_type_error(format!("a bytes-like object is required, not {}", obj)));}), - }; - - let range = self.elements.get_slice_range( - &is_valid_slice_arg(start, vm)?, - &is_valid_slice_arg(end, vm)?, - ); - + pub fn find(&self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult { + let (sub, range) = options.get_value(&self.elements, vm)?; // not allowed for this method if range.end < range.start { return Ok(-1isize); diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 3c1b323c8a..8ecc9dac14 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -270,25 +270,13 @@ impl PyBytesRef { } #[pymethod(name = "find")] - fn find( - self, - sub: PyObjectRef, - start: OptionalArg, - end: OptionalArg, - vm: &VirtualMachine, - ) -> PyResult { - Ok(vm.new_int(self.inner.find(sub, start, end, vm)?)) + fn find(self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult { + self.inner.find(options, vm) } #[pymethod(name = "index")] - fn index( - self, - sub: PyObjectRef, - start: OptionalArg, - end: OptionalArg, - vm: &VirtualMachine, - ) -> PyResult { - let res = self.inner.find(sub, start, end, vm)?; + fn index(self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult { + let res = self.inner.find(options, vm)?; if res == -1 { return Err(vm.new_value_error("substring not found".to_string())); } From a42bfae84d07f5743480af26ab8814d486513b5d Mon Sep 17 00:00:00 2001 From: jgirardet Date: Thu, 18 Apr 2019 21:41:46 +0200 Subject: [PATCH 21/26] kwargs for translate --- tests/snippets/bytes.py | 1 + vm/src/obj/objbyteinner.rs | 81 ++++++++++++++++++++++---------------- vm/src/obj/objbytes.rs | 12 ++---- 3 files changed, 51 insertions(+), 43 deletions(-) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index 0a3fb11abc..99709e7683 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -324,3 +324,4 @@ ) assert b"hjhtuyjyujuyj".translate(bytes.maketrans(b"hj", b"ab")) == b"abatuybyubuyb" assert b"hjhtuyfjtyhuhjuyj".translate(None, b"ht") == b"juyfjyujuyj" +assert b"hjhtuyfjtyhuhjuyj".translate(None, delete = b"ht") == b"juyfjyujuyj" diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index 6d5d491484..19a40f0a14 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -154,9 +154,8 @@ pub struct ByteInnerPaddingOptions { #[pyarg(positional_only, optional = true)] fillbyte: OptionalArg, } - impl ByteInnerPaddingOptions { - fn get_value(&self, fn_name: &str, len: usize, vm: &VirtualMachine) -> PyResult<(u8, usize)> { + fn get_value(self, fn_name: &str, len: usize, vm: &VirtualMachine) -> PyResult<(u8, usize)> { let fillbyte = if let OptionalArg::Present(v) = &self.fillbyte { match try_as_byte(&v) { Some(x) => { @@ -197,6 +196,49 @@ impl ByteInnerPaddingOptions { } } +#[derive(FromArgs)] +pub struct ByteInnerTranslateOptions { + #[pyarg(positional_only, optional = false)] + table: PyObjectRef, + #[pyarg(positional_or_keyword, optional = true)] + delete: OptionalArg, +} + +impl ByteInnerTranslateOptions { + pub fn get_value(self, vm: &VirtualMachine) -> PyResult<(Vec, Vec)> { + let table = match try_as_bytes_like(&self.table) { + Some(value) => value, + None => match_class!(self.table, + + _n @ PyNone => (0..=255).collect::>(), + obj => {return Err(vm.new_type_error(format!("a bytes-like object is required, not {}", obj)));}, + ), + }; + + if table.len() != 256 { + return Err( + vm.new_value_error("translation table must be 256 characters long".to_string()) + ); + } + + let delete = if let OptionalArg::Present(value) = &self.delete { + match try_as_bytes_like(&value) { + Some(value) => value, + None => { + return Err(vm.new_type_error(format!( + "a bytes-like object is required, not {}", + value + ))); + } + } + } else { + vec![] + }; + + Ok((table, delete)) + } +} + impl PyByteInner { pub fn repr(&self) -> PyResult { let mut res = String::with_capacity(self.elements.len()); @@ -677,40 +719,9 @@ impl PyByteInner { Ok(vm.ctx.new_bytes(res)) } - pub fn translate( - &self, - table: PyObjectRef, - delete: OptionalArg, - vm: &VirtualMachine, - ) -> PyResult { - let table = match try_as_bytes_like(&table) { - Some(value) => value, - None => match_class!(table, - _n @ PyNone => (0..=255).collect::>(), - obj => {return Err(vm.new_type_error(format!("a bytes-like object is required, not {}", obj)));}, - ), - }; - - if table.len() != 256 { - return Err( - vm.new_value_error("translation table must be 256 characters long".to_string()) - ); - } - - let delete = if let OptionalArg::Present(value) = delete { - match try_as_bytes_like(&value) { - Some(value) => value, - None => { - return Err(vm.new_type_error(format!( - "a bytes-like object is required, not {}", - value - ))); - } - } - } else { - vec![] - }; + pub fn translate(&self, options: ByteInnerTranslateOptions, vm: &VirtualMachine) -> PyResult { + let (table, delete) = options.get_value(vm)?; let mut res = vec![]; diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 8ecc9dac14..c3627c1d76 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -7,7 +7,8 @@ use crate::function::OptionalArg; use crate::pyobject::{PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue}; use super::objbyteinner::{ - ByteInnerFindOptions, ByteInnerNewOptions, ByteInnerPaddingOptions, PyByteInner, + ByteInnerFindOptions, ByteInnerNewOptions, ByteInnerPaddingOptions, ByteInnerTranslateOptions, + PyByteInner, }; use super::objiter; @@ -284,13 +285,8 @@ impl PyBytesRef { } #[pymethod(name = "translate")] - fn translate( - self, - table: PyObjectRef, - delete: OptionalArg, - vm: &VirtualMachine, - ) -> PyResult { - self.inner.translate(table, delete, vm) + fn translate(self, options: ByteInnerTranslateOptions, vm: &VirtualMachine) -> PyResult { + self.inner.translate(options, vm) } } From 8a2ce9e260a3949584c7d329f44f6ada322b4982 Mon Sep 17 00:00:00 2001 From: jgirardet Date: Thu, 18 Apr 2019 21:58:15 +0200 Subject: [PATCH 22/26] add swapcase --- tests/snippets/bytes.py | 4 +++- vm/src/obj/objbyteinner.rs | 12 ++++++++++++ vm/src/obj/objbytes.rs | 5 +++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index 99709e7683..3b3dcb4571 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -121,7 +121,7 @@ assert bytes(b"Is Title Case").istitle() assert not bytes(b"is Not title casE").istitle() -# upper lower, capitalize +# upper lower, capitalize, swapcase l = bytes(b"lower") b = bytes(b"UPPER") assert l.lower().islower() @@ -129,6 +129,8 @@ assert l.capitalize() == b"Lower" assert b.capitalize() == b"Upper" assert bytes().capitalize() == bytes() +assert b"AaBbCc123'@/".swapcase().swapcase() == b"AaBbCc123'@/" +assert b"AaBbCc123'@/".swapcase() == b"aAbBcC123'@/" # hex from hex assert bytes([0, 1, 9, 23, 90, 234]).hex() == "000109175aea" diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index 19a40f0a14..9b4ec8beaf 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -474,6 +474,18 @@ impl PyByteInner { new } + pub fn swapcase(&self, _vm: &VirtualMachine) -> Vec { + let mut new: Vec = Vec::new(); + for w in &self.elements { + match w { + 65..=90 => new.push(w.to_ascii_lowercase()), + 97..=122 => new.push(w.to_ascii_uppercase()), + x => new.push(*x), + } + } + new + } + pub fn hex(&self, vm: &VirtualMachine) -> PyResult { let bla = self .elements diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index c3627c1d76..964675f57a 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -214,6 +214,11 @@ impl PyBytesRef { Ok(vm.ctx.new_bytes(self.inner.capitalize(vm))) } + #[pymethod(name = "swapcase")] + fn swapcase(self, vm: &VirtualMachine) -> PyResult { + Ok(vm.ctx.new_bytes(self.inner.swapcase(vm))) + } + #[pymethod(name = "hex")] fn hex(self, vm: &VirtualMachine) -> PyResult { self.inner.hex(vm) From 9ea823abf148fd8c8ccf6d77fb9236c536bde83d Mon Sep 17 00:00:00 2001 From: jgirardet Date: Thu, 18 Apr 2019 23:06:11 +0200 Subject: [PATCH 23/26] add rfind, rindex --- tests/snippets/bytes.py | 8 ++++++++ vm/src/obj/objbyteinner.rs | 27 +++++++++++++++++++++------ vm/src/obj/objbytes.rs | 18 ++++++++++++++++-- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index 3b3dcb4571..380a193863 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -310,6 +310,14 @@ assert b"abcd".find(1) == -1 assert b"abcd".find(99) == 2 +assert b"abcdabcda".find(b"a") == 0 +assert b"abcdabcda".rfind(b"a") == 8 +assert b"abcdabcda".rfind(b"a", 2, 6) == 4 +assert b"abcdabcda".rfind(b"a", None, 6) == 4 +assert b"abcdabcda".rfind(b"a", 2, None) == 8 +assert b"abcdabcda".index(b"a") == 0 +assert b"abcdabcda".rindex(b"a") == 8 + # make trans # fmt: off diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index 9b4ec8beaf..050553ea8f 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -682,7 +682,12 @@ impl PyByteInner { Ok(vm.new_bool(suff.as_slice() == &self.elements.do_slice(range)[offset])) } - pub fn find(&self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult { + pub fn find( + &self, + options: ByteInnerFindOptions, + reverse: bool, + vm: &VirtualMachine, + ) -> PyResult { let (sub, range) = options.get_value(&self.elements, vm)?; // not allowed for this method if range.end < range.start { @@ -690,13 +695,23 @@ impl PyByteInner { } let start = range.start; + let end = range.end; - let slice = &self.elements[range]; - for (n, _) in slice.iter().enumerate() { - if n + sub.len() <= slice.len() && &slice[n..n + sub.len()] == sub.as_slice() { - return Ok((start + n) as isize); + if reverse { + let slice = self.elements.do_slice_reverse(range); + for (n, _) in slice.iter().enumerate() { + if n + sub.len() <= slice.len() && &slice[n..n + sub.len()] == sub.as_slice() { + return Ok((end - n - 1) as isize); + } } - } + } else { + let slice = self.elements.do_slice(range); + for (n, _) in slice.iter().enumerate() { + if n + sub.len() <= slice.len() && &slice[n..n + sub.len()] == sub.as_slice() { + return Ok((start + n) as isize); + } + } + }; Ok(-1isize) } diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 964675f57a..ba23751f7e 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -277,12 +277,26 @@ impl PyBytesRef { #[pymethod(name = "find")] fn find(self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult { - self.inner.find(options, vm) + self.inner.find(options, false, vm) } #[pymethod(name = "index")] fn index(self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult { - let res = self.inner.find(options, vm)?; + let res = self.inner.find(options, false, vm)?; + if res == -1 { + return Err(vm.new_value_error("substring not found".to_string())); + } + Ok(vm.new_int(res)) + } + + #[pymethod(name = "rfind")] + fn rfind(self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult { + self.inner.find(options, true, vm) + } + + #[pymethod(name = "rindex")] + fn rindex(self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult { + let res = self.inner.find(options, true, vm)?; if res == -1 { return Err(vm.new_value_error("substring not found".to_string())); } From 61ee15b97b6999484f77da677f517f5114870eb9 Mon Sep 17 00:00:00 2001 From: jgirardet Date: Fri, 19 Apr 2019 00:04:39 +0200 Subject: [PATCH 24/26] add strip lstrip rstrip --- tests/snippets/bytes.py | 30 +++++++++++++++-------- vm/src/obj/objbyteinner.rs | 49 ++++++++++++++++++++++++++++++++++++++ vm/src/obj/objbytes.rs | 25 +++++++++++++++++-- 3 files changed, 92 insertions(+), 12 deletions(-) diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index 380a193863..2733533d8f 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -219,15 +219,16 @@ b"||||koki", b"|||||koki", ] -assert [b"kok".rjust(i, b"|") for i in range(2, 10)] == [b'kok', - b'kok', - b'|kok', - b'||kok', - b'|||kok', - b'||||kok', - b'|||||kok', - b'||||||kok'] - +assert [b"kok".rjust(i, b"|") for i in range(2, 10)] == [ + b"kok", + b"kok", + b"|kok", + b"||kok", + b"|||kok", + b"||||kok", + b"|||||kok", + b"||||||kok", +] b"kok".rjust(4) == b" kok" # " test no arg" @@ -334,4 +335,13 @@ ) assert b"hjhtuyjyujuyj".translate(bytes.maketrans(b"hj", b"ab")) == b"abatuybyubuyb" assert b"hjhtuyfjtyhuhjuyj".translate(None, b"ht") == b"juyfjyujuyj" -assert b"hjhtuyfjtyhuhjuyj".translate(None, delete = b"ht") == b"juyfjyujuyj" +assert b"hjhtuyfjtyhuhjuyj".translate(None, delete=b"ht") == b"juyfjyujuyj" + + +# strip lstrip rstrip +assert b" spacious ".strip() == b"spacious" +assert b"www.example.com".strip(b"cmowz.") == b"example" +assert b" spacious ".lstrip() == b"spacious " +assert b"www.example.com".lstrip(b"cmowz.") == b"example.com" +assert b" spacious ".rstrip() == b" spacious" +assert b"mississippi".rstrip(b"ipz") == b"mississ" diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index 050553ea8f..46f55ec8db 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -760,6 +760,49 @@ impl PyByteInner { Ok(vm.ctx.new_bytes(res)) } + + pub fn strip( + &self, + chars: OptionalArg, + position: ByteInnerPosition, + vm: &VirtualMachine, + ) -> PyResult> { + let chars = if let OptionalArg::Present(content) = chars { + match try_as_bytes_like(&content) { + Some(value) => value, + None => { + return Err(vm.new_type_error(format!( + "a bytes-like object is required, not {}", + content + ))); + } + } + } else { + vec![b' '] + }; + + let mut start = 0; + let mut end = self.len(); + + if let ByteInnerPosition::Left | ByteInnerPosition::All = position { + for (n, i) in self.elements.iter().enumerate() { + if !chars.contains(i) { + start = n; + break; + } + } + } + + if let ByteInnerPosition::Right | ByteInnerPosition::All = position { + for (n, i) in self.elements.iter().rev().enumerate() { + if !chars.contains(i) { + end = self.len() - n; + break; + } + } + } + Ok(self.elements[start..end].to_vec()) + } } pub fn try_as_byte(obj: &PyObjectRef) -> Option> { @@ -789,3 +832,9 @@ pub trait ByteOr: ToPrimitive { } impl ByteOr for BigInt {} + +pub enum ByteInnerPosition { + Left, + Right, + All, +} diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index ba23751f7e..bd483be18a 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -7,8 +7,8 @@ use crate::function::OptionalArg; use crate::pyobject::{PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue}; use super::objbyteinner::{ - ByteInnerFindOptions, ByteInnerNewOptions, ByteInnerPaddingOptions, ByteInnerTranslateOptions, - PyByteInner, + ByteInnerFindOptions, ByteInnerNewOptions, ByteInnerPaddingOptions, ByteInnerPosition, + ByteInnerTranslateOptions, PyByteInner, }; use super::objiter; @@ -307,6 +307,27 @@ impl PyBytesRef { fn translate(self, options: ByteInnerTranslateOptions, vm: &VirtualMachine) -> PyResult { self.inner.translate(options, vm) } + + #[pymethod(name = "strip")] + fn strip(self, chars: OptionalArg, vm: &VirtualMachine) -> PyResult { + Ok(vm + .ctx + .new_bytes(self.inner.strip(chars, ByteInnerPosition::All, vm)?)) + } + + #[pymethod(name = "lstrip")] + fn lstrip(self, chars: OptionalArg, vm: &VirtualMachine) -> PyResult { + Ok(vm + .ctx + .new_bytes(self.inner.strip(chars, ByteInnerPosition::Left, vm)?)) + } + + #[pymethod(name = "rstrip")] + fn rstrip(self, chars: OptionalArg, vm: &VirtualMachine) -> PyResult { + Ok(vm + .ctx + .new_bytes(self.inner.strip(chars, ByteInnerPosition::Right, vm)?)) + } } #[derive(Debug)] From ece66018512e47f766646614f0ec53fd9b764327 Mon Sep 17 00:00:00 2001 From: jgirardet Date: Tue, 30 Apr 2019 15:47:41 +0200 Subject: [PATCH 25/26] add PyByteInner as fn argument / use Either --- vm/src/obj/objbyteinner.rs | 273 +++++++++++++++++-------------------- vm/src/obj/objbytes.rs | 30 ++-- 2 files changed, 139 insertions(+), 164 deletions(-) diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index 46f55ec8db..2f0e6b9039 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -1,5 +1,11 @@ use crate::obj::objint::PyIntRef; -use crate::obj::objslice::PySlice; +use crate::obj::objnone::PyNoneRef; +use crate::obj::objslice::PySliceRef; +use crate::obj::objtuple::PyTupleRef; +use crate::pyobject::Either; +use crate::pyobject::PyRef; +use crate::pyobject::PyValue; +use crate::pyobject::TryFromObject; use crate::pyobject::{PyIterable, PyObjectRef}; use core::ops::Range; use num_bigint::BigInt; @@ -16,7 +22,7 @@ use std::hash::{Hash, Hasher}; use super::objint; use super::objsequence::{is_valid_slice_arg, PySliceableSequence}; -use super::objtype; + use crate::obj::objint::PyInt; use num_integer::Integer; use num_traits::ToPrimitive; @@ -24,7 +30,7 @@ use num_traits::ToPrimitive; use super::objbytearray::{get_value as get_value_bytearray, PyByteArray}; use super::objbytes::PyBytes; use super::objmemory::PyMemoryView; -use super::objnone::PyNone; + use super::objsequence; #[derive(Debug, Default, Clone)] @@ -32,6 +38,37 @@ pub struct PyByteInner { pub elements: Vec, } +impl TryFromObject for PyByteInner { + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { + match_class!(obj, + + i @ PyBytes => Ok(PyByteInner{elements: i.get_value().to_vec()}), + j @ PyByteArray => Ok(PyByteInner{elements: get_value_bytearray(&j.as_object()).to_vec()}), + k @ PyMemoryView => Ok(PyByteInner{elements: k.get_obj_value().unwrap()}), + obj => Err(vm.new_type_error(format!( + "a bytes-like object is required, not {}", + obj.class() + ))) + ) + } +} + +impl TryFromObject for Either> { + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { + match PyByteInner::try_from_object(vm, obj.clone()) { + Ok(a) => Ok(Either::A(a)), + Err(_) => match obj.clone().downcast::() { + Ok(b) => Ok(Either::B(b)), + Err(_) => Err(vm.new_type_error(format!( + "a bytes-like object or {} is required, not {}", + B::class(vm), + obj.class() + ))), + }, + } + } +} + #[derive(FromArgs)] pub struct ByteInnerNewOptions { #[pyarg(positional_only, optional = true)] @@ -103,11 +140,11 @@ impl ByteInnerNewOptions { #[derive(FromArgs)] pub struct ByteInnerFindOptions { #[pyarg(positional_only, optional = false)] - sub: PyObjectRef, + sub: Either, #[pyarg(positional_only, optional = true)] - start: OptionalArg, + start: OptionalArg>, #[pyarg(positional_only, optional = true)] - end: OptionalArg, + end: OptionalArg>, } impl ByteInnerFindOptions { @@ -116,29 +153,19 @@ impl ByteInnerFindOptions { elements: &[u8], vm: &VirtualMachine, ) -> PyResult<(Vec, Range)> { - let sub = match try_as_bytes_like(&self.sub) { - Some(value) => value, - None => match_class!(self.sub, - i @ PyInt => vec![i.as_bigint().byte_or(vm)?], - obj => {return Err(vm.new_type_error(format!("argument should be integer or bytes-like object, not {}", obj)));}), + let sub = match self.sub { + Either::A(v) => v.elements.to_vec(), + Either::B(int) => vec![int.as_bigint().byte_or(vm)?], }; - let start = if let OptionalArg::Present(st) = self.start { - match_class!(st, - i @ PyInt => {Some(i.as_bigint().clone())}, - _obj @ PyNone => None, - _=> {return Err(vm.new_type_error("slice indices must be integers or None or have an __index__ method".to_string()));} - ) - } else { - None + + let start = match self.start { + OptionalArg::Present(Some(int)) => Some(int.as_bigint().clone()), + _ => None, }; - let end = if let OptionalArg::Present(e) = self.end { - match_class!(e, - i @ PyInt => {Some(i.as_bigint().clone())}, - _obj @ PyNone => None, - _=> {return Err(vm.new_type_error("slice indices must be integers or None or have an __index__ method".to_string()));} - ) - } else { - None + + let end = match self.end { + OptionalArg::Present(Some(int)) => Some(int.as_bigint().clone()), + _ => None, }; let range = elements.to_vec().get_slice_range(&start, &end); @@ -199,20 +226,16 @@ impl ByteInnerPaddingOptions { #[derive(FromArgs)] pub struct ByteInnerTranslateOptions { #[pyarg(positional_only, optional = false)] - table: PyObjectRef, + table: Either, #[pyarg(positional_or_keyword, optional = true)] - delete: OptionalArg, + delete: OptionalArg, } impl ByteInnerTranslateOptions { pub fn get_value(self, vm: &VirtualMachine) -> PyResult<(Vec, Vec)> { - let table = match try_as_bytes_like(&self.table) { - Some(value) => value, - None => match_class!(self.table, - - _n @ PyNone => (0..=255).collect::>(), - obj => {return Err(vm.new_type_error(format!("a bytes-like object is required, not {}", obj)));}, - ), + let table = match self.table { + Either::A(v) => v.elements.to_vec(), + Either::B(_) => (0..=255).collect::>(), }; if table.len() != 256 { @@ -221,18 +244,9 @@ impl ByteInnerTranslateOptions { ); } - let delete = if let OptionalArg::Present(value) = &self.delete { - match try_as_bytes_like(&value) { - Some(value) => value, - None => { - return Err(vm.new_type_error(format!( - "a bytes-like object is required, not {}", - value - ))); - } - } - } else { - vec![] + let delete = match self.delete { + OptionalArg::Present(byte) => byte.elements, + _ => vec![], }; Ok((table, delete)) @@ -319,52 +333,43 @@ impl PyByteInner { elements } - pub fn contains(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { - match try_as_bytes_like(&needle) { - Some(value) => self.contains_bytes(&value, vm), - None => match_class!(needle, - i @ PyInt => self.contains_int(&i, vm), - obj => {Err(vm.new_type_error(format!("a bytes-like object is required, not {}", obj)))}), - } - } - - fn contains_bytes(&self, other: &[u8], vm: &VirtualMachine) -> PyResult { - for (n, i) in self.elements.iter().enumerate() { - if n + other.len() <= self.len() - && *i == other[0] - && &self.elements[n..n + other.len()] == other - { - return Ok(vm.new_bool(true)); + pub fn contains(&self, needle: Either, vm: &VirtualMachine) -> PyResult { + match needle { + Either::A(byte) => { + let other = &byte.elements[..]; + for (n, i) in self.elements.iter().enumerate() { + if n + other.len() <= self.len() + && *i == other[0] + && &self.elements[n..n + other.len()] == other + { + return Ok(vm.new_bool(true)); + } + } + Ok(vm.new_bool(false)) + } + Either::B(int) => { + if self.elements.contains(&int.as_bigint().byte_or(vm)?) { + Ok(vm.new_bool(true)) + } else { + Ok(vm.new_bool(false)) + } } - } - Ok(vm.new_bool(false)) - } - - fn contains_int(&self, int: &PyInt, vm: &VirtualMachine) -> PyResult { - if self.elements.contains(&int.as_bigint().byte_or(vm)?) { - Ok(vm.new_bool(true)) - } else { - Ok(vm.new_bool(false)) } } - pub fn getitem(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { - match_class!(needle, - int @ PyInt => //self.inner.getitem_int(&int, vm), - { - if let Some(idx) = self.elements.get_pos(int.as_bigint().to_i32().unwrap()) { - Ok(vm.new_int(self.elements[idx])) - } else { - Err(vm.new_index_error("index out of range".to_string())) + pub fn getitem(&self, needle: Either, vm: &VirtualMachine) -> PyResult { + match needle { + Either::A(int) => { + if let Some(idx) = self.elements.get_pos(int.as_bigint().to_i32().unwrap()) { + Ok(vm.new_int(self.elements[idx])) + } else { + Err(vm.new_index_error("index out of range".to_string())) + } + } + Either::B(slice) => Ok(vm + .ctx + .new_bytes(self.elements.get_slice_items(vm, slice.as_object())?)), } - }, - slice @ PySlice => //self.inner.getitem_slice(slice.as_object(), vm), - { - Ok(vm - .ctx - .new_bytes(self.elements.get_slice_items(vm, slice.as_object())?)) - }, - obj => Err(vm.new_type_error(format!("byte indices must be integers or slices, not {}", obj)))) } pub fn isalnum(&self, vm: &VirtualMachine) -> PyResult { @@ -475,7 +480,7 @@ impl PyByteInner { } pub fn swapcase(&self, _vm: &VirtualMachine) -> Vec { - let mut new: Vec = Vec::new(); + let mut new: Vec = Vec::with_capacity(self.elements.len()); for w in &self.elements { match w { 65..=90 => new.push(w.to_ascii_lowercase()), @@ -532,7 +537,6 @@ impl PyByteInner { options: ByteInnerPaddingOptions, vm: &VirtualMachine, ) -> PyResult> { - // let fn_name = "center".to_string(); let (fillbyte, diff) = options.get_value("center", self.len(), vm)?; let mut ln: usize = diff / 2; @@ -559,7 +563,6 @@ impl PyByteInner { options: ByteInnerPaddingOptions, vm: &VirtualMachine, ) -> PyResult> { - // let fn_name = "ljust".to_string(); let (fillbyte, diff) = options.get_value("ljust", self.len(), vm)?; // merge all @@ -575,7 +578,6 @@ impl PyByteInner { options: ByteInnerPaddingOptions, vm: &VirtualMachine, ) -> PyResult> { - // let fn_name = "rjust".to_string(); let (fillbyte, diff) = options.get_value("rjust", self.len(), vm)?; // merge all @@ -629,35 +631,28 @@ impl PyByteInner { pub fn startsendswith( &self, - arg: PyObjectRef, + arg: Either, start: OptionalArg, end: OptionalArg, endswith: bool, // true for endswith, false for startswith vm: &VirtualMachine, ) -> PyResult { - let suff = if objtype::isinstance(&arg, &vm.ctx.tuple_type()) { - let mut flatten = vec![]; - for v in objsequence::get_elements(&arg).to_vec() { - match try_as_bytes_like(&v) { - None => { - return Err(vm.new_type_error(format!( - "a bytes-like object is required, not {}", - &v.class().name, - ))); + let suff = match arg { + Either::A(byte) => byte.elements, + Either::B(tuple) => { + let mut flatten = vec![]; + for v in objsequence::get_elements(tuple.as_object()).to_vec() { + match try_as_bytes_like(&v) { + None => { + return Err(vm.new_type_error(format!( + "a bytes-like object is required, not {}", + &v.class().name, + ))); + } + Some(value) => flatten.extend(value), } - Some(value) => flatten.extend(value), - } - } - flatten - } else { - match try_as_bytes_like(&arg) { - Some(value) => value, - None => { - return Err(vm.new_type_error(format!( - "endswith first arg must be bytes or a tuple of bytes, not {}", - arg - ))); } + flatten } }; @@ -715,33 +710,17 @@ impl PyByteInner { Ok(-1isize) } - pub fn maketrans(from: PyObjectRef, to: PyObjectRef, vm: &VirtualMachine) -> PyResult { + pub fn maketrans(from: PyByteInner, to: PyByteInner, vm: &VirtualMachine) -> PyResult { let mut res = vec![]; - let from = match try_as_bytes_like(&from) { - Some(value) => value, - None => { - return Err( - vm.new_type_error(format!("a bytes-like object is required, not {}", from)) - ); - } - }; - - let to = match try_as_bytes_like(&to) { - Some(value) => value, - None => { - return Err( - vm.new_type_error(format!("a bytes-like object is required, not {}", to)) - ); - } - }; - for i in 0..=255 { - res.push(if let Some(position) = from.iter().position(|&x| x == i) { - to[position] - } else { - i - }); + res.push( + if let Some(position) = from.elements.iter().position(|&x| x == i) { + to.elements[position] + } else { + i + }, + ); } Ok(vm.ctx.new_bytes(res)) @@ -763,20 +742,12 @@ impl PyByteInner { pub fn strip( &self, - chars: OptionalArg, + chars: OptionalArg, position: ByteInnerPosition, - vm: &VirtualMachine, + _vm: &VirtualMachine, ) -> PyResult> { - let chars = if let OptionalArg::Present(content) = chars { - match try_as_bytes_like(&content) { - Some(value) => value, - None => { - return Err(vm.new_type_error(format!( - "a bytes-like object is required, not {}", - content - ))); - } - } + let chars = if let OptionalArg::Present(bytes) = chars { + bytes.elements } else { vec![b' '] }; diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index bd483be18a..c16fd123fe 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -1,4 +1,9 @@ +use crate::obj::objint::PyIntRef; +use crate::obj::objslice::PySliceRef; use crate::obj::objstr::PyStringRef; +use crate::obj::objtuple::PyTupleRef; + +use crate::pyobject::Either; use crate::vm::VirtualMachine; use core::cell::Cell; use std::ops::Deref; @@ -35,7 +40,6 @@ impl PyBytes { inner: PyByteInner { elements }, } } - pub fn get_value(&self) -> &[u8] { &self.inner.elements } @@ -150,12 +154,12 @@ impl PyBytesRef { } #[pymethod(name = "__contains__")] - fn contains(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { + fn contains(self, needle: Either, vm: &VirtualMachine) -> PyResult { self.inner.contains(needle, vm) } #[pymethod(name = "__getitem__")] - fn getitem(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { + fn getitem(self, needle: Either, vm: &VirtualMachine) -> PyResult { self.inner.getitem(needle, vm) } @@ -256,7 +260,7 @@ impl PyBytesRef { #[pymethod(name = "endswith")] fn endswith( self, - suffix: PyObjectRef, + suffix: Either, start: OptionalArg, end: OptionalArg, vm: &VirtualMachine, @@ -267,12 +271,12 @@ impl PyBytesRef { #[pymethod(name = "startswith")] fn startswith( self, - suffix: PyObjectRef, + prefix: Either, start: OptionalArg, end: OptionalArg, vm: &VirtualMachine, ) -> PyResult { - self.inner.startsendswith(suffix, start, end, false, vm) + self.inner.startsendswith(prefix, start, end, false, vm) } #[pymethod(name = "find")] @@ -281,12 +285,12 @@ impl PyBytesRef { } #[pymethod(name = "index")] - fn index(self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult { + fn index(self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult { let res = self.inner.find(options, false, vm)?; if res == -1 { return Err(vm.new_value_error("substring not found".to_string())); } - Ok(vm.new_int(res)) + Ok(res) } #[pymethod(name = "rfind")] @@ -295,12 +299,12 @@ impl PyBytesRef { } #[pymethod(name = "rindex")] - fn rindex(self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult { + fn rindex(self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult { let res = self.inner.find(options, true, vm)?; if res == -1 { return Err(vm.new_value_error("substring not found".to_string())); } - Ok(vm.new_int(res)) + Ok(res) } #[pymethod(name = "translate")] @@ -309,21 +313,21 @@ impl PyBytesRef { } #[pymethod(name = "strip")] - fn strip(self, chars: OptionalArg, vm: &VirtualMachine) -> PyResult { + fn strip(self, chars: OptionalArg, vm: &VirtualMachine) -> PyResult { Ok(vm .ctx .new_bytes(self.inner.strip(chars, ByteInnerPosition::All, vm)?)) } #[pymethod(name = "lstrip")] - fn lstrip(self, chars: OptionalArg, vm: &VirtualMachine) -> PyResult { + fn lstrip(self, chars: OptionalArg, vm: &VirtualMachine) -> PyResult { Ok(vm .ctx .new_bytes(self.inner.strip(chars, ByteInnerPosition::Left, vm)?)) } #[pymethod(name = "rstrip")] - fn rstrip(self, chars: OptionalArg, vm: &VirtualMachine) -> PyResult { + fn rstrip(self, chars: OptionalArg, vm: &VirtualMachine) -> PyResult { Ok(vm .ctx .new_bytes(self.inner.strip(chars, ByteInnerPosition::Right, vm)?)) From 5d13daef207852ecde3feef346981bd1ab8d1f5d Mon Sep 17 00:00:00 2001 From: Jimmy Girardet Date: Tue, 30 Apr 2019 18:01:40 +0200 Subject: [PATCH 26/26] remove all try_as_bytes_like --- vm/src/obj/objbyteinner.rs | 32 +++----------------------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs index 2f0e6b9039..b75f8be697 100644 --- a/vm/src/obj/objbyteinner.rs +++ b/vm/src/obj/objbyteinner.rs @@ -612,18 +612,9 @@ impl PyByteInner { pub fn join(&self, iter: PyIterable, vm: &VirtualMachine) -> PyResult { let mut refs = vec![]; - for (index, v) in iter.iter(vm)?.enumerate() { + for v in iter.iter(vm)? { let v = v?; - match try_as_bytes_like(&v) { - None => { - return Err(vm.new_type_error(format!( - "sequence item {}: expected a bytes-like object, {} found", - index, - &v.class().name, - ))); - } - Some(value) => refs.extend(value), - } + refs.extend(PyByteInner::try_from_object(vm, v)?.elements) } Ok(vm.ctx.new_bytes(refs)) @@ -642,15 +633,7 @@ impl PyByteInner { Either::B(tuple) => { let mut flatten = vec![]; for v in objsequence::get_elements(tuple.as_object()).to_vec() { - match try_as_bytes_like(&v) { - None => { - return Err(vm.new_type_error(format!( - "a bytes-like object is required, not {}", - &v.class().name, - ))); - } - Some(value) => flatten.extend(value), - } + flatten.extend(PyByteInner::try_from_object(vm, v)?.elements) } flatten } @@ -784,15 +767,6 @@ pub fn try_as_byte(obj: &PyObjectRef) -> Option> { _ => None) } -pub fn try_as_bytes_like(obj: &PyObjectRef) -> Option> { - match_class!(obj.clone(), - - i @ PyBytes => Some(i.get_value().to_vec()), - j @ PyByteArray => Some(get_value_bytearray(&j.as_object()).to_vec()), - k @ PyMemoryView => Some(k.get_obj_value().unwrap()), - _ => None) -} - pub trait ByteOr: ToPrimitive { fn byte_or(&self, vm: &VirtualMachine) -> Result { match self.to_u8() {