forked from Rust-related/RustPython
clean up memory
This commit is contained in:
@@ -68,16 +68,6 @@ impl Constructor for PyMemoryView {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(with(
|
||||
Hashable,
|
||||
Comparable,
|
||||
AsBuffer,
|
||||
AsMapping,
|
||||
AsSequence,
|
||||
Constructor,
|
||||
Iterable,
|
||||
Representable
|
||||
))]
|
||||
impl PyMemoryView {
|
||||
fn parse_format(format: &str, vm: &VirtualMachine) -> PyResult<FormatSpec> {
|
||||
FormatSpec::parse(format.as_bytes(), vm)
|
||||
@@ -143,13 +133,6 @@ impl PyMemoryView {
|
||||
zelf
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
pub fn release(&self) {
|
||||
if self.released.compare_exchange(false, true).is_ok() {
|
||||
self.buffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
fn try_not_released(&self, vm: &VirtualMachine) -> PyResult<()> {
|
||||
if self.released.load() {
|
||||
Err(vm.new_value_error("operation forbidden on released memoryview object".to_owned()))
|
||||
@@ -158,100 +141,6 @@ impl PyMemoryView {
|
||||
}
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn obj(&self, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
|
||||
self.try_not_released(vm).map(|_| self.buffer.obj.clone())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn nbytes(&self, vm: &VirtualMachine) -> PyResult<usize> {
|
||||
self.try_not_released(vm).map(|_| self.desc.len)
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn readonly(&self, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
self.try_not_released(vm).map(|_| self.desc.readonly)
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn itemsize(&self, vm: &VirtualMachine) -> PyResult<usize> {
|
||||
self.try_not_released(vm).map(|_| self.desc.itemsize)
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn ndim(&self, vm: &VirtualMachine) -> PyResult<usize> {
|
||||
self.try_not_released(vm).map(|_| self.desc.ndim())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn shape(&self, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
|
||||
self.try_not_released(vm)?;
|
||||
Ok(vm.ctx.new_tuple(
|
||||
self.desc
|
||||
.dim_desc
|
||||
.iter()
|
||||
.map(|(shape, _, _)| shape.to_pyobject(vm))
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn strides(&self, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
|
||||
self.try_not_released(vm)?;
|
||||
Ok(vm.ctx.new_tuple(
|
||||
self.desc
|
||||
.dim_desc
|
||||
.iter()
|
||||
.map(|(_, stride, _)| stride.to_pyobject(vm))
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn suboffsets(&self, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
|
||||
self.try_not_released(vm)?;
|
||||
Ok(vm.ctx.new_tuple(
|
||||
self.desc
|
||||
.dim_desc
|
||||
.iter()
|
||||
.map(|(_, _, suboffset)| suboffset.to_pyobject(vm))
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn format(&self, vm: &VirtualMachine) -> PyResult<PyStr> {
|
||||
self.try_not_released(vm)
|
||||
.map(|_| PyStr::from(self.desc.format.clone()))
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn contiguous(&self, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
self.try_not_released(vm).map(|_| self.desc.is_contiguous())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn c_contiguous(&self, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
self.try_not_released(vm).map(|_| self.desc.is_contiguous())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn f_contiguous(&self, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
// TODO: fortain order
|
||||
self.try_not_released(vm)
|
||||
.map(|_| self.desc.ndim() <= 1 && self.desc.is_contiguous())
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn enter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
|
||||
zelf.try_not_released(vm).map(|_| zelf)
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn exit(&self, _args: FuncArgs) {
|
||||
self.release();
|
||||
}
|
||||
|
||||
fn getitem_by_idx(&self, i: isize, vm: &VirtualMachine) -> PyResult {
|
||||
if self.desc.ndim() != 1 {
|
||||
return Err(vm.new_not_implemented_error(
|
||||
@@ -281,29 +170,6 @@ impl PyMemoryView {
|
||||
format_unpack(&self.format_spec, &bytes[pos..pos + self.desc.itemsize], vm)
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn getitem(zelf: PyRef<Self>, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
zelf.try_not_released(vm)?;
|
||||
if zelf.desc.ndim() == 0 {
|
||||
// 0-d memoryview can be referenced using mv[...] or mv[()] only
|
||||
if needle.is(&vm.ctx.ellipsis) {
|
||||
return Ok(zelf.into());
|
||||
}
|
||||
if let Some(tuple) = needle.payload::<PyTuple>() {
|
||||
if tuple.is_empty() {
|
||||
return zelf.unpack_single(0, vm);
|
||||
}
|
||||
}
|
||||
return Err(vm.new_type_error("invalid indexing of 0-dim memory".to_owned()));
|
||||
}
|
||||
|
||||
match SubscriptNeedle::try_from_object(vm, needle)? {
|
||||
SubscriptNeedle::Index(i) => zelf.getitem_by_idx(i, vm),
|
||||
SubscriptNeedle::Slice(slice) => zelf.getitem_by_slice(&slice, vm),
|
||||
SubscriptNeedle::MultiIndex(indices) => zelf.getitem_by_multi_idx(&indices, vm),
|
||||
}
|
||||
}
|
||||
|
||||
fn setitem_by_idx(&self, i: isize, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
if self.desc.ndim() != 1 {
|
||||
return Err(vm.new_not_implemented_error("sub-views are not implemented".to_owned()));
|
||||
@@ -317,60 +183,6 @@ impl PyMemoryView {
|
||||
self.pack_single(pos, value, vm)
|
||||
}
|
||||
|
||||
fn setitem_by_slice(
|
||||
zelf: PyRef<Self>,
|
||||
slice: &PySlice,
|
||||
src: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
if zelf.desc.ndim() != 1 {
|
||||
return Err(vm.new_not_implemented_error("sub-view are not implemented".to_owned()));
|
||||
}
|
||||
|
||||
let mut dest = zelf.new_view();
|
||||
dest.init_slice(slice, 0, vm)?;
|
||||
dest.init_len();
|
||||
|
||||
if zelf.is(&src) {
|
||||
return if !is_equiv_structure(&zelf.desc, &dest.desc) {
|
||||
Err(vm.new_value_error(
|
||||
"memoryview assignment: lvalue and rvalue have different structures".to_owned(),
|
||||
))
|
||||
} else {
|
||||
// assign self[:] to self
|
||||
Ok(())
|
||||
};
|
||||
};
|
||||
|
||||
let src = if let Some(src) = src.downcast_ref::<PyMemoryView>() {
|
||||
if zelf.buffer.obj.is(&src.buffer.obj) {
|
||||
src.to_contiguous(vm)
|
||||
} else {
|
||||
AsBuffer::as_buffer(src, vm)?
|
||||
}
|
||||
} else {
|
||||
PyBuffer::try_from_object(vm, src)?
|
||||
};
|
||||
|
||||
if !is_equiv_structure(&src.desc, &dest.desc) {
|
||||
return Err(vm.new_value_error(
|
||||
"memoryview assignment: lvalue and rvalue have different structures".to_owned(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut bytes_mut = dest.buffer.obj_bytes_mut();
|
||||
let src_bytes = src.obj_bytes();
|
||||
dest.desc.zip_eq(&src.desc, true, |a_range, b_range| {
|
||||
let a_range = (a_range.start + dest.start as isize) as usize
|
||||
..(a_range.end + dest.start as isize) as usize;
|
||||
let b_range = b_range.start as usize..b_range.end as usize;
|
||||
bytes_mut[a_range].copy_from_slice(&src_bytes[b_range]);
|
||||
false
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setitem_by_multi_idx(
|
||||
&self,
|
||||
indexes: &[isize],
|
||||
@@ -381,47 +193,6 @@ impl PyMemoryView {
|
||||
self.pack_single(pos, value, vm)
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn delitem(&self, _needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
if self.desc.readonly {
|
||||
return Err(vm.new_type_error("cannot modify read-only memory".to_owned()));
|
||||
}
|
||||
Err(vm.new_type_error("cannot delete memory".to_owned()))
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn setitem(
|
||||
zelf: PyRef<Self>,
|
||||
needle: PyObjectRef,
|
||||
value: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
zelf.try_not_released(vm)?;
|
||||
if zelf.desc.readonly {
|
||||
return Err(vm.new_type_error("cannot modify read-only memory".to_owned()));
|
||||
}
|
||||
if value.is(&vm.ctx.none) {
|
||||
return Err(vm.new_type_error("cannot delete memory".to_owned()));
|
||||
}
|
||||
|
||||
if zelf.desc.ndim() == 0 {
|
||||
// TODO: merge branches when we got conditional if let
|
||||
if needle.is(&vm.ctx.ellipsis) {
|
||||
return zelf.pack_single(0, value, vm);
|
||||
} else if let Some(tuple) = needle.payload::<PyTuple>() {
|
||||
if tuple.is_empty() {
|
||||
return zelf.pack_single(0, value, vm);
|
||||
}
|
||||
}
|
||||
return Err(vm.new_type_error("invalid indexing of 0-dim memory".to_owned()));
|
||||
}
|
||||
match SubscriptNeedle::try_from_object(vm, needle)? {
|
||||
SubscriptNeedle::Index(i) => zelf.setitem_by_idx(i, value, vm),
|
||||
SubscriptNeedle::Slice(slice) => Self::setitem_by_slice(zelf, &slice, value, vm),
|
||||
SubscriptNeedle::MultiIndex(indices) => zelf.setitem_by_multi_idx(&indices, value, vm),
|
||||
}
|
||||
}
|
||||
|
||||
fn pack_single(&self, pos: usize, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let mut bytes = self.buffer.obj_bytes_mut();
|
||||
// TODO: Optimize
|
||||
@@ -522,26 +293,6 @@ impl PyMemoryView {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// return the length of the first dimension
|
||||
#[pymethod(magic)]
|
||||
fn len(&self, vm: &VirtualMachine) -> PyResult<usize> {
|
||||
self.try_not_released(vm)?;
|
||||
Ok(if self.desc.ndim() == 0 {
|
||||
1
|
||||
} else {
|
||||
// shape for dim[0]
|
||||
self.desc.dim_desc[0].0
|
||||
})
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn tobytes(&self, vm: &VirtualMachine) -> PyResult<PyBytesRef> {
|
||||
self.try_not_released(vm)?;
|
||||
let mut v = vec![];
|
||||
self.append_to(&mut v);
|
||||
Ok(PyBytes::from(v).into_ref(vm))
|
||||
}
|
||||
|
||||
fn _to_list(
|
||||
&self,
|
||||
bytes: &[u8],
|
||||
@@ -572,6 +323,389 @@ impl PyMemoryView {
|
||||
Ok(vm.ctx.new_list(v))
|
||||
}
|
||||
|
||||
fn eq(zelf: &Py<Self>, other: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
if zelf.is(other) {
|
||||
return Ok(true);
|
||||
}
|
||||
if zelf.released.load() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if let Some(other) = other.payload::<Self>() {
|
||||
if other.released.load() {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
let other = match PyBuffer::try_from_borrowed_object(vm, other) {
|
||||
Ok(buf) => buf,
|
||||
Err(_) => return Ok(false),
|
||||
};
|
||||
|
||||
if !is_equiv_shape(&zelf.desc, &other.desc) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let a_itemsize = zelf.desc.itemsize;
|
||||
let b_itemsize = other.desc.itemsize;
|
||||
let a_format_spec = &zelf.format_spec;
|
||||
let b_format_spec = &Self::parse_format(&other.desc.format, vm)?;
|
||||
|
||||
if zelf.desc.ndim() == 0 {
|
||||
let a_val = format_unpack(a_format_spec, &zelf.buffer.obj_bytes()[..a_itemsize], vm)?;
|
||||
let b_val = format_unpack(b_format_spec, &other.obj_bytes()[..b_itemsize], vm)?;
|
||||
return vm.bool_eq(&a_val, &b_val);
|
||||
}
|
||||
|
||||
// TODO: optimize cmp by format
|
||||
let mut ret = Ok(true);
|
||||
let a_bytes = zelf.buffer.obj_bytes();
|
||||
let b_bytes = other.obj_bytes();
|
||||
zelf.desc.zip_eq(&other.desc, false, |a_range, b_range| {
|
||||
let a_range = (a_range.start + zelf.start as isize) as usize
|
||||
..(a_range.end + zelf.start as isize) as usize;
|
||||
let b_range = b_range.start as usize..b_range.end as usize;
|
||||
let a_val = match format_unpack(a_format_spec, &a_bytes[a_range], vm) {
|
||||
Ok(val) => val,
|
||||
Err(e) => {
|
||||
ret = Err(e);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
let b_val = match format_unpack(b_format_spec, &b_bytes[b_range], vm) {
|
||||
Ok(val) => val,
|
||||
Err(e) => {
|
||||
ret = Err(e);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
ret = vm.bool_eq(&a_val, &b_val);
|
||||
if let Ok(b) = ret {
|
||||
!b
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
ret
|
||||
}
|
||||
|
||||
fn obj_bytes(&self) -> BorrowedValue<[u8]> {
|
||||
if self.desc.is_contiguous() {
|
||||
BorrowedValue::map(self.buffer.obj_bytes(), |x| {
|
||||
&x[self.start..self.start + self.desc.len]
|
||||
})
|
||||
} else {
|
||||
BorrowedValue::map(self.buffer.obj_bytes(), |x| &x[self.start..])
|
||||
}
|
||||
}
|
||||
|
||||
fn obj_bytes_mut(&self) -> BorrowedValueMut<[u8]> {
|
||||
if self.desc.is_contiguous() {
|
||||
BorrowedValueMut::map(self.buffer.obj_bytes_mut(), |x| {
|
||||
&mut x[self.start..self.start + self.desc.len]
|
||||
})
|
||||
} else {
|
||||
BorrowedValueMut::map(self.buffer.obj_bytes_mut(), |x| &mut x[self.start..])
|
||||
}
|
||||
}
|
||||
|
||||
fn as_contiguous(&self) -> Option<BorrowedValue<[u8]>> {
|
||||
self.desc.is_contiguous().then(|| {
|
||||
BorrowedValue::map(self.buffer.obj_bytes(), |x| {
|
||||
&x[self.start..self.start + self.desc.len]
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn _as_contiguous_mut(&self) -> Option<BorrowedValueMut<[u8]>> {
|
||||
self.desc.is_contiguous().then(|| {
|
||||
BorrowedValueMut::map(self.buffer.obj_bytes_mut(), |x| {
|
||||
&mut x[self.start..self.start + self.desc.len]
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn append_to(&self, buf: &mut Vec<u8>) {
|
||||
if let Some(bytes) = self.as_contiguous() {
|
||||
buf.extend_from_slice(&bytes);
|
||||
} else {
|
||||
buf.reserve(self.desc.len);
|
||||
let bytes = &*self.buffer.obj_bytes();
|
||||
self.desc.for_each_segment(true, |range| {
|
||||
let start = (range.start + self.start as isize) as usize;
|
||||
let end = (range.end + self.start as isize) as usize;
|
||||
buf.extend_from_slice(&bytes[start..end]);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn contiguous_or_collect<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
let borrowed;
|
||||
let mut collected;
|
||||
let v = if let Some(bytes) = self.as_contiguous() {
|
||||
borrowed = bytes;
|
||||
&*borrowed
|
||||
} else {
|
||||
collected = vec![];
|
||||
self.append_to(&mut collected);
|
||||
&collected
|
||||
};
|
||||
f(v)
|
||||
}
|
||||
|
||||
/// clone data from memoryview
|
||||
/// keep the shape, convert to contiguous
|
||||
pub fn to_contiguous(&self, vm: &VirtualMachine) -> PyBuffer {
|
||||
let mut data = vec![];
|
||||
self.append_to(&mut data);
|
||||
|
||||
if self.desc.ndim() == 0 {
|
||||
return VecBuffer::from(data)
|
||||
.into_ref(vm)
|
||||
.into_pybuffer_with_descriptor(self.desc.clone());
|
||||
}
|
||||
|
||||
let mut dim_desc = self.desc.dim_desc.clone();
|
||||
dim_desc.last_mut().unwrap().1 = self.desc.itemsize as isize;
|
||||
dim_desc.last_mut().unwrap().2 = 0;
|
||||
for i in (0..dim_desc.len() - 1).rev() {
|
||||
dim_desc[i].1 = dim_desc[i + 1].1 * dim_desc[i + 1].0 as isize;
|
||||
dim_desc[i].2 = 0;
|
||||
}
|
||||
|
||||
let desc = BufferDescriptor {
|
||||
len: self.desc.len,
|
||||
readonly: self.desc.readonly,
|
||||
itemsize: self.desc.itemsize,
|
||||
format: self.desc.format.clone(),
|
||||
dim_desc,
|
||||
};
|
||||
|
||||
VecBuffer::from(data)
|
||||
.into_ref(vm)
|
||||
.into_pybuffer_with_descriptor(desc)
|
||||
}
|
||||
}
|
||||
|
||||
impl Py<PyMemoryView> {
|
||||
fn setitem_by_slice(
|
||||
&self,
|
||||
slice: &PySlice,
|
||||
src: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
if self.desc.ndim() != 1 {
|
||||
return Err(vm.new_not_implemented_error("sub-view are not implemented".to_owned()));
|
||||
}
|
||||
|
||||
let mut dest = self.new_view();
|
||||
dest.init_slice(slice, 0, vm)?;
|
||||
dest.init_len();
|
||||
|
||||
if self.is(&src) {
|
||||
return if !is_equiv_structure(&self.desc, &dest.desc) {
|
||||
Err(vm.new_value_error(
|
||||
"memoryview assignment: lvalue and rvalue have different structures".to_owned(),
|
||||
))
|
||||
} else {
|
||||
// assign self[:] to self
|
||||
Ok(())
|
||||
};
|
||||
};
|
||||
|
||||
let src = if let Some(src) = src.downcast_ref::<PyMemoryView>() {
|
||||
if self.buffer.obj.is(&src.buffer.obj) {
|
||||
src.to_contiguous(vm)
|
||||
} else {
|
||||
AsBuffer::as_buffer(src, vm)?
|
||||
}
|
||||
} else {
|
||||
PyBuffer::try_from_object(vm, src)?
|
||||
};
|
||||
|
||||
if !is_equiv_structure(&src.desc, &dest.desc) {
|
||||
return Err(vm.new_value_error(
|
||||
"memoryview assignment: lvalue and rvalue have different structures".to_owned(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut bytes_mut = dest.buffer.obj_bytes_mut();
|
||||
let src_bytes = src.obj_bytes();
|
||||
dest.desc.zip_eq(&src.desc, true, |a_range, b_range| {
|
||||
let a_range = (a_range.start + dest.start as isize) as usize
|
||||
..(a_range.end + dest.start as isize) as usize;
|
||||
let b_range = b_range.start as usize..b_range.end as usize;
|
||||
bytes_mut[a_range].copy_from_slice(&src_bytes[b_range]);
|
||||
false
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(with(
|
||||
Py,
|
||||
Hashable,
|
||||
Comparable,
|
||||
AsBuffer,
|
||||
AsMapping,
|
||||
AsSequence,
|
||||
Constructor,
|
||||
Iterable,
|
||||
Representable
|
||||
))]
|
||||
impl PyMemoryView {
|
||||
#[pymethod]
|
||||
pub fn release(&self) {
|
||||
if self.released.compare_exchange(false, true).is_ok() {
|
||||
self.buffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn obj(&self, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
|
||||
self.try_not_released(vm).map(|_| self.buffer.obj.clone())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn nbytes(&self, vm: &VirtualMachine) -> PyResult<usize> {
|
||||
self.try_not_released(vm).map(|_| self.desc.len)
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn readonly(&self, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
self.try_not_released(vm).map(|_| self.desc.readonly)
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn itemsize(&self, vm: &VirtualMachine) -> PyResult<usize> {
|
||||
self.try_not_released(vm).map(|_| self.desc.itemsize)
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn ndim(&self, vm: &VirtualMachine) -> PyResult<usize> {
|
||||
self.try_not_released(vm).map(|_| self.desc.ndim())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn shape(&self, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
|
||||
self.try_not_released(vm)?;
|
||||
Ok(vm.ctx.new_tuple(
|
||||
self.desc
|
||||
.dim_desc
|
||||
.iter()
|
||||
.map(|(shape, _, _)| shape.to_pyobject(vm))
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn strides(&self, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
|
||||
self.try_not_released(vm)?;
|
||||
Ok(vm.ctx.new_tuple(
|
||||
self.desc
|
||||
.dim_desc
|
||||
.iter()
|
||||
.map(|(_, stride, _)| stride.to_pyobject(vm))
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn suboffsets(&self, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
|
||||
self.try_not_released(vm)?;
|
||||
Ok(vm.ctx.new_tuple(
|
||||
self.desc
|
||||
.dim_desc
|
||||
.iter()
|
||||
.map(|(_, _, suboffset)| suboffset.to_pyobject(vm))
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn format(&self, vm: &VirtualMachine) -> PyResult<PyStr> {
|
||||
self.try_not_released(vm)
|
||||
.map(|_| PyStr::from(self.desc.format.clone()))
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn contiguous(&self, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
self.try_not_released(vm).map(|_| self.desc.is_contiguous())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn c_contiguous(&self, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
self.try_not_released(vm).map(|_| self.desc.is_contiguous())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn f_contiguous(&self, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
// TODO: fortain order
|
||||
self.try_not_released(vm)
|
||||
.map(|_| self.desc.ndim() <= 1 && self.desc.is_contiguous())
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn enter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
|
||||
zelf.try_not_released(vm).map(|_| zelf)
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn exit(&self, _args: FuncArgs) {
|
||||
self.release();
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn getitem(zelf: PyRef<Self>, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
zelf.try_not_released(vm)?;
|
||||
if zelf.desc.ndim() == 0 {
|
||||
// 0-d memoryview can be referenced using mv[...] or mv[()] only
|
||||
if needle.is(&vm.ctx.ellipsis) {
|
||||
return Ok(zelf.into());
|
||||
}
|
||||
if let Some(tuple) = needle.payload::<PyTuple>() {
|
||||
if tuple.is_empty() {
|
||||
return zelf.unpack_single(0, vm);
|
||||
}
|
||||
}
|
||||
return Err(vm.new_type_error("invalid indexing of 0-dim memory".to_owned()));
|
||||
}
|
||||
|
||||
match SubscriptNeedle::try_from_object(vm, needle)? {
|
||||
SubscriptNeedle::Index(i) => zelf.getitem_by_idx(i, vm),
|
||||
SubscriptNeedle::Slice(slice) => zelf.getitem_by_slice(&slice, vm),
|
||||
SubscriptNeedle::MultiIndex(indices) => zelf.getitem_by_multi_idx(&indices, vm),
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn delitem(&self, _needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
if self.desc.readonly {
|
||||
return Err(vm.new_type_error("cannot modify read-only memory".to_owned()));
|
||||
}
|
||||
Err(vm.new_type_error("cannot delete memory".to_owned()))
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn len(&self, vm: &VirtualMachine) -> PyResult<usize> {
|
||||
self.try_not_released(vm)?;
|
||||
Ok(if self.desc.ndim() == 0 {
|
||||
1
|
||||
} else {
|
||||
// shape for dim[0]
|
||||
self.desc.dim_desc[0].0
|
||||
})
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn tobytes(&self, vm: &VirtualMachine) -> PyResult<PyBytesRef> {
|
||||
self.try_not_released(vm)?;
|
||||
let mut v = vec![];
|
||||
self.append_to(&mut v);
|
||||
Ok(PyBytes::from(v).into_ref(vm))
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn tolist(&self, vm: &VirtualMachine) -> PyResult<PyListRef> {
|
||||
self.try_not_released(vm)?;
|
||||
@@ -714,178 +848,51 @@ impl PyMemoryView {
|
||||
Ok(self.cast_to_1d(format, vm)?.into_ref(vm))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn eq(zelf: &Py<Self>, other: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
if zelf.is(other) {
|
||||
return Ok(true);
|
||||
}
|
||||
if zelf.released.load() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if let Some(other) = other.payload::<Self>() {
|
||||
if other.released.load() {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
let other = match PyBuffer::try_from_borrowed_object(vm, other) {
|
||||
Ok(buf) => buf,
|
||||
Err(_) => return Ok(false),
|
||||
};
|
||||
|
||||
if !is_equiv_shape(&zelf.desc, &other.desc) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let a_itemsize = zelf.desc.itemsize;
|
||||
let b_itemsize = other.desc.itemsize;
|
||||
let a_format_spec = &zelf.format_spec;
|
||||
let b_format_spec = &Self::parse_format(&other.desc.format, vm)?;
|
||||
|
||||
if zelf.desc.ndim() == 0 {
|
||||
let a_val = format_unpack(a_format_spec, &zelf.buffer.obj_bytes()[..a_itemsize], vm)?;
|
||||
let b_val = format_unpack(b_format_spec, &other.obj_bytes()[..b_itemsize], vm)?;
|
||||
return vm.bool_eq(&a_val, &b_val);
|
||||
}
|
||||
|
||||
// TODO: optimize cmp by format
|
||||
let mut ret = Ok(true);
|
||||
let a_bytes = zelf.buffer.obj_bytes();
|
||||
let b_bytes = other.obj_bytes();
|
||||
zelf.desc.zip_eq(&other.desc, false, |a_range, b_range| {
|
||||
let a_range = (a_range.start + zelf.start as isize) as usize
|
||||
..(a_range.end + zelf.start as isize) as usize;
|
||||
let b_range = b_range.start as usize..b_range.end as usize;
|
||||
let a_val = match format_unpack(a_format_spec, &a_bytes[a_range], vm) {
|
||||
Ok(val) => val,
|
||||
Err(e) => {
|
||||
ret = Err(e);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
let b_val = match format_unpack(b_format_spec, &b_bytes[b_range], vm) {
|
||||
Ok(val) => val,
|
||||
Err(e) => {
|
||||
ret = Err(e);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
ret = vm.bool_eq(&a_val, &b_val);
|
||||
if let Ok(b) = ret {
|
||||
!b
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
ret
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
impl Py<PyMemoryView> {
|
||||
#[pymethod(magic)]
|
||||
fn reduce_ex(zelf: PyRef<Self>, _proto: usize, vm: &VirtualMachine) -> PyResult {
|
||||
Self::reduce(zelf, vm)
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn reduce(_zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
|
||||
Err(vm.new_type_error("cannot pickle 'memoryview' object".to_owned()))
|
||||
}
|
||||
|
||||
fn obj_bytes(&self) -> BorrowedValue<[u8]> {
|
||||
if self.desc.is_contiguous() {
|
||||
BorrowedValue::map(self.buffer.obj_bytes(), |x| {
|
||||
&x[self.start..self.start + self.desc.len]
|
||||
})
|
||||
} else {
|
||||
BorrowedValue::map(self.buffer.obj_bytes(), |x| &x[self.start..])
|
||||
fn setitem(
|
||||
&self,
|
||||
needle: PyObjectRef,
|
||||
value: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
self.try_not_released(vm)?;
|
||||
if self.desc.readonly {
|
||||
return Err(vm.new_type_error("cannot modify read-only memory".to_owned()));
|
||||
}
|
||||
}
|
||||
|
||||
fn obj_bytes_mut(&self) -> BorrowedValueMut<[u8]> {
|
||||
if self.desc.is_contiguous() {
|
||||
BorrowedValueMut::map(self.buffer.obj_bytes_mut(), |x| {
|
||||
&mut x[self.start..self.start + self.desc.len]
|
||||
})
|
||||
} else {
|
||||
BorrowedValueMut::map(self.buffer.obj_bytes_mut(), |x| &mut x[self.start..])
|
||||
if value.is(&vm.ctx.none) {
|
||||
return Err(vm.new_type_error("cannot delete memory".to_owned()));
|
||||
}
|
||||
}
|
||||
|
||||
fn as_contiguous(&self) -> Option<BorrowedValue<[u8]>> {
|
||||
self.desc.is_contiguous().then(|| {
|
||||
BorrowedValue::map(self.buffer.obj_bytes(), |x| {
|
||||
&x[self.start..self.start + self.desc.len]
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn _as_contiguous_mut(&self) -> Option<BorrowedValueMut<[u8]>> {
|
||||
self.desc.is_contiguous().then(|| {
|
||||
BorrowedValueMut::map(self.buffer.obj_bytes_mut(), |x| {
|
||||
&mut x[self.start..self.start + self.desc.len]
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn append_to(&self, buf: &mut Vec<u8>) {
|
||||
if let Some(bytes) = self.as_contiguous() {
|
||||
buf.extend_from_slice(&bytes);
|
||||
} else {
|
||||
buf.reserve(self.desc.len);
|
||||
let bytes = &*self.buffer.obj_bytes();
|
||||
self.desc.for_each_segment(true, |range| {
|
||||
let start = (range.start + self.start as isize) as usize;
|
||||
let end = (range.end + self.start as isize) as usize;
|
||||
buf.extend_from_slice(&bytes[start..end]);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn contiguous_or_collect<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
let borrowed;
|
||||
let mut collected;
|
||||
let v = if let Some(bytes) = self.as_contiguous() {
|
||||
borrowed = bytes;
|
||||
&*borrowed
|
||||
} else {
|
||||
collected = vec![];
|
||||
self.append_to(&mut collected);
|
||||
&collected
|
||||
};
|
||||
f(v)
|
||||
}
|
||||
|
||||
/// clone data from memoryview
|
||||
/// keep the shape, convert to contiguous
|
||||
pub fn to_contiguous(&self, vm: &VirtualMachine) -> PyBuffer {
|
||||
let mut data = vec![];
|
||||
self.append_to(&mut data);
|
||||
|
||||
if self.desc.ndim() == 0 {
|
||||
return VecBuffer::from(data)
|
||||
.into_ref(vm)
|
||||
.into_pybuffer_with_descriptor(self.desc.clone());
|
||||
// TODO: merge branches when we got conditional if let
|
||||
if needle.is(&vm.ctx.ellipsis) {
|
||||
return self.pack_single(0, value, vm);
|
||||
} else if let Some(tuple) = needle.payload::<PyTuple>() {
|
||||
if tuple.is_empty() {
|
||||
return self.pack_single(0, value, vm);
|
||||
}
|
||||
}
|
||||
return Err(vm.new_type_error("invalid indexing of 0-dim memory".to_owned()));
|
||||
}
|
||||
|
||||
let mut dim_desc = self.desc.dim_desc.clone();
|
||||
dim_desc.last_mut().unwrap().1 = self.desc.itemsize as isize;
|
||||
dim_desc.last_mut().unwrap().2 = 0;
|
||||
for i in (0..dim_desc.len() - 1).rev() {
|
||||
dim_desc[i].1 = dim_desc[i + 1].1 * dim_desc[i + 1].0 as isize;
|
||||
dim_desc[i].2 = 0;
|
||||
match SubscriptNeedle::try_from_object(vm, needle)? {
|
||||
SubscriptNeedle::Index(i) => self.setitem_by_idx(i, value, vm),
|
||||
SubscriptNeedle::Slice(slice) => self.setitem_by_slice(&slice, value, vm),
|
||||
SubscriptNeedle::MultiIndex(indices) => self.setitem_by_multi_idx(&indices, value, vm),
|
||||
}
|
||||
}
|
||||
|
||||
let desc = BufferDescriptor {
|
||||
len: self.desc.len,
|
||||
readonly: self.desc.readonly,
|
||||
itemsize: self.desc.itemsize,
|
||||
format: self.desc.format.clone(),
|
||||
dim_desc,
|
||||
};
|
||||
#[pymethod(magic)]
|
||||
fn reduce_ex(&self, _proto: usize, vm: &VirtualMachine) -> PyResult {
|
||||
self.reduce(vm)
|
||||
}
|
||||
|
||||
VecBuffer::from(data)
|
||||
.into_ref(vm)
|
||||
.into_pybuffer_with_descriptor(desc)
|
||||
#[pymethod(magic)]
|
||||
fn reduce(&self, vm: &VirtualMachine) -> PyResult {
|
||||
Err(vm.new_type_error("cannot pickle 'memoryview' object".to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -977,7 +984,7 @@ impl AsMapping for PyMemoryView {
|
||||
ass_subscript: atomic_func!(|mapping, needle, value, vm| {
|
||||
let zelf = PyMemoryView::mapping_downcast(mapping);
|
||||
if let Some(value) = value {
|
||||
PyMemoryView::setitem(zelf.to_owned(), needle.to_owned(), value, vm)
|
||||
zelf.setitem(needle.to_owned(), value, vm)
|
||||
} else {
|
||||
Err(vm.new_type_error("cannot delete memory".to_owned()))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user