1use 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
68pub(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 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 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 let format = self.inner().clone();
226 let formatted = format.cformat(values, vm)?;
227 Ok(formatted.into())
228 }
229
230 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 let buffer = iterable_of_ints
326 .check_buffer()
327 .then(|| {
328 PyBuffer::from_object(vm, &iterable_of_ints, BufferFlags::SIMPLE).map_err(|_| {
329 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 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 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 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 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 (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}