Skip to content

Commit e5a7679

Browse files
killme2008youknowone
authored andcommitted
Impl AsBuffer, AsMapping and AsSequence protocols for PyMmap
1 parent 929ad30 commit e5a7679

File tree

2 files changed

+241
-7
lines changed

2 files changed

+241
-7
lines changed

stdlib/src/mmap.rs

Lines changed: 240 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,20 @@ pub(crate) use mmap::make_module;
33

44
#[pymodule]
55
mod mmap {
6-
use crate::common::lock::{PyMutex, PyMutexGuard};
6+
use crate::common::{
7+
borrow::{BorrowedValue, BorrowedValueMut},
8+
lock::{MapImmutable, PyMutex, PyMutexGuard},
9+
};
710
use crate::vm::{
811
builtins::{PyBytes, PyBytesRef, PyInt, PyIntRef, PyTypeRef},
9-
byte::value_from_object,
12+
byte::{bytes_from_object, value_from_object},
1013
function::{ArgBytesLike, FuncArgs, OptionalArg},
11-
sliceable::saturate_index,
12-
types::Constructor,
13-
AsObject, FromArgs, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
14+
protocol::{
15+
BufferDescriptor, BufferMethods, PyBuffer, PyMappingMethods, PySequenceMethods,
16+
},
17+
sliceable::{saturate_index, wrap_index, SaturatedSlice, SequenceIndex},
18+
types::{AsBuffer, AsMapping, AsSequence, Constructor},
19+
AsObject, FromArgs, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
1420
TryFromBorrowedObject, VirtualMachine,
1521
};
1622
use crossbeam_utils::atomic::AtomicCell;
@@ -335,8 +341,87 @@ mod mmap {
335341
}
336342
}
337343

338-
#[pyimpl(with(Constructor), flags(BASETYPE))]
344+
static BUFFER_METHODS: BufferMethods = BufferMethods {
345+
obj_bytes: |buffer| buffer.obj_as::<PyMmap>().as_bytes(),
346+
obj_bytes_mut: |buffer| buffer.obj_as::<PyMmap>().as_bytes_mut(),
347+
release: |buffer| {
348+
buffer.obj_as::<PyMmap>().exports.fetch_sub(1);
349+
},
350+
retain: |buffer| {
351+
buffer.obj_as::<PyMmap>().exports.fetch_add(1);
352+
},
353+
};
354+
355+
impl AsBuffer for PyMmap {
356+
fn as_buffer(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyBuffer> {
357+
let buf = PyBuffer::new(
358+
zelf.to_owned().into(),
359+
BufferDescriptor::simple(zelf.len(), true),
360+
&BUFFER_METHODS,
361+
);
362+
363+
Ok(buf)
364+
}
365+
}
366+
367+
impl AsMapping for PyMmap {
368+
const AS_MAPPING: PyMappingMethods = PyMappingMethods {
369+
length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())),
370+
subscript: Some(|mapping, needle, vm| {
371+
Self::mapping_downcast(mapping)._getitem(needle, vm)
372+
}),
373+
ass_subscript: Some(|mapping, needle, value, vm| {
374+
let zelf = Self::mapping_downcast(mapping);
375+
if let Some(value) = value {
376+
Self::_setitem(zelf.to_owned(), needle, value, vm)
377+
} else {
378+
Err(vm.new_type_error("mmap object doesn't support item deletion".to_owned()))
379+
}
380+
}),
381+
};
382+
}
383+
384+
impl AsSequence for PyMmap {
385+
const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
386+
length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())),
387+
item: Some(|seq, i, vm| {
388+
let zelf = Self::sequence_downcast(seq);
389+
zelf.get_item_by_index(i, vm)
390+
}),
391+
ass_item: Some(|seq, i, value, vm| {
392+
let zelf = Self::sequence_downcast(seq);
393+
if let Some(value) = value {
394+
Self::setitem_by_index(zelf.to_owned(), i, value, vm)
395+
} else {
396+
Err(vm.new_type_error("mmap object doesn't support item deletion".to_owned()))
397+
}
398+
}),
399+
..PySequenceMethods::NOT_IMPLEMENTED
400+
};
401+
}
402+
403+
#[pyimpl(with(Constructor, AsMapping, AsSequence, AsBuffer), flags(BASETYPE))]
339404
impl PyMmap {
405+
fn as_bytes_mut(&self) -> BorrowedValueMut<[u8]> {
406+
PyMutexGuard::map(self.mmap.lock(), |m| {
407+
match m.as_mut().expect("mmap closed or invalid") {
408+
MmapObj::Read(_) => panic!("mmap can't modify a readonly memory map."),
409+
MmapObj::Write(mmap) => &mut mmap[..],
410+
}
411+
})
412+
.into()
413+
}
414+
415+
fn as_bytes(&self) -> BorrowedValue<[u8]> {
416+
PyMutexGuard::map_immutable(self.mmap.lock(), |m| {
417+
match m.as_ref().expect("mmap closed or invalid") {
418+
MmapObj::Read(ref mmap) => &mmap[..],
419+
MmapObj::Write(ref mmap) => &mmap[..],
420+
}
421+
})
422+
.into()
423+
}
424+
340425
#[pymethod(magic)]
341426
pub(crate) fn len(&self) -> usize {
342427
self.inner_size() as usize
@@ -789,6 +874,155 @@ mod mmap {
789874
Ok(())
790875
}
791876

877+
fn get_item_by_index(&self, i: isize, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
878+
let i = wrap_index(i, self.len())
879+
.ok_or_else(|| vm.new_index_error("mmap index out of range".to_owned()))?;
880+
881+
let b = match self.check_valid(vm)?.deref().as_ref().unwrap() {
882+
MmapObj::Read(mmap) => mmap[i as usize],
883+
MmapObj::Write(mmap) => mmap[i as usize],
884+
};
885+
886+
Ok(PyInt::from(b).into_ref(vm).into())
887+
}
888+
889+
fn getitem_by_slice(
890+
&self,
891+
slice: &SaturatedSlice,
892+
vm: &VirtualMachine,
893+
) -> PyResult<PyObjectRef> {
894+
let (range, step, slicelen) = slice.adjust_indices(self.len());
895+
896+
let mmap = self.check_valid(vm)?;
897+
898+
if slicelen == 0 {
899+
return Ok(PyBytes::from(vec![]).into_ref(vm).into());
900+
} else if step == 1 {
901+
let bytes = match mmap.deref().as_ref().unwrap() {
902+
MmapObj::Read(mmap) => &mmap[range],
903+
MmapObj::Write(mmap) => &mmap[range],
904+
};
905+
return Ok(PyBytes::from(bytes.to_vec()).into_ref(vm).into());
906+
}
907+
908+
let mut result_buf = Vec::with_capacity(slicelen);
909+
if step.is_negative() {
910+
for i in range.rev().step_by(step.unsigned_abs()) {
911+
let b = match mmap.deref().as_ref().unwrap() {
912+
MmapObj::Read(mmap) => mmap[i],
913+
MmapObj::Write(mmap) => mmap[i],
914+
};
915+
result_buf.push(b);
916+
}
917+
} else {
918+
for i in range.step_by(step.unsigned_abs()) {
919+
let b = match mmap.deref().as_ref().unwrap() {
920+
MmapObj::Read(mmap) => mmap[i],
921+
MmapObj::Write(mmap) => mmap[i],
922+
};
923+
result_buf.push(b);
924+
}
925+
}
926+
Ok(PyBytes::from(result_buf).into_ref(vm).into())
927+
}
928+
929+
fn _getitem(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
930+
match SequenceIndex::try_from_borrowed_object(vm, needle, "mmap")? {
931+
SequenceIndex::Int(i) => self.get_item_by_index(i, vm),
932+
SequenceIndex::Slice(slice) => self.getitem_by_slice(&slice, vm),
933+
}
934+
}
935+
936+
#[pymethod(magic)]
937+
fn getitem(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
938+
self._getitem(&needle, vm)
939+
}
940+
941+
fn _setitem(
942+
zelf: PyRef<Self>,
943+
needle: &PyObject,
944+
value: PyObjectRef,
945+
vm: &VirtualMachine,
946+
) -> PyResult<()> {
947+
match SequenceIndex::try_from_borrowed_object(vm, needle, "mmap")? {
948+
SequenceIndex::Int(i) => Self::setitem_by_index(zelf, i, value, vm),
949+
SequenceIndex::Slice(slice) => Self::setitem_by_slice(zelf, &slice, value, vm),
950+
}
951+
}
952+
953+
fn setitem_by_index(
954+
zelf: PyRef<Self>,
955+
i: isize,
956+
value: PyObjectRef,
957+
vm: &VirtualMachine,
958+
) -> PyResult<()> {
959+
let i = wrap_index(i, zelf.len())
960+
.ok_or_else(|| vm.new_index_error("mmap index out of range".to_owned()))?;
961+
962+
let b = value_from_object(vm, &value)?;
963+
964+
zelf.try_writable(vm, |mmap| {
965+
mmap[i as usize] = b;
966+
})?;
967+
968+
Ok(())
969+
}
970+
971+
fn setitem_by_slice(
972+
zelf: PyRef<Self>,
973+
slice: &SaturatedSlice,
974+
value: PyObjectRef,
975+
vm: &VirtualMachine,
976+
) -> PyResult<()> {
977+
let (range, step, slicelen) = slice.adjust_indices(zelf.len());
978+
979+
let bytes = bytes_from_object(vm, &value)?;
980+
981+
if bytes.len() != slicelen {
982+
return Err(vm.new_index_error("mmap slice assignment is wrong size".to_owned()));
983+
}
984+
985+
if slicelen == 0 {
986+
// do nothing
987+
Ok(())
988+
} else if step == 1 {
989+
zelf.try_writable(vm, |mmap| {
990+
(&mut mmap[range])
991+
.write(&bytes)
992+
.map_err(|e| vm.new_os_error(e.to_string()))?;
993+
Ok(())
994+
})?
995+
} else {
996+
let mut bi = 0; // bytes index
997+
if step.is_negative() {
998+
for i in range.rev().step_by(step.unsigned_abs()) {
999+
zelf.try_writable(vm, |mmap| {
1000+
mmap[i] = bytes[bi];
1001+
})?;
1002+
bi += 1;
1003+
}
1004+
} else {
1005+
for i in range.step_by(step.unsigned_abs()) {
1006+
zelf.try_writable(vm, |mmap| {
1007+
mmap[i] = bytes[bi];
1008+
})?;
1009+
bi += 1;
1010+
}
1011+
}
1012+
Ok(())
1013+
}
1014+
}
1015+
1016+
#[pymethod(magic)]
1017+
fn setitem(
1018+
zelf: PyRef<Self>,
1019+
needle: PyObjectRef,
1020+
value: PyObjectRef,
1021+
vm: &VirtualMachine,
1022+
) -> PyResult<()> {
1023+
Self::_setitem(zelf, &needle, value, vm)
1024+
}
1025+
7921026
#[pymethod(magic)]
7931027
fn enter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
7941028
let _m = zelf.check_valid(vm)?;

vm/src/sliceable.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ impl SequenceIndex {
292292
}
293293

294294
// Use PySliceableSequence::wrap_index for implementors
295-
pub(crate) fn wrap_index(p: isize, len: usize) -> Option<usize> {
295+
pub fn wrap_index(p: isize, len: usize) -> Option<usize> {
296296
let neg = p.is_negative();
297297
let p = p.wrapping_abs() as usize;
298298
if neg {

0 commit comments

Comments
 (0)