rustpython_vm/function/
buffer.rs1use crate::{
2 AsObject, PyObject, PyObjectRef, PyResult, TryFromBorrowedObject, TryFromObject,
3 VirtualMachine,
4 builtins::{PyStr, PyStrRef},
5 common::borrow::{BorrowedValue, BorrowedValueMut},
6 protocol::{BufferFlags, PyBuffer},
7};
8
9#[derive(Debug, Traverse)]
13pub struct ArgBytesLike(PyBuffer);
14
15impl PyObject {
16 pub fn try_bytes_like<F, R>(&self, vm: &VirtualMachine, f: F) -> PyResult<R>
17 where
18 F: FnOnce(&[u8]) -> R,
19 {
20 let buffer = PyBuffer::from_object(vm, self, BufferFlags::SIMPLE)?;
21 buffer
22 .as_contiguous()
23 .map(|x| f(&x))
24 .ok_or_else(|| vm.new_buffer_error("non-contiguous buffer is not a bytes-like object"))
25 }
26
27 pub fn try_rw_bytes_like<F, R>(&self, vm: &VirtualMachine, f: F) -> PyResult<R>
28 where
29 F: FnOnce(&mut [u8]) -> R,
30 {
31 let buffer = PyBuffer::from_object(vm, self, BufferFlags::WRITABLE)?;
32 buffer
33 .as_contiguous_mut()
34 .map(|mut x| f(&mut x))
35 .ok_or_else(|| vm.new_type_error("buffer is not a read-write bytes-like object"))
36 }
37}
38
39impl ArgBytesLike {
40 #[must_use]
41 pub fn borrow_buf(&self) -> BorrowedValue<'_, [u8]> {
42 unsafe { self.0.contiguous_unchecked() }
43 }
44
45 pub fn with_ref<F, R>(&self, f: F) -> R
46 where
47 F: FnOnce(&[u8]) -> R,
48 {
49 f(&self.borrow_buf())
50 }
51
52 pub fn borrow_buf_unlocked(&self, vm: &VirtualMachine) -> PyResult<UnlockedBuf<'_>> {
63 let borrowed = self.borrow_buf();
64 if !borrowed.is_locked() {
65 return Ok(UnlockedBuf::Borrowed(borrowed));
66 }
67 let mut copy = Vec::new();
68 copy.try_reserve_exact(borrowed.len())
69 .map_err(|_| vm.no_memory_error())?;
70 copy.extend_from_slice(&borrowed);
71 Ok(UnlockedBuf::Copied(copy))
72 }
73
74 #[must_use]
75 pub const fn len(&self) -> usize {
76 self.0.desc.len
77 }
78
79 #[must_use]
83 pub const fn itemsize(&self) -> usize {
84 self.0.desc.itemsize
85 }
86
87 #[must_use]
88 pub const fn is_empty(&self) -> bool {
89 self.len() == 0
90 }
91
92 #[must_use]
93 pub fn as_object(&self) -> &PyObject {
94 &self.0.obj
95 }
96
97 #[must_use]
100 pub fn source_object(&self) -> &PyObject {
101 self.0
102 .obj
103 .downcast_ref::<crate::builtins::PyMemoryView>()
104 .map_or(&self.0.obj, |view| view.viewed_object())
105 }
106}
107
108impl From<ArgBytesLike> for PyBuffer {
109 fn from(buffer: ArgBytesLike) -> Self {
110 buffer.0
111 }
112}
113
114impl From<ArgBytesLike> for PyObjectRef {
115 fn from(buffer: ArgBytesLike) -> Self {
116 buffer.as_object().to_owned()
117 }
118}
119
120impl ArgBytesLike {
121 fn from_request(vm: &VirtualMachine, obj: &PyObject, flags: BufferFlags) -> PyResult<Self> {
122 let buffer = PyBuffer::from_object(vm, obj, flags)?;
123 if buffer.desc.is_contiguous() {
124 Ok(Self(buffer))
125 } else {
126 Err(vm.new_buffer_error("non-contiguous buffer is not a bytes-like object"))
127 }
128 }
129}
130
131impl<'a> TryFromBorrowedObject<'a> for ArgBytesLike {
132 fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<Self> {
133 Self::from_request(vm, obj, BufferFlags::SIMPLE)
134 }
135}
136
137#[derive(Debug, Traverse)]
140pub struct ArgContiguousBytesLike(ArgBytesLike);
141
142impl core::ops::Deref for ArgContiguousBytesLike {
143 type Target = ArgBytesLike;
144
145 fn deref(&self) -> &Self::Target {
146 &self.0
147 }
148}
149
150impl<'a> TryFromBorrowedObject<'a> for ArgContiguousBytesLike {
151 fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<Self> {
152 ArgBytesLike::from_request(vm, obj, BufferFlags::CONTIG_RO).map(Self)
153 }
154}
155
156#[derive(Debug)]
158pub enum UnlockedBuf<'a> {
159 Borrowed(BorrowedValue<'a, [u8]>),
160 Copied(Vec<u8>),
161}
162
163impl core::ops::Deref for UnlockedBuf<'_> {
164 type Target = [u8];
165
166 fn deref(&self) -> &[u8] {
167 match self {
168 Self::Borrowed(b) => b,
169 Self::Copied(v) => v,
170 }
171 }
172}
173
174#[derive(Debug, Traverse)]
176pub struct ArgMemoryBuffer(PyBuffer);
177
178impl ArgMemoryBuffer {
179 #[must_use]
180 pub fn borrow_buf_mut(&self) -> BorrowedValueMut<'_, [u8]> {
181 unsafe { self.0.contiguous_mut_unchecked() }
182 }
183
184 pub fn with_ref<F, R>(&self, f: F) -> R
185 where
186 F: FnOnce(&mut [u8]) -> R,
187 {
188 f(&mut self.borrow_buf_mut())
189 }
190
191 #[must_use]
192 pub const fn len(&self) -> usize {
193 self.0.desc.len
194 }
195
196 #[must_use]
197 pub const fn is_empty(&self) -> bool {
198 self.len() == 0
199 }
200
201 #[must_use]
204 pub fn source_object(&self) -> &PyObject {
205 self.0
206 .obj
207 .downcast_ref::<crate::builtins::PyMemoryView>()
208 .map_or(&self.0.obj, |view| view.viewed_object())
209 }
210}
211
212impl From<ArgMemoryBuffer> for PyBuffer {
213 fn from(buffer: ArgMemoryBuffer) -> Self {
214 buffer.0
215 }
216}
217
218impl<'a> TryFromBorrowedObject<'a> for ArgMemoryBuffer {
219 fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<Self> {
220 let buffer = PyBuffer::from_object(vm, obj, BufferFlags::WRITABLE).map_err(|exc| {
221 if obj.check_buffer() {
222 vm.new_type_error("buffer is not a read-write bytes-like object")
225 } else {
226 exc
227 }
228 })?;
229 if !buffer.desc.is_contiguous() {
230 Err(vm.new_buffer_error("non-contiguous buffer is not a bytes-like object"))
231 } else if buffer.desc.readonly {
232 Err(vm.new_type_error("buffer is not a read-write bytes-like object"))
233 } else {
234 Ok(Self(buffer))
235 }
236 }
237}
238
239pub enum ArgStrOrBytesLike {
241 Buf(ArgBytesLike),
242 Str(PyStrRef),
243}
244
245impl ArgStrOrBytesLike {
246 #[must_use]
247 pub fn as_object(&self) -> &PyObject {
248 match self {
249 Self::Buf(b) => b.as_object(),
250 Self::Str(s) => s.as_object(),
251 }
252 }
253}
254
255impl TryFromObject for ArgStrOrBytesLike {
256 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
257 obj.downcast()
258 .map(Self::Str)
259 .or_else(|obj| ArgBytesLike::try_from_object(vm, obj).map(Self::Buf))
260 }
261}
262
263impl ArgStrOrBytesLike {
264 #[must_use]
265 pub fn borrow_bytes(&self) -> BorrowedValue<'_, [u8]> {
266 match self {
267 Self::Buf(b) => b.borrow_buf(),
268 Self::Str(s) => s.as_bytes().into(),
269 }
270 }
271}
272
273#[derive(Debug)]
274pub enum ArgAsciiBuffer {
275 String(PyStrRef),
276 Buffer(ArgBytesLike),
277}
278
279impl TryFromObject for ArgAsciiBuffer {
280 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
281 match obj.downcast::<PyStr>() {
282 Ok(string) => {
283 if string.as_wtf8().is_ascii() {
284 Ok(Self::String(string))
285 } else {
286 Err(vm.new_value_error("string argument should contain only ASCII characters"))
287 }
288 }
289 Err(obj) => ArgBytesLike::try_from_object(vm, obj).map(ArgAsciiBuffer::Buffer),
290 }
291 }
292}
293
294impl ArgAsciiBuffer {
295 #[must_use]
296 pub fn len(&self) -> usize {
297 match self {
298 Self::String(s) => s.as_wtf8().len(),
299 Self::Buffer(buffer) => buffer.len(),
300 }
301 }
302
303 #[must_use]
304 pub fn is_empty(&self) -> bool {
305 self.len() == 0
306 }
307
308 #[inline]
309 pub fn with_ref<F, R>(&self, f: F) -> R
310 where
311 F: FnOnce(&[u8]) -> R,
312 {
313 match self {
314 Self::String(s) => f(s.as_bytes()),
315 Self::Buffer(buffer) => buffer.with_ref(f),
316 }
317 }
318}