Skip to main content

rustpython_vm/builtins/
bytearray.rs

1//! Implementation of the python bytearray object.
2use super::{
3    PositionIterInternal, PyBytes, PyDictRef, PyGenericAlias, PyStrRef, PyTuple, PyTupleRef,
4    PyType, PyTypeRef, iter::builtins_iter, locked_next,
5};
6use crate::{
7    AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
8    VirtualMachine,
9    anystr::{self, AnyStr},
10    atomic_func,
11    byte::{bytearray_extend_from_object, bytearray_from_object, value_from_object},
12    bytes_inner::{
13        ByteInnerFindOptions, ByteInnerHexOptions, ByteInnerNewOptions, ByteInnerPaddingOptions,
14        ByteInnerReplaceOptions, ByteInnerSplitOptions, ByteInnerStripOptions, ByteInnerSub,
15        ByteInnerTranslateOptions, BytesJoin, DecodeArgs, PyBytesInner, bytes_decode,
16    },
17    class::PyClassImpl,
18    common::{
19        atomic::{AtomicUsize, Ordering},
20        lock::{
21            PyDetachingRwLock, PyDetachingRwLockReadGuard, PyDetachingRwLockWriteGuard,
22            PyMappedDetachingRwLockReadGuard, PyMappedDetachingRwLockWriteGuard, PyMutex,
23        },
24    },
25    convert::{ToPyObject, ToPyResult},
26    function::{ArgBytesLike, PyComparisonValue, PySsize},
27    protocol::{
28        BufferDescriptor, BufferFlags, BufferMethods, BufferResizeGuard, PyBuffer, PyIterReturn,
29        PyMappingMethods, PyNumberMethods, PySequenceMethods,
30    },
31    sliceable::{SequenceIndex, SliceableSequenceMutOp, SliceableSequenceOp},
32    types::{
33        AsBuffer, AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor,
34        DefaultConstructor, Initializer, IterNext, Iterable, PyComparisonOp, Representable,
35        SelfIter,
36    },
37};
38use bstr::ByteSlice;
39use core::mem::size_of;
40
41#[pyclass(module = false, name = "bytearray", unhashable = true)]
42#[derive(Debug, Default)]
43pub struct PyByteArray {
44    inner: PyDetachingRwLock<PyBytesInner>,
45    exports: AtomicUsize,
46}
47
48pub(crate) type PyByteArrayRef = PyRef<PyByteArray>;
49
50impl From<PyBytesInner> for PyByteArray {
51    fn from(inner: PyBytesInner) -> Self {
52        Self::from_inner(inner)
53    }
54}
55
56impl From<Vec<u8>> for PyByteArray {
57    fn from(elements: Vec<u8>) -> Self {
58        Self::from(PyBytesInner { elements })
59    }
60}
61
62impl PyPayload for PyByteArray {
63    fn class(ctx: &Context) -> &'static Py<PyType> {
64        ctx.types.bytearray_type
65    }
66}
67
68/// Fill bytearray class methods dictionary.
69pub(crate) fn init(context: &'static Context) {
70    PyByteArray::extend_class(context, context.types.bytearray_type);
71    PyByteArrayIterator::extend_class(context, context.types.bytearray_iterator_type);
72}
73
74impl PyByteArray {
75    #[deprecated(note = "use PyByteArray::from(...).into_ref() instead")]
76    pub fn new_ref(data: Vec<u8>, ctx: &Context) -> PyRef<Self> {
77        Self::from(data).into_ref(ctx)
78    }
79
80    const fn from_inner(inner: PyBytesInner) -> Self {
81        Self {
82            inner: PyDetachingRwLock::new(inner),
83            exports: AtomicUsize::new(0),
84        }
85    }
86
87    pub fn borrow_buf(&self) -> PyMappedDetachingRwLockReadGuard<'_, [u8]> {
88        PyDetachingRwLockReadGuard::map(self.inner.read(), |inner| &*inner.elements)
89    }
90
91    pub fn borrow_buf_mut(&self) -> PyMappedDetachingRwLockWriteGuard<'_, Vec<u8>> {
92        PyDetachingRwLockWriteGuard::map(self.inner.write(), |inner| &mut inner.elements)
93    }
94
95    fn repeat(&self, value: isize, vm: &VirtualMachine) -> PyResult<Self> {
96        self.inner().mul(value, vm).map(|x| x.into())
97    }
98
99    fn _setitem_by_index(&self, i: isize, value: &PyObject, vm: &VirtualMachine) -> PyResult<()> {
100        let value = value_from_object(vm, value)?;
101        self.borrow_buf_mut().setitem_by_index(vm, i, value)
102    }
103
104    fn _setitem(
105        zelf: &Py<Self>,
106        needle: &PyObject,
107        value: &PyObject,
108        vm: &VirtualMachine,
109    ) -> PyResult<()> {
110        match SequenceIndex::try_from_borrowed_object(vm, needle, "bytearray")? {
111            SequenceIndex::Int(i) => zelf._setitem_by_index(i, value, vm),
112            SequenceIndex::Slice(slice) => {
113                let items = if zelf.is(&value) {
114                    zelf.borrow_buf().to_vec()
115                } else {
116                    bytearray_from_object(vm, value)?
117                };
118                if let Some(mut w) = zelf.try_resizable_opt() {
119                    w.elements.setitem_by_slice(vm, slice, &items)
120                } else {
121                    zelf.borrow_buf_mut()
122                        .setitem_by_slice_no_resize(vm, slice, &items)
123                }
124            }
125        }
126    }
127
128    fn _getitem(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult {
129        match SequenceIndex::try_from_borrowed_object(vm, needle, "bytearray")? {
130            SequenceIndex::Int(i) => self
131                .borrow_buf()
132                .getitem_by_index(vm, i)
133                .map(|x| vm.ctx.new_int(x).into()),
134            SequenceIndex::Slice(slice) => self
135                .borrow_buf()
136                .getitem_by_slice(vm, slice)
137                .map(|x| vm.ctx.new_bytearray(x).into()),
138        }
139    }
140
141    pub fn _delitem(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult<()> {
142        match SequenceIndex::try_from_borrowed_object(vm, needle, "bytearray")? {
143            SequenceIndex::Int(i) => self.try_resizable(vm)?.elements.delitem_by_index(vm, i),
144            SequenceIndex::Slice(slice) => {
145                // TODO: delete 0 elements don't need resizable
146                self.try_resizable(vm)?.elements.delitem_by_slice(vm, slice)
147            }
148        }
149    }
150
151    fn irepeat(zelf: &Py<Self>, n: isize, vm: &VirtualMachine) -> PyResult<()> {
152        if n == 1 {
153            return Ok(());
154        }
155        let mut w = match zelf.try_resizable(vm) {
156            Ok(w) => w,
157            Err(err) => {
158                return if zelf.borrow_buf().is_empty() {
159                    // We can multiple an empty vector by any integer
160                    Ok(())
161                } else {
162                    Err(err)
163                };
164            }
165        };
166
167        w.imul(n, vm)
168    }
169}
170
171impl PyByteArray {
172    #[inline]
173    fn inner(&self) -> PyDetachingRwLockReadGuard<'_, PyBytesInner> {
174        self.inner.read()
175    }
176    #[inline]
177    fn inner_mut(&self) -> PyDetachingRwLockWriteGuard<'_, PyBytesInner> {
178        self.inner.write()
179    }
180
181    fn __len__(&self) -> usize {
182        self.borrow_buf().len()
183    }
184
185    fn __add__(&self, other: ArgBytesLike) -> Self {
186        self.inner().add(&other.borrow_buf()).into()
187    }
188
189    fn __contains__(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
190        let needle = ByteInnerSub::from_contains_arg(needle, vm)?;
191        self.inner().contains(needle, vm)
192    }
193
194    fn __iadd__(
195        zelf: PyRef<Self>,
196        other: ArgBytesLike,
197        vm: &VirtualMachine,
198    ) -> PyResult<PyRef<Self>> {
199        zelf.try_resizable(vm)?
200            .elements
201            .extend(&*other.borrow_buf());
202        Ok(zelf)
203    }
204
205    fn __getitem__(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult {
206        self._getitem(needle, vm)
207    }
208
209    pub fn __delitem__(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult<()> {
210        self._delitem(needle, vm)
211    }
212
213    fn __mul__(&self, value: PySsize, vm: &VirtualMachine) -> PyResult<Self> {
214        self.repeat(value, vm)
215    }
216
217    fn __imul__(zelf: PyRef<Self>, value: PySsize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
218        Self::irepeat(&zelf, value, vm)?;
219        Ok(zelf)
220    }
221
222    fn __mod__(&self, values: PyObjectRef, vm: &VirtualMachine) -> PyResult<Self> {
223        // Formatting calls the values' conversion methods, which can reach
224        // this bytearray, so the format is taken by value.
225        let format = self.inner().clone();
226        let formatted = format.cformat(values, vm)?;
227        Ok(formatted.into())
228    }
229
230    // TODO: Uncomment when Python adds __class_getitem__ to bytearray
231    // #[pyclassmethod]
232    fn __class_getitem__(
233        cls: PyTypeRef,
234        args: PyObjectRef,
235        vm: &VirtualMachine,
236    ) -> PyResult<PyGenericAlias> {
237        PyGenericAlias::from_args(cls, args, vm)
238    }
239}
240
241#[pyclass(
242    flags(BASETYPE, _MATCH_SELF),
243    with(
244        Py,
245        PyRef,
246        Constructor,
247        Initializer,
248        Comparable,
249        AsBuffer,
250        AsMapping,
251        AsSequence,
252        AsNumber,
253        Iterable,
254        Representable
255    )
256)]
257impl PyByteArray {}
258
259#[derive(FromArgs)]
260struct ByteArrayReduceExArgs {
261    #[pyarg(positional, default)]
262    proto: usize,
263}
264
265#[derive(FromArgs)]
266struct PopArgs {
267    #[pyarg(positional, default = -1)]
268    index: isize,
269}
270
271#[pyclass]
272impl Py<PyByteArray> {
273    fn __setitem__(
274        &self,
275        needle: &PyObject,
276        value: PyObjectRef,
277        vm: &VirtualMachine,
278    ) -> PyResult<()> {
279        PyByteArray::_setitem(self, needle, &value, vm)
280    }
281
282    #[pymethod]
283    fn pop(&self, index: PopArgs, vm: &VirtualMachine) -> PyResult<u8> {
284        let elements = &mut self.try_resizable(vm)?.elements;
285        let index = elements
286            .wrap_index(index.index)
287            .ok_or_else(|| vm.new_index_error("index out of range"))?;
288        Ok(elements.remove(index))
289    }
290
291    #[pymethod]
292    fn insert(&self, index: isize, item: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
293        let value = value_from_object(vm, &item)?;
294        let elements = &mut self.try_resizable(vm)?.elements;
295        let index = elements.saturate_index(index);
296        elements.insert(index, value);
297        Ok(())
298    }
299
300    #[pymethod]
301    fn append(&self, item: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
302        let value = value_from_object(vm, &item)?;
303        self.try_resizable(vm)?.elements.push(value);
304        Ok(())
305    }
306
307    #[pymethod]
308    fn remove(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
309        let byte = value_from_object(vm, &value)?;
310        let elements = &mut self.try_resizable(vm)?.elements;
311        let index = elements
312            .find_byte(byte)
313            .ok_or_else(|| vm.new_value_error("value not found in bytearray"))?;
314        elements.remove(index);
315        Ok(())
316    }
317
318    #[pymethod]
319    fn extend(&self, iterable_of_ints: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
320        if self.is(&iterable_of_ints) {
321            return PyByteArray::irepeat(self, 2, vm);
322        }
323        // bytearray_setslice keeps the export alive across the resize, so a value
324        // looking at this bytearray is what stops it from growing.
325        let buffer = iterable_of_ints
326            .check_buffer()
327            .then(|| {
328                PyBuffer::from_object(vm, &iterable_of_ints, BufferFlags::SIMPLE).map_err(|_| {
329                    // What an exporter refuses to hand out leaves the value simply
330                    // not usable here, whatever the exporter's own complaint was.
331                    vm.new_type_error(format!(
332                        "can't set bytearray slice from {}",
333                        iterable_of_ints.class().name()
334                    ))
335                })
336            })
337            .transpose()?;
338        let items = match &buffer {
339            Some(buffer) => buffer
340                .as_contiguous()
341                .ok_or_else(|| {
342                    vm.new_buffer_error("non-contiguous buffer is not a bytes-like object")
343                })?
344                .to_vec(),
345            None => bytearray_extend_from_object(vm, &iterable_of_ints)?,
346        };
347        self.try_resizable(vm)?.elements.extend(items);
348        Ok(())
349    }
350
351    #[pymethod]
352    fn clear(&self, vm: &VirtualMachine) -> PyResult<()> {
353        self.try_resizable(vm)?.elements.clear();
354        Ok(())
355    }
356
357    #[pymethod]
358    fn __reduce_ex__(
359        &self,
360        args: ByteArrayReduceExArgs,
361        vm: &VirtualMachine,
362    ) -> (PyTypeRef, PyTupleRef, Option<PyDictRef>) {
363        let _ = args.proto;
364        self.__reduce__(vm)
365    }
366
367    #[pymethod]
368    fn __reduce__(&self, vm: &VirtualMachine) -> (PyTypeRef, PyTupleRef, Option<PyDictRef>) {
369        let bytes = PyBytes::from(self.borrow_buf().to_vec()).to_pyobject(vm);
370        (
371            self.class().to_owned(),
372            PyTuple::new_ref(vec![bytes], &vm.ctx),
373            self.as_object().dict(),
374        )
375    }
376
377    #[cfg(debug_assertions)]
378    #[pygetset]
379    fn exports(&self) -> usize {
380        self.exports.load(Ordering::Relaxed)
381    }
382
383    #[pymethod]
384    fn __alloc__(&self) -> usize {
385        self.inner().capacity()
386    }
387
388    #[pymethod]
389    fn __sizeof__(&self) -> usize {
390        size_of::<PyByteArray>() + self.borrow_buf().len() * size_of::<u8>()
391    }
392
393    #[pyslot]
394    fn slot_str(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyStrRef> {
395        let zelf = zelf
396            .downcast_ref::<PyByteArray>()
397            .expect("expected bytearray");
398        PyBytesInner::warn_on_str("str() on a bytearray instance", vm)?;
399        let class_name = zelf.class().name();
400        let repr = zelf.inner().repr_with_name(&class_name, vm)?;
401        Ok(vm.ctx.new_str(repr))
402    }
403
404    #[pystaticmethod]
405    fn maketrans(frm: PyBytesInner, to: PyBytesInner, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
406        PyBytesInner::maketrans(frm, to, vm)
407    }
408
409    #[pymethod]
410    fn isalnum(&self) -> bool {
411        self.inner().isalnum()
412    }
413
414    #[pymethod]
415    fn isalpha(&self) -> bool {
416        self.inner().isalpha()
417    }
418
419    #[pymethod]
420    fn isascii(&self) -> bool {
421        self.inner().isascii()
422    }
423
424    #[pymethod]
425    fn isdigit(&self) -> bool {
426        self.inner().isdigit()
427    }
428
429    #[pymethod]
430    fn islower(&self) -> bool {
431        self.inner().islower()
432    }
433
434    #[pymethod]
435    fn isspace(&self) -> bool {
436        self.inner().isspace()
437    }
438
439    #[pymethod]
440    fn isupper(&self) -> bool {
441        self.inner().isupper()
442    }
443
444    #[pymethod]
445    fn istitle(&self) -> bool {
446        self.inner().istitle()
447    }
448
449    #[pymethod]
450    fn lower(&self) -> PyByteArray {
451        self.inner().lower().into()
452    }
453
454    #[pymethod]
455    fn upper(&self) -> PyByteArray {
456        self.inner().upper().into()
457    }
458
459    #[pymethod]
460    fn capitalize(&self) -> PyByteArray {
461        self.inner().capitalize().into()
462    }
463
464    #[pymethod]
465    fn swapcase(&self) -> PyByteArray {
466        self.inner().swapcase().into()
467    }
468
469    #[pymethod]
470    fn hex(&self, options: ByteInnerHexOptions, vm: &VirtualMachine) -> PyResult<String> {
471        // Measuring the separator runs Python, so it happens before the buffer
472        // is borrowed.
473        let (sep, bytes_per_sep) = options.resolve(vm)?;
474        Ok(self.inner().hex(sep, bytes_per_sep))
475    }
476
477    #[pyclassmethod]
478    fn fromhex(cls: PyTypeRef, string: PyObjectRef, vm: &VirtualMachine) -> PyResult {
479        let bytes = PyBytesInner::fromhex_object(&string, vm)?;
480        let bytes = vm.ctx.new_bytes(bytes);
481        let args = vec![bytes.into()].into();
482        PyType::call(&cls, args, vm)
483    }
484
485    #[pymethod]
486    fn center(
487        &self,
488        options: ByteInnerPaddingOptions,
489        vm: &VirtualMachine,
490    ) -> PyResult<PyByteArray> {
491        Ok(self.inner().center(options, vm)?.into())
492    }
493
494    #[pymethod]
495    fn ljust(
496        &self,
497        options: ByteInnerPaddingOptions,
498        vm: &VirtualMachine,
499    ) -> PyResult<PyByteArray> {
500        Ok(self.inner().ljust(options, vm)?.into())
501    }
502
503    #[pymethod]
504    fn rjust(
505        &self,
506        options: ByteInnerPaddingOptions,
507        vm: &VirtualMachine,
508    ) -> PyResult<PyByteArray> {
509        Ok(self.inner().rjust(options, vm)?.into())
510    }
511
512    #[pymethod]
513    fn count(&self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult<usize> {
514        self.inner().count(options, vm)
515    }
516
517    #[pymethod]
518    fn join(&self, iterable_of_bytes: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyByteArray> {
519        // Export before driving the iterable: resizing is forbidden, but
520        // same-size mutations must be reflected in the joined result.
521        self.exports.fetch_add(1, Ordering::Release);
522        scopeguard::defer! { self.exports.fetch_sub(1, Ordering::Release); }
523        let separator_len = self.__len__();
524        let items = BytesJoin::new(iterable_of_bytes, vm)?;
525        Ok(items
526            .join(separator_len, || self.borrow_buf().into(), vm)?
527            .into())
528    }
529
530    #[pymethod]
531    fn endswith(&self, options: anystr::StartsEndsWithArgs, vm: &VirtualMachine) -> PyResult<bool> {
532        let borrowed = self.borrow_buf();
533        let (affix, substr) =
534            match options.prepare(&*borrowed, borrowed.len(), |s, r| s.get_bytes(r)) {
535                Some(x) => x,
536                None => return Ok(false),
537            };
538        substr.py_starts_ends_with(
539            &affix,
540            "endswith",
541            "bytes",
542            |s, x: PyBytesInner| s.ends_with(x.as_bytes()),
543            vm,
544        )
545    }
546
547    #[pymethod]
548    fn startswith(
549        &self,
550        options: anystr::StartsEndsWithArgs,
551        vm: &VirtualMachine,
552    ) -> PyResult<bool> {
553        let borrowed = self.borrow_buf();
554        let (affix, substr) =
555            match options.prepare(&*borrowed, borrowed.len(), |s, r| s.get_bytes(r)) {
556                Some(x) => x,
557                None => return Ok(false),
558            };
559        substr.py_starts_ends_with(
560            &affix,
561            "startswith",
562            "bytes",
563            |s, x: PyBytesInner| s.starts_with(x.as_bytes()),
564            vm,
565        )
566    }
567
568    #[pymethod]
569    fn find(&self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult<isize> {
570        let index = self.inner().find(options, |h, n| h.find(n), vm)?;
571        Ok(index.map_or(-1, |v| v as isize))
572    }
573
574    #[pymethod]
575    fn index(&self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult<usize> {
576        let index = self.inner().find(options, |h, n| h.find(n), vm)?;
577        index.ok_or_else(|| vm.new_value_error("substring not found"))
578    }
579
580    #[pymethod]
581    fn rfind(&self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult<isize> {
582        let index = self.inner().find(options, |h, n| h.rfind(n), vm)?;
583        Ok(index.map_or(-1, |v| v as isize))
584    }
585
586    #[pymethod]
587    fn rindex(&self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult<usize> {
588        let index = self.inner().find(options, |h, n| h.rfind(n), vm)?;
589        index.ok_or_else(|| vm.new_value_error("substring not found"))
590    }
591
592    #[pymethod]
593    fn translate(
594        &self,
595        options: ByteInnerTranslateOptions,
596        vm: &VirtualMachine,
597    ) -> PyResult<PyByteArray> {
598        Ok(self.inner().translate(options, vm)?.into())
599    }
600
601    #[pymethod]
602    fn strip(&self, options: ByteInnerStripOptions) -> PyByteArray {
603        self.inner().strip(options.bytes).into()
604    }
605
606    #[pymethod]
607    fn removeprefix(&self, prefix: PyBytesInner) -> PyByteArray {
608        self.inner().removeprefix(prefix).into()
609    }
610
611    #[pymethod]
612    fn removesuffix(&self, suffix: PyBytesInner) -> PyByteArray {
613        self.inner().removesuffix(suffix).to_vec().into()
614    }
615
616    #[pymethod]
617    fn split(
618        &self,
619        options: ByteInnerSplitOptions,
620        vm: &VirtualMachine,
621    ) -> PyResult<Vec<PyObjectRef>> {
622        self.inner()
623            .split(options, |s, vm| vm.ctx.new_bytearray(s.to_vec()).into(), vm)
624    }
625
626    #[pymethod]
627    fn rsplit(
628        &self,
629        options: ByteInnerSplitOptions,
630        vm: &VirtualMachine,
631    ) -> PyResult<Vec<PyObjectRef>> {
632        self.inner()
633            .rsplit(options, |s, vm| vm.ctx.new_bytearray(s.to_vec()).into(), vm)
634    }
635
636    #[pymethod]
637    fn partition(&self, sep: PyBytesInner, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
638        // sep ALWAYS converted to  bytearray even it's bytes or memoryview
639        // so its ok to accept PyBytesInner
640        let value = self.inner();
641        let (front, has_mid, back) = value.partition(&sep, vm)?;
642        Ok(vm.new_tuple((
643            vm.ctx.new_bytearray(front.to_vec()),
644            vm.ctx
645                .new_bytearray(if has_mid { sep.elements } else { Vec::new() }),
646            vm.ctx.new_bytearray(back.to_vec()),
647        )))
648    }
649
650    #[pymethod]
651    fn rpartition(&self, sep: PyBytesInner, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
652        let value = self.inner();
653        let (back, has_mid, front) = value.rpartition(&sep, vm)?;
654        Ok(vm.new_tuple((
655            vm.ctx.new_bytearray(front.to_vec()),
656            vm.ctx
657                .new_bytearray(if has_mid { sep.elements } else { Vec::new() }),
658            vm.ctx.new_bytearray(back.to_vec()),
659        )))
660    }
661
662    #[pymethod]
663    fn expandtabs(&self, options: anystr::ExpandTabsArgs) -> PyByteArray {
664        self.inner().expandtabs(options).into()
665    }
666
667    #[pymethod]
668    fn splitlines(&self, options: anystr::SplitLinesArgs, vm: &VirtualMachine) -> Vec<PyObjectRef> {
669        self.inner()
670            .splitlines(options, |x| vm.ctx.new_bytearray(x.to_vec()).into())
671    }
672
673    #[pymethod]
674    fn zfill(&self, width: isize, vm: &VirtualMachine) -> PyResult<PyByteArray> {
675        Ok(self.inner().zfill(width, vm)?.into())
676    }
677
678    #[pymethod]
679    fn replace(
680        &self,
681        options: ByteInnerReplaceOptions,
682        vm: &VirtualMachine,
683    ) -> PyResult<PyByteArray> {
684        Ok(self.inner().replace(options, vm)?.into())
685    }
686
687    #[pymethod]
688    fn copy(&self) -> PyByteArray {
689        self.borrow_buf().to_vec().into()
690    }
691
692    #[pymethod]
693    fn title(&self) -> PyByteArray {
694        self.inner().title().into()
695    }
696
697    #[pymethod]
698    fn reverse(&self) {
699        self.borrow_buf_mut().reverse();
700    }
701
702    #[pymethod]
703    pub fn resize(&self, size: isize, vm: &VirtualMachine) -> PyResult<()> {
704        if size < 0 {
705            return Err(vm.new_value_error("bytearray.resize(): new size must be >= 0"));
706        }
707        let mut inner = self.try_resizable(vm)?;
708        let size = size as usize;
709        let elements = &mut inner.elements;
710        elements
711            .try_reserve_exact(size.saturating_sub(elements.len()))
712            .map_err(|_| vm.no_memory_error())?;
713        elements.resize(size, 0);
714        Ok(())
715    }
716}
717
718#[pyclass]
719impl PyRef<PyByteArray> {
720    #[pymethod]
721    fn lstrip(self, options: ByteInnerStripOptions, vm: &VirtualMachine) -> Self {
722        let inner = self.inner();
723        let stripped = inner.lstrip(options.bytes);
724        let elements = &inner.elements;
725        if stripped == elements {
726            drop(inner);
727            self
728        } else {
729            vm.ctx.new_pyref(PyByteArray::from(stripped.to_vec()))
730        }
731    }
732
733    #[pymethod]
734    fn rstrip(self, options: ByteInnerStripOptions, vm: &VirtualMachine) -> Self {
735        let inner = self.inner();
736        let stripped = inner.rstrip(options.bytes);
737        let elements = &inner.elements;
738        if stripped == elements {
739            drop(inner);
740            self
741        } else {
742            vm.ctx.new_pyref(PyByteArray::from(stripped.to_vec()))
743        }
744    }
745
746    #[pymethod]
747    fn decode(self, args: DecodeArgs, vm: &VirtualMachine) -> PyResult<PyStrRef> {
748        bytes_decode(self.into(), args, vm)
749    }
750}
751
752impl DefaultConstructor for PyByteArray {}
753
754impl Initializer for PyByteArray {
755    type Args = ByteInnerNewOptions;
756
757    fn init(zelf: &Py<Self>, options: Self::Args, vm: &VirtualMachine) -> PyResult<()> {
758        // First unpack bytearray and *then* get a lock to set it.
759        let mut inner = options.get_inner(bytearray_from_object, vm)?;
760        core::mem::swap(&mut *zelf.inner_mut(), &mut inner);
761        Ok(())
762    }
763}
764
765impl Comparable for PyByteArray {
766    fn cmp(
767        zelf: &Py<Self>,
768        other: &PyObject,
769        op: PyComparisonOp,
770        vm: &VirtualMachine,
771    ) -> PyResult<PyComparisonValue> {
772        if let Some(res) = op.identical_optimization(zelf, other) {
773            return Ok(res.into());
774        }
775        Ok(zelf.inner().cmp(other, op, vm))
776    }
777}
778
779static BUFFER_METHODS: BufferMethods = BufferMethods {
780    obj_bytes: |buffer| buffer.obj_as::<PyByteArray>().borrow_buf().into(),
781    obj_bytes_mut: |buffer| {
782        PyMappedDetachingRwLockWriteGuard::map(
783            buffer.obj_as::<PyByteArray>().borrow_buf_mut(),
784            |x| x.as_mut_slice(),
785        )
786        .into()
787    },
788    release: |buffer| {
789        buffer
790            .obj_as::<PyByteArray>()
791            .exports
792            .fetch_sub(1, Ordering::Release);
793    },
794    retain: |buffer| {
795        buffer
796            .obj_as::<PyByteArray>()
797            .exports
798            .fetch_add(1, Ordering::Release);
799    },
800};
801
802impl AsBuffer for PyByteArray {
803    const RELEASE_BUFFER: bool = true;
804
805    fn slot_as_buffer(
806        zelf: &PyObject,
807        flags: BufferFlags,
808        vm: &VirtualMachine,
809    ) -> PyResult<PyBuffer> {
810        let zelf = zelf
811            .downcast_ref::<Self>()
812            .ok_or_else(|| vm.new_type_error("unexpected payload for as_buffer"))?;
813        flags.fill_info_check(false, vm)?;
814        Self::as_buffer(zelf, vm)
815    }
816
817    fn as_buffer(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyBuffer> {
818        Ok(PyBuffer::new(
819            zelf.to_owned().into(),
820            BufferDescriptor::simple(zelf.__len__(), false),
821            &BUFFER_METHODS,
822        ))
823    }
824}
825
826impl BufferResizeGuard for PyByteArray {
827    type Resizable<'a> = PyDetachingRwLockWriteGuard<'a, PyBytesInner>;
828
829    fn try_resizable_opt(&self) -> Option<Self::Resizable<'_>> {
830        // An export is a borrow someone else still holds, so it is answered
831        // before the lock rather than by waiting on it.
832        (self.exports.load(Ordering::SeqCst) == 0).then(|| self.inner.write())
833    }
834}
835
836impl AsMapping for PyByteArray {
837    fn as_mapping() -> &'static PyMappingMethods {
838        static AS_MAPPING: PyMappingMethods = PyMappingMethods {
839            length: atomic_func!(|mapping, _vm| Ok(
840                PyByteArray::mapping_downcast(mapping).__len__()
841            )),
842            subscript: atomic_func!(|mapping, needle, vm| {
843                PyByteArray::mapping_downcast(mapping).__getitem__(needle, vm)
844            }),
845            ass_subscript: atomic_func!(|mapping, needle, value, vm| {
846                let zelf = PyByteArray::mapping_downcast(mapping);
847                if let Some(value) = value {
848                    zelf.__setitem__(needle, value, vm)
849                } else {
850                    zelf.__delitem__(needle, vm)
851                }
852            }),
853        };
854        &AS_MAPPING
855    }
856}
857
858impl AsSequence for PyByteArray {
859    fn as_sequence() -> &'static PySequenceMethods {
860        static AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
861            length: atomic_func!(|seq, _vm| Ok(PyByteArray::sequence_downcast(seq).__len__())),
862            concat: atomic_func!(|seq, other, vm| {
863                PyByteArray::sequence_downcast(seq)
864                    .inner()
865                    .concat(other, vm)
866                    .map(|x| PyByteArray::from(x).into_pyobject(vm))
867            }),
868            repeat: atomic_func!(|seq, n, vm| {
869                PyByteArray::sequence_downcast(seq)
870                    .repeat(n, vm)
871                    .map(|x| x.into_pyobject(vm))
872            }),
873            item: atomic_func!(|seq, i, vm| {
874                PyByteArray::sequence_downcast(seq)
875                    .borrow_buf()
876                    .getitem_by_index(vm, i)
877                    .map(|x| vm.ctx.new_bytes(vec![x]).into())
878            }),
879            ass_item: atomic_func!(|seq, i, value, vm| {
880                let zelf = PyByteArray::sequence_downcast(seq);
881                if let Some(value) = value {
882                    zelf._setitem_by_index(i, &value, vm)
883                } else {
884                    zelf.borrow_buf_mut().delitem_by_index(vm, i)
885                }
886            }),
887            contains: atomic_func!(|seq, other, vm| {
888                PyByteArray::sequence_downcast(seq).__contains__(other.to_owned(), vm)
889            }),
890            inplace_concat: atomic_func!(|seq, other, vm| {
891                let other = ArgBytesLike::try_from_object(vm, other.to_owned())?;
892                let zelf = PyByteArray::sequence_downcast(seq).to_owned();
893                PyByteArray::__iadd__(zelf, other, vm).map(|x| x.into())
894            }),
895            inplace_repeat: atomic_func!(|seq, n, vm| {
896                let zelf = PyByteArray::sequence_downcast(seq).to_owned();
897                PyByteArray::irepeat(&zelf, n, vm)?;
898                Ok(zelf.into())
899            }),
900        };
901        &AS_SEQUENCE
902    }
903}
904
905impl AsNumber for PyByteArray {
906    fn as_number() -> &'static PyNumberMethods {
907        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
908            remainder: Some(|a, b, vm| {
909                if let Some(a) = a.downcast_ref::<PyByteArray>() {
910                    a.__mod__(b.to_owned(), vm).to_pyresult(vm)
911                } else {
912                    Ok(vm.ctx.not_implemented())
913                }
914            }),
915            ..PyNumberMethods::NOT_IMPLEMENTED
916        };
917        &AS_NUMBER
918    }
919}
920
921impl Iterable for PyByteArray {
922    fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
923        Ok(PyByteArrayIterator {
924            internal: PyMutex::new(PositionIterInternal::new(zelf, 0)),
925        }
926        .into_pyobject(vm))
927    }
928}
929
930impl Representable for PyByteArray {
931    #[inline]
932    fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
933        let class = zelf.class();
934        let class_name = class.name();
935        zelf.inner().repr_with_name(&class_name, vm)
936    }
937}
938
939#[pyclass(module = false, name = "bytearray_iterator")]
940#[derive(Debug)]
941pub(crate) struct PyByteArrayIterator {
942    internal: PyMutex<PositionIterInternal<PyByteArrayRef>>,
943}
944
945impl PyPayload for PyByteArrayIterator {
946    #[inline]
947    fn class(ctx: &Context) -> &'static Py<PyType> {
948        ctx.types.bytearray_iterator_type
949    }
950}
951
952#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
953impl Py<PyByteArrayIterator> {
954    #[pymethod]
955    fn __length_hint__(&self) -> usize {
956        self.internal.lock().length_hint(|obj| obj.__len__())
957    }
958    #[pymethod]
959    fn __reduce__(&self, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
960        let func = builtins_iter(vm)?;
961        Ok(self.internal.lock().reduce(
962            func,
963            |x| x.clone().into(),
964            |vm| vm.ctx.empty_tuple.clone().into(),
965            vm,
966        ))
967    }
968
969    #[pymethod]
970    fn __setstate__(&self, object: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
971        self.internal
972            .lock()
973            .set_state(&object, |obj, pos| pos.min(obj.__len__()), vm)
974    }
975}
976
977impl SelfIter for PyByteArrayIterator {}
978impl IterNext for PyByteArrayIterator {
979    fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
980        locked_next(&zelf.internal, |bytearray, pos| {
981            let buf = bytearray.borrow_buf();
982            Ok(PyIterReturn::from_result(
983                buf.get(pos).map(|&x| vm.new_pyobj(x)).ok_or(None),
984            ))
985        })
986    }
987}