Skip to content

Commit 61bae9a

Browse files
authored
Merge pull request RustPython#2856 from youknowone/buffer-protocol
give an own module for buffer protocol + TryFromBorrowedObject
2 parents 289ddea + 62bff17 commit 61bae9a

27 files changed

+359
-327
lines changed

vm/src/buffer.rs

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
//! Buffer protocol
2+
3+
use crate::common::borrow::{BorrowedValue, BorrowedValueMut};
4+
use crate::common::rc::PyRc;
5+
use crate::PyThreadingConstraint;
6+
use crate::{PyObjectRef, PyResult, TypeProtocol};
7+
use crate::{TryFromBorrowedObject, VirtualMachine};
8+
use std::{borrow::Cow, fmt::Debug, ops::Deref};
9+
10+
pub trait PyBuffer: Debug + PyThreadingConstraint {
11+
fn get_options(&self) -> &BufferOptions;
12+
/// Get the full inner buffer of this memory. You probably want [`as_contiguous()`], as
13+
/// `obj_bytes` doesn't take into account the range a memoryview might operate on, among other
14+
/// footguns.
15+
fn obj_bytes(&self) -> BorrowedValue<[u8]>;
16+
/// Get the full inner buffer of this memory, mutably. You probably want
17+
/// [`as_contiguous_mut()`], as `obj_bytes` doesn't take into account the range a memoryview
18+
/// might operate on, among other footguns.
19+
fn obj_bytes_mut(&self) -> BorrowedValueMut<[u8]>;
20+
fn release(&self);
21+
22+
fn as_contiguous(&self) -> Option<BorrowedValue<[u8]>> {
23+
if !self.get_options().contiguous {
24+
return None;
25+
}
26+
Some(self.obj_bytes())
27+
}
28+
29+
fn as_contiguous_mut(&self) -> Option<BorrowedValueMut<[u8]>> {
30+
if !self.get_options().contiguous {
31+
return None;
32+
}
33+
Some(self.obj_bytes_mut())
34+
}
35+
36+
fn to_contiguous(&self) -> Vec<u8> {
37+
self.obj_bytes().to_vec()
38+
}
39+
}
40+
41+
#[derive(Debug, Clone)]
42+
pub struct BufferOptions {
43+
pub readonly: bool,
44+
pub len: usize,
45+
pub itemsize: usize,
46+
pub contiguous: bool,
47+
pub format: Cow<'static, str>,
48+
// TODO: support multiple dimension array
49+
pub ndim: usize,
50+
pub shape: Vec<usize>,
51+
pub strides: Vec<isize>,
52+
}
53+
54+
impl BufferOptions {
55+
pub const DEFAULT: Self = BufferOptions {
56+
readonly: true,
57+
len: 0,
58+
itemsize: 1,
59+
contiguous: true,
60+
format: Cow::Borrowed("B"),
61+
ndim: 1,
62+
shape: Vec::new(),
63+
strides: Vec::new(),
64+
};
65+
}
66+
67+
impl Default for BufferOptions {
68+
fn default() -> Self {
69+
Self::DEFAULT
70+
}
71+
}
72+
73+
#[derive(Debug)]
74+
pub struct PyBufferRef(Box<dyn PyBuffer>);
75+
76+
impl TryFromBorrowedObject for PyBufferRef {
77+
fn try_from_borrowed_object(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult<Self> {
78+
let obj_cls = obj.class();
79+
for cls in obj_cls.iter_mro() {
80+
if let Some(f) = cls.slots.as_buffer.as_ref() {
81+
return f(obj, vm).map(|x| PyBufferRef(x));
82+
}
83+
}
84+
Err(vm.new_type_error(format!(
85+
"a bytes-like object is required, not '{}'",
86+
obj_cls.name
87+
)))
88+
}
89+
}
90+
91+
impl Drop for PyBufferRef {
92+
fn drop(&mut self) {
93+
self.0.release();
94+
}
95+
}
96+
97+
impl Deref for PyBufferRef {
98+
type Target = dyn PyBuffer;
99+
100+
fn deref(&self) -> &Self::Target {
101+
self.0.deref()
102+
}
103+
}
104+
105+
impl PyBufferRef {
106+
pub fn new(buffer: impl PyBuffer + 'static) -> Self {
107+
Self(Box::new(buffer))
108+
}
109+
110+
pub fn into_rcbuf(self) -> RcBuffer {
111+
// move self.0 out of self; PyBufferRef impls Drop so it's tricky
112+
let this = std::mem::ManuallyDrop::new(self);
113+
let buf_box = unsafe { std::ptr::read(&this.0) };
114+
RcBuffer(buf_box.into())
115+
}
116+
}
117+
118+
impl From<Box<dyn PyBuffer>> for PyBufferRef {
119+
fn from(buffer: Box<dyn PyBuffer>) -> Self {
120+
PyBufferRef(buffer)
121+
}
122+
}
123+
124+
#[derive(Debug, Clone)]
125+
pub struct RcBuffer(PyRc<dyn PyBuffer>);
126+
impl Deref for RcBuffer {
127+
type Target = dyn PyBuffer;
128+
fn deref(&self) -> &Self::Target {
129+
self.0.deref()
130+
}
131+
}
132+
133+
impl Drop for RcBuffer {
134+
fn drop(&mut self) {
135+
// check if this is the last rc before the inner buffer gets dropped
136+
if let Some(buf) = PyRc::get_mut(&mut self.0) {
137+
buf.release()
138+
}
139+
}
140+
}
141+
142+
impl PyBuffer for RcBuffer {
143+
fn get_options(&self) -> &BufferOptions {
144+
self.0.get_options()
145+
}
146+
fn obj_bytes(&self) -> BorrowedValue<[u8]> {
147+
self.0.obj_bytes()
148+
}
149+
fn obj_bytes_mut(&self) -> BorrowedValueMut<[u8]> {
150+
self.0.obj_bytes_mut()
151+
}
152+
fn release(&self) {}
153+
fn as_contiguous(&self) -> Option<BorrowedValue<[u8]>> {
154+
self.0.as_contiguous()
155+
}
156+
fn as_contiguous_mut(&self) -> Option<BorrowedValueMut<[u8]>> {
157+
self.0.as_contiguous_mut()
158+
}
159+
fn to_contiguous(&self) -> Vec<u8> {
160+
self.0.to_contiguous()
161+
}
162+
}
163+
164+
pub(crate) trait ResizeGuard<'a> {
165+
type Resizable: 'a;
166+
fn try_resizable(&'a self, vm: &VirtualMachine) -> PyResult<Self::Resizable>;
167+
}

vm/src/builtins/bytearray.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,25 @@
22
use super::bytes::{PyBytes, PyBytesRef};
33
use super::dict::PyDictRef;
44
use super::int::PyIntRef;
5-
use super::memory::{BufferOptions, PyBuffer, ResizeGuard};
65
use super::pystr::PyStrRef;
76
use super::pytype::PyTypeRef;
87
use super::tuple::PyTupleRef;
98
use crate::anystr::{self, AnyStr};
9+
use crate::buffer::{BufferOptions, PyBuffer, ResizeGuard};
1010
use crate::bytesinner::{
1111
bytes_decode, bytes_from_object, value_from_object, ByteInnerFindOptions, ByteInnerNewOptions,
1212
ByteInnerPaddingOptions, ByteInnerSplitOptions, ByteInnerTranslateOptions, DecodeArgs,
1313
PyBytesInner,
1414
};
15-
use crate::byteslike::PyBytesLike;
15+
use crate::byteslike::ArgBytesLike;
1616
use crate::common::borrow::{BorrowedValue, BorrowedValueMut};
1717
use crate::common::lock::{
1818
PyMappedRwLockReadGuard, PyMappedRwLockWriteGuard, PyRwLock, PyRwLockReadGuard,
1919
PyRwLockWriteGuard,
2020
};
2121
use crate::function::{FuncArgs, OptionalArg, OptionalOption};
2222
use crate::sliceable::{PySliceableSequence, PySliceableSequenceMut, SequenceIndex};
23-
use crate::slots::{
24-
BufferProtocol, Comparable, Hashable, Iterable, PyComparisonOp, PyIter, Unhashable,
25-
};
23+
use crate::slots::{AsBuffer, Comparable, Hashable, Iterable, PyComparisonOp, PyIter, Unhashable};
2624
use crate::utils::Either;
2725
use crate::vm::VirtualMachine;
2826
use crate::{
@@ -99,7 +97,7 @@ pub(crate) fn init(context: &PyContext) {
9997
PyByteArrayIterator::extend_class(context, &context.types.bytearray_iterator_type);
10098
}
10199

102-
#[pyimpl(flags(BASETYPE), with(Hashable, Comparable, BufferProtocol, Iterable))]
100+
#[pyimpl(flags(BASETYPE), with(Hashable, Comparable, AsBuffer, Iterable))]
103101
impl PyByteArray {
104102
#[pyslot]
105103
fn tp_new(cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
@@ -144,7 +142,7 @@ impl PyByteArray {
144142
}
145143

146144
#[pymethod(magic)]
147-
fn add(&self, other: PyBytesLike, vm: &VirtualMachine) -> PyObjectRef {
145+
fn add(&self, other: ArgBytesLike, vm: &VirtualMachine) -> PyObjectRef {
148146
vm.ctx.new_bytearray(self.inner().add(&*other.borrow_buf()))
149147
}
150148

@@ -192,7 +190,7 @@ impl PyByteArray {
192190
}
193191

194192
#[pymethod(magic)]
195-
fn iadd(zelf: PyRef<Self>, other: PyBytesLike, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
193+
fn iadd(zelf: PyRef<Self>, other: ArgBytesLike, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
196194
zelf.try_resizable(vm)?
197195
.elements
198196
.extend(&*other.borrow_buf());
@@ -667,7 +665,7 @@ impl Comparable for PyByteArray {
667665
}
668666
}
669667

670-
impl BufferProtocol for PyByteArray {
668+
impl AsBuffer for PyByteArray {
671669
fn get_buffer(zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<Box<dyn PyBuffer>> {
672670
zelf.exports.fetch_add(1);
673671
let buf = ByteArrayBuffer {

vm/src/builtins/bytes.rs

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,29 @@
1-
use bstr::ByteSlice;
2-
use crossbeam_utils::atomic::AtomicCell;
3-
use rustpython_common::borrow::{BorrowedValue, BorrowedValueMut};
4-
use std::mem::size_of;
5-
use std::ops::Deref;
6-
71
use super::dict::PyDictRef;
82
use super::int::PyIntRef;
93
use super::pystr::PyStrRef;
104
use super::pytype::PyTypeRef;
115
use crate::anystr::{self, AnyStr};
6+
use crate::buffer::{BufferOptions, PyBuffer};
127
use crate::builtins::tuple::PyTupleRef;
138
use crate::bytesinner::{
149
bytes_decode, ByteInnerFindOptions, ByteInnerNewOptions, ByteInnerPaddingOptions,
1510
ByteInnerSplitOptions, ByteInnerTranslateOptions, DecodeArgs, PyBytesInner,
1611
};
17-
use crate::byteslike::PyBytesLike;
12+
use crate::byteslike::ArgBytesLike;
1813
use crate::common::hash::PyHash;
1914
use crate::function::{OptionalArg, OptionalOption};
20-
use crate::slots::{BufferProtocol, Comparable, Hashable, Iterable, PyComparisonOp, PyIter};
15+
use crate::slots::{AsBuffer, Comparable, Hashable, Iterable, PyComparisonOp, PyIter};
2116
use crate::utils::Either;
2217
use crate::vm::VirtualMachine;
2318
use crate::{
2419
IdProtocol, IntoPyObject, PyClassImpl, PyComparisonValue, PyContext, PyIterable, PyObjectRef,
25-
PyRef, PyResult, PyValue, TryFromObject, TypeProtocol,
20+
PyRef, PyResult, PyValue, TryFromBorrowedObject, TypeProtocol,
2621
};
27-
28-
use crate::builtins::memory::{BufferOptions, PyBuffer};
22+
use bstr::ByteSlice;
23+
use crossbeam_utils::atomic::AtomicCell;
24+
use rustpython_common::borrow::{BorrowedValue, BorrowedValueMut};
25+
use std::mem::size_of;
26+
use std::ops::Deref;
2927

3028
/// "bytes(iterable_of_ints) -> bytes\n\
3129
/// bytes(string, encoding[, errors]) -> bytes\n\
@@ -98,7 +96,7 @@ pub(crate) fn init(context: &PyContext) {
9896
PyBytesIterator::extend_class(context, &context.types.bytes_iterator_type);
9997
}
10098

101-
#[pyimpl(flags(BASETYPE), with(Hashable, Comparable, BufferProtocol, Iterable))]
99+
#[pyimpl(flags(BASETYPE), with(Hashable, Comparable, AsBuffer, Iterable))]
102100
impl PyBytes {
103101
#[pyslot]
104102
fn tp_new(
@@ -136,7 +134,7 @@ impl PyBytes {
136134
}
137135

138136
#[pymethod(magic)]
139-
fn add(&self, other: PyBytesLike, vm: &VirtualMachine) -> PyObjectRef {
137+
fn add(&self, other: ArgBytesLike, vm: &VirtualMachine) -> PyObjectRef {
140138
vm.ctx.new_bytes(self.inner.add(&*other.borrow_buf()))
141139
}
142140

@@ -366,7 +364,7 @@ impl PyBytes {
366364

367365
#[pymethod]
368366
fn partition(&self, sep: PyObjectRef, vm: &VirtualMachine) -> PyResult {
369-
let sub = PyBytesInner::try_from_object(vm, sep.clone())?;
367+
let sub = PyBytesInner::try_from_borrowed_object(vm, &sep)?;
370368
let (front, has_mid, back) = self.inner.partition(&sub, vm)?;
371369
Ok(vm.ctx.new_tuple(vec![
372370
vm.ctx.new_bytes(front),
@@ -381,7 +379,7 @@ impl PyBytes {
381379

382380
#[pymethod]
383381
fn rpartition(&self, sep: PyObjectRef, vm: &VirtualMachine) -> PyResult {
384-
let sub = PyBytesInner::try_from_object(vm, sep.clone())?;
382+
let sub = PyBytesInner::try_from_borrowed_object(vm, &sep)?;
385383
let (back, has_mid, front) = self.inner.rpartition(&sub, vm)?;
386384
Ok(vm.ctx.new_tuple(vec![
387385
vm.ctx.new_bytes(front),
@@ -502,7 +500,7 @@ impl PyBytes {
502500
}
503501
}
504502

505-
impl BufferProtocol for PyBytes {
503+
impl AsBuffer for PyBytes {
506504
fn get_buffer(zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<Box<dyn PyBuffer>> {
507505
let buf = BytesBuffer {
508506
bytes: zelf.clone(),
@@ -604,8 +602,8 @@ impl PyIter for PyBytesIterator {
604602
}
605603
}
606604

607-
impl TryFromObject for PyBytes {
608-
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
609-
PyBytesInner::try_from_object(vm, obj).map(|x| x.into())
605+
impl TryFromBorrowedObject for PyBytes {
606+
fn try_from_borrowed_object(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult<Self> {
607+
PyBytesInner::try_from_borrowed_object(vm, obj).map(|x| x.into())
610608
}
611609
}

vm/src/builtins/make_module.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ mod decl {
2020
use crate::builtins::pystr::{PyStr, PyStrRef};
2121
use crate::builtins::pytype::PyTypeRef;
2222
use crate::builtins::{PyByteArray, PyBytes};
23-
use crate::byteslike::PyBytesLike;
23+
use crate::byteslike::ArgBytesLike;
2424
use crate::common::{hash::PyHash, str::to_ascii};
2525
#[cfg(feature = "rustpython-compiler")]
2626
use crate::compile;
@@ -563,7 +563,7 @@ mod decl {
563563
}
564564

565565
#[pyfunction]
566-
fn ord(string: Either<PyBytesLike, PyStrRef>, vm: &VirtualMachine) -> PyResult<u32> {
566+
fn ord(string: Either<ArgBytesLike, PyStrRef>, vm: &VirtualMachine) -> PyResult<u32> {
567567
match string {
568568
Either::A(bytes) => bytes.with_ref(|bytes| {
569569
let bytes_len = bytes.len();

0 commit comments

Comments
 (0)