add endswith startswith

This commit is contained in:
Jimmy Girardet
2019-04-17 15:55:37 +02:00
committed by jgirardet
parent 14658b6236
commit 8a7c46da7b
4 changed files with 116 additions and 2 deletions

View File

@@ -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<PyObjectRef>,
end: OptionalArg<PyObjectRef>,
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<Vec<u8>> {

View File

@@ -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<PyObjectRef>,
end: OptionalArg<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult {
self.inner.startsendswith(suffix, start, end, true, vm)
}
#[pymethod(name = "startswith")]
fn startswith(
self,
suffix: PyObjectRef,
start: OptionalArg<PyObjectRef>,
end: OptionalArg<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult {
self.inner.startsendswith(suffix, start, end, false, vm)
}
}
#[derive(Debug)]

View File

@@ -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<Target = Vec<
}
panic!("Cannot extract elements from non-sequence");
}
//Check if given arg could be used with PySciceableSequance.get_slice_range()
pub fn is_valid_slice_arg(
arg: OptionalArg<PyObjectRef>,
vm: &VirtualMachine,
) -> Result<Option<BigInt>, 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)
}
}