diff --git a/vm/src/builtins/memory.rs b/vm/src/builtins/memory.rs index 81576b5985..0b944eb4ca 100644 --- a/vm/src/builtins/memory.rs +++ b/vm/src/builtins/memory.rs @@ -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::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 { - self.try_not_released(vm).map(|_| self.buffer.obj.clone()) - } - - #[pygetset] - fn nbytes(&self, vm: &VirtualMachine) -> PyResult { - self.try_not_released(vm).map(|_| self.desc.len) - } - - #[pygetset] - fn readonly(&self, vm: &VirtualMachine) -> PyResult { - self.try_not_released(vm).map(|_| self.desc.readonly) - } - - #[pygetset] - fn itemsize(&self, vm: &VirtualMachine) -> PyResult { - self.try_not_released(vm).map(|_| self.desc.itemsize) - } - - #[pygetset] - fn ndim(&self, vm: &VirtualMachine) -> PyResult { - self.try_not_released(vm).map(|_| self.desc.ndim()) - } - - #[pygetset] - fn shape(&self, vm: &VirtualMachine) -> PyResult { - 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 { - 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 { - 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 { - self.try_not_released(vm) - .map(|_| PyStr::from(self.desc.format.clone())) - } - - #[pygetset] - fn contiguous(&self, vm: &VirtualMachine) -> PyResult { - self.try_not_released(vm).map(|_| self.desc.is_contiguous()) - } - - #[pygetset] - fn c_contiguous(&self, vm: &VirtualMachine) -> PyResult { - self.try_not_released(vm).map(|_| self.desc.is_contiguous()) - } - - #[pygetset] - fn f_contiguous(&self, vm: &VirtualMachine) -> PyResult { - // TODO: fortain order - self.try_not_released(vm) - .map(|_| self.desc.ndim() <= 1 && self.desc.is_contiguous()) - } - - #[pymethod(magic)] - fn enter(zelf: PyRef, vm: &VirtualMachine) -> PyResult> { - 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, 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::() { - 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, - 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::() { - 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, - 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::() { - 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 { - 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 { - 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, other: &PyObject, vm: &VirtualMachine) -> PyResult { + if zelf.is(other) { + return Ok(true); + } + if zelf.released.load() { + return Ok(false); + } + + if let Some(other) = other.payload::() { + 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> { + 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> { + 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) { + 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>(&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 { + 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::() { + 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 { + self.try_not_released(vm).map(|_| self.buffer.obj.clone()) + } + + #[pygetset] + fn nbytes(&self, vm: &VirtualMachine) -> PyResult { + self.try_not_released(vm).map(|_| self.desc.len) + } + + #[pygetset] + fn readonly(&self, vm: &VirtualMachine) -> PyResult { + self.try_not_released(vm).map(|_| self.desc.readonly) + } + + #[pygetset] + fn itemsize(&self, vm: &VirtualMachine) -> PyResult { + self.try_not_released(vm).map(|_| self.desc.itemsize) + } + + #[pygetset] + fn ndim(&self, vm: &VirtualMachine) -> PyResult { + self.try_not_released(vm).map(|_| self.desc.ndim()) + } + + #[pygetset] + fn shape(&self, vm: &VirtualMachine) -> PyResult { + 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 { + 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 { + 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 { + self.try_not_released(vm) + .map(|_| PyStr::from(self.desc.format.clone())) + } + + #[pygetset] + fn contiguous(&self, vm: &VirtualMachine) -> PyResult { + self.try_not_released(vm).map(|_| self.desc.is_contiguous()) + } + + #[pygetset] + fn c_contiguous(&self, vm: &VirtualMachine) -> PyResult { + self.try_not_released(vm).map(|_| self.desc.is_contiguous()) + } + + #[pygetset] + fn f_contiguous(&self, vm: &VirtualMachine) -> PyResult { + // TODO: fortain order + self.try_not_released(vm) + .map(|_| self.desc.ndim() <= 1 && self.desc.is_contiguous()) + } + + #[pymethod(magic)] + fn enter(zelf: PyRef, vm: &VirtualMachine) -> PyResult> { + zelf.try_not_released(vm).map(|_| zelf) + } + + #[pymethod(magic)] + fn exit(&self, _args: FuncArgs) { + self.release(); + } + + #[pymethod(magic)] + fn getitem(zelf: PyRef, 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::() { + 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 { + 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 { + 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 { 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, other: &PyObject, vm: &VirtualMachine) -> PyResult { - if zelf.is(other) { - return Ok(true); - } - if zelf.released.load() { - return Ok(false); - } - - if let Some(other) = other.payload::() { - 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 { #[pymethod(magic)] - fn reduce_ex(zelf: PyRef, _proto: usize, vm: &VirtualMachine) -> PyResult { - Self::reduce(zelf, vm) - } - - #[pymethod(magic)] - fn reduce(_zelf: PyRef, 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> { - 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> { - 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) { - 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>(&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::() { + 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())) }