diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index edf21c973c..561ea4d772 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -540,8 +540,12 @@ where let end_pos = self.get_pos(); let tok = if is_bytes { - Tok::Bytes { - value: string_content.as_bytes().to_vec(), + if string_content.is_ascii() { + Tok::Bytes { + value: string_content.as_bytes().to_vec(), + } + } else { + return Err(LexicalError::StringError); } } else { Tok::String { diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py index 19ebef68ae..0ffbc29f8c 100644 --- a/tests/snippets/bytes.py +++ b/tests/snippets/bytes.py @@ -1,6 +1,129 @@ -assert b'foobar'.__eq__(2) == NotImplemented -assert b'foobar'.__ne__(2) == NotImplemented -assert b'foobar'.__gt__(2) == NotImplemented -assert b'foobar'.__ge__(2) == NotImplemented -assert b'foobar'.__lt__(2) == NotImplemented -assert b'foobar'.__le__(2) == NotImplemented +from testutils import assertRaises + +# new +assert bytes([1, 2, 3]) +assert bytes((1, 2, 3)) +assert bytes(range(4)) +assert bytes(3) +assert b"bla" +assert bytes("bla", "utf8") +with assertRaises(TypeError): + bytes("bla") + + +a = b"abcd" +b = b"ab" +c = b"abcd" + +# +# repr +assert repr(bytes([0, 1, 2])) == repr(b"\x00\x01\x02") +assert repr( + bytes([0, 1, 9, 10, 11, 13, 31, 32, 33, 89, 120, 255]) + == "b'\\x00\\x01\\t\\n\\x0b\\r\\x1f !Yx\\xff'" +) +assert repr(b"abcd") == "b'abcd'" + +# len +assert len(bytes("abcdé", "utf8")) == 6 + +# comp +assert a == b"abcd" +assert a > b +assert a >= b +assert b < a +assert b <= a + +assert b"foobar".__eq__(2) == NotImplemented +assert b"foobar".__ne__(2) == NotImplemented +assert b"foobar".__gt__(2) == NotImplemented +assert b"foobar".__ge__(2) == NotImplemented +assert b"foobar".__lt__(2) == NotImplemented +assert b"foobar".__le__(2) == NotImplemented + +# hash +hash(a) == hash(b"abcd") + +# iter +[i for i in b"abcd"] == ["a", "b", "c", "d"] +assert list(bytes(3)) == [0, 0, 0] + +# add +assert a + b == b"abcdab" + +# contains +assert b"ab" in b"abcd" +assert b"cd" in b"abcd" +assert b"abcd" in b"abcd" +assert b"a" in b"abcd" +assert b"d" in b"abcd" +assert b"dc" not in b"abcd" +assert 97 in b"abcd" +assert 150 not in b"abcd" +with assertRaises(ValueError): + 350 in b"abcd" + + +# getitem +d = b"abcdefghij" + +assert d[1] == 98 +assert d[-1] == 106 +assert d[2:6] == b"cdef" +assert d[-6:] == b"efghij" +assert d[1:8:2] == b"bdfh" +assert d[8:1:-2] == b"igec" + + +# is_xx methods + +assert bytes(b"1a23").isalnum() +assert not bytes(b"1%a23").isalnum() + +assert bytes(b"abc").isalpha() +assert not bytes(b"abc1").isalpha() + +# travis doesn't like this +# assert bytes(b'xyz').isascii() +# assert not bytes([128, 157, 32]).isascii() + +assert bytes(b"1234567890").isdigit() +assert not bytes(b"12ab").isdigit() + +l = bytes(b"lower") +b = bytes(b"UPPER") + +assert l.islower() +assert not l.isupper() +assert b.isupper() +assert not bytes(b"Super Friends").islower() + +assert bytes(b" \n\t").isspace() +assert not bytes(b"\td\n").isspace() + +assert b.isupper() +assert not b.islower() +assert l.islower() +assert not bytes(b"tuPpEr").isupper() + +assert bytes(b"Is Title Case").istitle() +assert not bytes(b"is Not title casE").istitle() + +# upper lower +l = bytes(b"lower") +b = bytes(b"UPPER") +assert l.lower().islower() +assert b.upper().isupper() + +# hex from hex +assert bytes([0, 1, 9, 23, 90, 234]).hex() == "000109175aea" + +bytes.fromhex("62 6c7a 34350a ") == b"blz45\n" +try: + bytes.fromhex("62 a 21") +except ValueError as e: + str(e) == "non-hexadecimal number found in fromhex() arg at position 4" +try: + bytes.fromhex("6Z2") +except ValueError as e: + str(e) == "non-hexadecimal number found in fromhex() arg at position 1" diff --git a/vm/src/obj/mod.rs b/vm/src/obj/mod.rs index 0419d827f0..c824e726d8 100644 --- a/vm/src/obj/mod.rs +++ b/vm/src/obj/mod.rs @@ -3,6 +3,7 @@ pub mod objbool; pub mod objbuiltinfunc; pub mod objbytearray; +pub mod objbyteinner; pub mod objbytes; pub mod objclassmethod; pub mod objcode; diff --git a/vm/src/obj/objbyteinner.rs b/vm/src/obj/objbyteinner.rs new file mode 100644 index 0000000000..532504b028 --- /dev/null +++ b/vm/src/obj/objbyteinner.rs @@ -0,0 +1,349 @@ +use crate::pyobject::PyObjectRef; + +use crate::function::OptionalArg; + +use crate::vm::VirtualMachine; + +use crate::pyobject::{PyResult, TypeProtocol}; + +use crate::obj::objstr::PyString; +use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; + +use super::objint; +use super::objsequence::PySliceableSequence; +use crate::obj::objint::PyInt; +use num_traits::ToPrimitive; + +#[derive(Debug, Default, Clone)] +pub struct PyByteInner { + pub elements: Vec, +} + +impl PyByteInner { + pub fn new( + val_option: OptionalArg, + enc_option: OptionalArg, + 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 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 + ); + } + } else { + return Err(vm.new_type_error(format!( + "bytes() argument 2 must be str, not {}", + enc.class().name + ))); + } + } else { + return Err(vm.new_type_error("encoding without a string argument".to_string())); + } + } else { + return Err(vm.new_type_error("encoding without a string argument".to_string())); + } + // Only one argument + } else { + let value = if let OptionalArg::Present(ival) = val_option { + match_class!(ival.clone(), + i @ PyInt => { + let size = objint::get_value(&i.into_object()).to_usize().unwrap(); + Ok(vec![0; size])}, + _l @ PyString=> {return Err(vm.new_type_error("string argument without an encoding".to_string()));}, + obj => { + let elements = vm.extract_elements(&obj).or_else(|_| {Err(vm.new_type_error(format!( + "cannot convert {} object to bytes", obj.class().name)))}); + + let mut data_bytes = vec![]; + for elem in elements.unwrap(){ + let v = objint::to_int(vm, &elem, 10)?; + if let Some(i) = v.to_u8() { + data_bytes.push(i); + } else { + return Err(vm.new_value_error("bytes must be in range(0, 256)".to_string())); + } + + } + Ok(data_bytes) + } + ) + } else { + Ok(vec![]) + }; + match value { + Ok(val) => Ok(PyByteInner { elements: val }), + Err(err) => Err(err), + } + } + } + + pub fn repr(&self) -> PyResult { + let mut res = String::with_capacity(self.elements.len()); + for i in self.elements.iter() { + match i { + 0..=8 => res.push_str(&format!("\\x0{}", i)), + 9 => res.push_str("\\t"), + 10 => res.push_str("\\n"), + 13 => res.push_str("\\r"), + 32..=126 => res.push(*(i) as char), + _ => res.push_str(&format!("\\x{:x}", i)), + } + } + Ok(res) + } + + pub fn len(&self) -> usize { + self.elements.len() + } + + pub fn is_empty(&self) -> bool { + self.elements.len() == 0 + } + + pub fn eq(&self, other: &PyByteInner, vm: &VirtualMachine) -> PyResult { + if self.elements == other.elements { + Ok(vm.new_bool(true)) + } else { + Ok(vm.new_bool(false)) + } + } + + pub fn ge(&self, other: &PyByteInner, vm: &VirtualMachine) -> PyResult { + if self.elements >= other.elements { + Ok(vm.new_bool(true)) + } else { + Ok(vm.new_bool(false)) + } + } + + pub fn le(&self, other: &PyByteInner, vm: &VirtualMachine) -> PyResult { + if self.elements <= other.elements { + Ok(vm.new_bool(true)) + } else { + Ok(vm.new_bool(false)) + } + } + + pub fn gt(&self, other: &PyByteInner, vm: &VirtualMachine) -> PyResult { + if self.elements > other.elements { + Ok(vm.new_bool(true)) + } else { + Ok(vm.new_bool(false)) + } + } + + pub fn lt(&self, other: &PyByteInner, vm: &VirtualMachine) -> PyResult { + if self.elements < other.elements { + Ok(vm.new_bool(true)) + } else { + Ok(vm.new_bool(false)) + } + } + + pub fn hash(&self) -> usize { + let mut hasher = DefaultHasher::new(); + self.elements.hash(&mut hasher); + hasher.finish() as usize + } + + pub fn add(&self, other: &PyByteInner, _vm: &VirtualMachine) -> Vec { + let elements: Vec = self + .elements + .iter() + .chain(other.elements.iter()) + .cloned() + .collect(); + elements + } + + pub fn contains_bytes(&self, other: &PyByteInner, 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() + { + return Ok(vm.new_bool(true)); + } + } + 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)) + } + } else { + Err(vm.new_value_error("byte must be in range(0, 256)".to_string())) + } + } + + pub fn getitem_int(&self, int: &PyInt, vm: &VirtualMachine) -> PyResult { + 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 { + Ok(vm + .ctx + .new_bytes(self.elements.get_slice_items(vm, slice).unwrap())) + } + + pub fn isalnum(&self, vm: &VirtualMachine) -> PyResult { + Ok(vm.new_bool( + !self.elements.is_empty() + && self + .elements + .iter() + .all(|x| char::from(*x).is_alphanumeric()), + )) + } + + pub fn isalpha(&self, vm: &VirtualMachine) -> PyResult { + Ok(vm.new_bool( + !self.elements.is_empty() + && self.elements.iter().all(|x| char::from(*x).is_alphabetic()), + )) + } + + pub fn isascii(&self, vm: &VirtualMachine) -> PyResult { + Ok(vm.new_bool( + !self.elements.is_empty() && self.elements.iter().all(|x| char::from(*x).is_ascii()), + )) + } + + pub fn isdigit(&self, vm: &VirtualMachine) -> PyResult { + Ok(vm.new_bool( + !self.elements.is_empty() && self.elements.iter().all(|x| char::from(*x).is_digit(10)), + )) + } + + pub fn islower(&self, vm: &VirtualMachine) -> PyResult { + Ok(vm.new_bool( + !self.elements.is_empty() + && self + .elements + .iter() + .filter(|x| !char::from(**x).is_whitespace()) + .all(|x| char::from(*x).is_lowercase()), + )) + } + + pub fn isspace(&self, vm: &VirtualMachine) -> PyResult { + Ok(vm.new_bool( + !self.elements.is_empty() + && self.elements.iter().all(|x| char::from(*x).is_whitespace()), + )) + } + + pub fn isupper(&self, vm: &VirtualMachine) -> PyResult { + Ok(vm.new_bool( + !self.elements.is_empty() + && self + .elements + .iter() + .filter(|x| !char::from(**x).is_whitespace()) + .all(|x| char::from(*x).is_uppercase()), + )) + } + + pub fn istitle(&self, vm: &VirtualMachine) -> PyResult { + if self.elements.is_empty() { + return Ok(vm.new_bool(false)); + } + + let mut iter = self.elements.iter().peekable(); + let mut prev_cased = false; + + while let Some(c) = iter.next() { + let current = char::from(*c); + let next = if let Some(k) = iter.peek() { + char::from(**k) + } else if current.is_uppercase() { + return Ok(vm.new_bool(!prev_cased)); + } else { + return Ok(vm.new_bool(prev_cased)); + }; + + let is_cased = current.to_uppercase().next().unwrap() != current + || current.to_lowercase().next().unwrap() != current; + if (is_cased && next.is_uppercase() && !prev_cased) + || (!is_cased && next.is_lowercase()) + { + return Ok(vm.new_bool(false)); + } + + prev_cased = is_cased; + } + + Ok(vm.new_bool(true)) + } + + pub fn lower(&self, _vm: &VirtualMachine) -> Vec { + self.elements.to_ascii_lowercase() + } + + pub fn upper(&self, _vm: &VirtualMachine) -> Vec { + self.elements.to_ascii_uppercase() + } + + pub fn hex(&self, vm: &VirtualMachine) -> PyResult { + let bla = self + .elements + .iter() + .map(|x| format!("{:02x}", x)) + .collect::(); + Ok(vm.ctx.new_str(bla)) + } + + pub fn fromhex(string: String, vm: &VirtualMachine) -> Result, PyObjectRef> { + // first check for invalid character + for (i, c) in string.char_indices() { + if !c.is_digit(16) && !c.is_whitespace() { + return Err(vm.new_value_error(format!( + "non-hexadecimal number found in fromhex() arg at position {}", + i + ))); + } + } + + // strip white spaces + let stripped = string.split_whitespace().collect::(); + + // Hex is evaluated on 2 digits + if stripped.len() % 2 != 0 { + return Err(vm.new_value_error(format!( + "non-hexadecimal number found in fromhex() arg at position {}", + stripped.len() - 1 + ))); + } + + // parse even string + Ok(stripped + .chars() + .collect::>() + .chunks(2) + .map(|x| x.to_vec().iter().collect::()) + .map(|x| u8::from_str_radix(&x, 16)) + .map(|x| x.unwrap()) + .collect::>()) + } +} diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index f24c9273c5..2c8edca6ab 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -1,27 +1,38 @@ -use std::cell::Cell; -use std::collections::hash_map::DefaultHasher; -use std::hash::{Hash, Hasher}; +use crate::obj::objint::PyInt; +use crate::obj::objstr::PyString; +use crate::vm::VirtualMachine; +use core::cell::Cell; use std::ops::Deref; -use num_traits::ToPrimitive; - use crate::function::OptionalArg; -use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue}; -use crate::vm::VirtualMachine; +use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; -use super::objint; +use super::objbyteinner::PyByteInner; use super::objiter; +use super::objslice::PySlice; use super::objtype::PyClassRef; -#[derive(Debug)] +/// "bytes(iterable_of_ints) -> bytes\n\ +/// bytes(string, encoding[, errors]) -> bytes\n\ +/// bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer\n\ +/// bytes(int) -> bytes object of size given by the parameter initialized with null bytes\n\ +/// bytes() -> empty bytes object\n\nConstruct an immutable array of bytes from:\n \ +/// - an iterable yielding integers in range(256)\n \ +/// - a text string encoded using the specified encoding\n \ +/// - any object implementing the buffer API.\n \ +/// - an integer"; +#[pyclass(name = "bytes", __inside_vm)] +#[derive(Clone, Debug)] pub struct PyBytes { - value: Vec, + inner: PyByteInner, } type PyBytesRef = PyRef; impl PyBytes { - pub fn new(data: Vec) -> Self { - PyBytes { value: data } + pub fn new(elements: Vec) -> Self { + PyBytes { + inner: PyByteInner { elements }, + } } } @@ -29,7 +40,7 @@ impl Deref for PyBytes { type Target = [u8]; fn deref(&self) -> &[u8] { - &self.value + &self.inner.elements } } @@ -39,131 +50,180 @@ impl PyValue for PyBytes { } } -// Binary data support +pub fn get_value<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a { + &obj.payload::().unwrap().inner.elements +} -// Fill bytes class methods: pub fn init(context: &PyContext) { - let bytes_doc = - "bytes(iterable_of_ints) -> bytes\n\ - bytes(string, encoding[, errors]) -> bytes\n\ - bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer\n\ - bytes(int) -> bytes object of size given by the parameter initialized with null bytes\n\ - bytes() -> empty bytes object\n\nConstruct an immutable array of bytes from:\n \ - - an iterable yielding integers in range(256)\n \ - - a text string encoded using the specified encoding\n \ - - any object implementing the buffer API.\n \ - - an integer"; - - extend_class!(context, &context.bytes_type, { - "__new__" => context.new_rustfunc(bytes_new), - "__eq__" => context.new_rustfunc(PyBytesRef::eq), - "__lt__" => context.new_rustfunc(PyBytesRef::lt), - "__le__" => context.new_rustfunc(PyBytesRef::le), - "__gt__" => context.new_rustfunc(PyBytesRef::gt), - "__ge__" => context.new_rustfunc(PyBytesRef::ge), - "__hash__" => context.new_rustfunc(PyBytesRef::hash), - "__repr__" => context.new_rustfunc(PyBytesRef::repr), - "__len__" => context.new_rustfunc(PyBytesRef::len), - "__iter__" => context.new_rustfunc(PyBytesRef::iter), - "__doc__" => context.new_str(bytes_doc.to_string()) - }); - + PyBytesRef::extend_class(context, &context.bytes_type); + let bytes_type = &context.bytes_type; + extend_class!(context, bytes_type, { +"fromhex" => context.new_rustfunc(PyBytesRef::fromhex), +}); let bytesiterator_type = &context.bytesiterator_type; extend_class!(context, bytesiterator_type, { - "__next__" => context.new_rustfunc(PyBytesIteratorRef::next), - "__iter__" => context.new_rustfunc(PyBytesIteratorRef::iter), - }); -} - -fn bytes_new( - cls: PyClassRef, - val_option: OptionalArg, - vm: &VirtualMachine, -) -> PyResult { - // Create bytes data: - let value = if let OptionalArg::Present(ival) = val_option { - let elements = vm.extract_elements(&ival)?; - let mut data_bytes = vec![]; - for elem in elements.iter() { - let v = objint::to_int(vm, elem, 10)?; - data_bytes.push(v.to_u8().unwrap()); - } - data_bytes - // return Err(vm.new_type_error("Cannot construct bytes".to_string())); - } else { - vec![] - }; - - PyBytes::new(value).into_ref_with_type(vm, cls) +"__next__" => context.new_rustfunc(PyBytesIteratorRef::next), +"__iter__" => context.new_rustfunc(PyBytesIteratorRef::iter), +}); } +#[pyimpl(__inside_vm)] impl PyBytesRef { - fn eq(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { - if let Ok(other) = other.downcast::() { - vm.ctx.new_bool(self.value == other.value) - } else { - vm.ctx.not_implemented() + #[pymethod(name = "__new__")] + fn bytes_new( + cls: PyClassRef, + val_option: OptionalArg, + enc_option: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult { + PyBytes { + inner: PyByteInner::new(val_option, enc_option, vm)?, } + .into_ref_with_type(vm, cls) } - fn ge(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { - if let Ok(other) = other.downcast::() { - vm.ctx.new_bool(self.value >= other.value) - } else { - vm.ctx.not_implemented() - } + #[pymethod(name = "__repr__")] + fn repr(self, vm: &VirtualMachine) -> PyResult { + Ok(vm.new_str(format!("b'{}'", self.inner.repr()?))) } - fn gt(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { - if let Ok(other) = other.downcast::() { - vm.ctx.new_bool(self.value > other.value) - } else { - vm.ctx.not_implemented() - } + #[pymethod(name = "__len__")] + fn len(self, _vm: &VirtualMachine) -> usize { + self.inner.len() } - fn le(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { - if let Ok(other) = other.downcast::() { - vm.ctx.new_bool(self.value <= other.value) - } else { - vm.ctx.not_implemented() - } + #[pymethod(name = "__eq__")] + fn eq(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { + match_class!(other, + bytes @ PyBytes => self.inner.eq(&bytes.inner, vm), + _ => Ok(vm.ctx.not_implemented())) } - fn lt(self, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { - if let Ok(other) = other.downcast::() { - vm.ctx.new_bool(self.value < other.value) - } else { - vm.ctx.not_implemented() - } + #[pymethod(name = "__ge__")] + fn ge(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { + match_class!(other, + bytes @ PyBytes => self.inner.ge(&bytes.inner, vm), + _ => Ok(vm.ctx.not_implemented())) } - - fn len(self, _vm: &VirtualMachine) -> usize { - self.value.len() + #[pymethod(name = "__le__")] + fn le(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { + match_class!(other, + bytes @ PyBytes => self.inner.le(&bytes.inner, vm), + _ => Ok(vm.ctx.not_implemented())) } - - fn hash(self, _vm: &VirtualMachine) -> u64 { - let mut hasher = DefaultHasher::new(); - self.value.hash(&mut hasher); - hasher.finish() + #[pymethod(name = "__gt__")] + fn gt(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { + match_class!(other, + bytes @ PyBytes => self.inner.gt(&bytes.inner, vm), + _ => Ok(vm.ctx.not_implemented())) } - - fn repr(self, _vm: &VirtualMachine) -> String { - // TODO: don't just unwrap - let data = String::from_utf8(self.value.clone()).unwrap(); - format!("b'{}'", data) + #[pymethod(name = "__lt__")] + fn lt(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { + match_class!(other, + bytes @ PyBytes => self.inner.lt(&bytes.inner, vm), + _ => Ok(vm.ctx.not_implemented())) + } + #[pymethod(name = "__hash__")] + fn hash(self, _vm: &VirtualMachine) -> usize { + self.inner.hash() } + #[pymethod(name = "__iter__")] fn iter(self, _vm: &VirtualMachine) -> PyBytesIterator { PyBytesIterator { position: Cell::new(0), bytes: self, } } -} -pub fn get_value<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a { - &obj.payload::().unwrap().value + #[pymethod(name = "__add__")] + fn add(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { + match_class!(other, + bytes @ PyBytes => Ok(vm.ctx.new_bytes(self.inner.add(&bytes.inner, vm))), + _ => Ok(vm.ctx.not_implemented())) + } + + #[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)))) + } + + #[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)))) + } + + #[pymethod(name = "isalnum")] + fn isalnum(self, vm: &VirtualMachine) -> PyResult { + self.inner.isalnum(vm) + } + + #[pymethod(name = "isalpha")] + fn isalpha(self, vm: &VirtualMachine) -> PyResult { + self.inner.isalpha(vm) + } + + #[pymethod(name = "isascii")] + fn isascii(self, vm: &VirtualMachine) -> PyResult { + self.inner.isascii(vm) + } + + #[pymethod(name = "isdigit")] + fn isdigit(self, vm: &VirtualMachine) -> PyResult { + self.inner.isdigit(vm) + } + + #[pymethod(name = "islower")] + fn islower(self, vm: &VirtualMachine) -> PyResult { + self.inner.islower(vm) + } + + #[pymethod(name = "isspace")] + fn isspace(self, vm: &VirtualMachine) -> PyResult { + self.inner.isspace(vm) + } + + #[pymethod(name = "isupper")] + fn isupper(self, vm: &VirtualMachine) -> PyResult { + self.inner.isupper(vm) + } + + #[pymethod(name = "istitle")] + fn istitle(self, vm: &VirtualMachine) -> PyResult { + self.inner.istitle(vm) + } + + #[pymethod(name = "lower")] + fn lower(self, vm: &VirtualMachine) -> PyResult { + Ok(vm.ctx.new_bytes(self.inner.lower(vm))) + } + + #[pymethod(name = "upper")] + fn upper(self, vm: &VirtualMachine) -> PyResult { + Ok(vm.ctx.new_bytes(self.inner.upper(vm))) + } + + #[pymethod(name = "hex")] + fn hex(self, vm: &VirtualMachine) -> PyResult { + 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 ))) + ) + } } #[derive(Debug)] @@ -182,7 +242,7 @@ type PyBytesIteratorRef = PyRef; impl PyBytesIteratorRef { fn next(self, vm: &VirtualMachine) -> PyResult { - if self.position.get() < self.bytes.value.len() { + if self.position.get() < self.bytes.inner.len() { let ret = self.bytes[self.position.get()]; self.position.set(self.position.get() + 1); Ok(ret)