Skip to main content

rustpython_vm/builtins/
bytes.rs

1use super::{
2    PositionIterInternal, PyDictRef, PyGenericAlias, PyIntRef, PyStrRef, PyTuple, PyTupleRef,
3    PyType, PyTypeRef, iter::builtins_iter,
4};
5use crate::common::lock::LazyLock;
6use crate::{
7    AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
8    TryFromBorrowedObject, TryFromObject, VirtualMachine,
9    anystr::{self, AnyStr},
10    atomic_func,
11    bytes_inner::{
12        ByteInnerFindOptions, ByteInnerNewOptions, ByteInnerPaddingOptions, ByteInnerSplitOptions,
13        ByteInnerTranslateOptions, DecodeArgs, PyBytesInner, bytes_decode,
14    },
15    class::PyClassImpl,
16    common::{hash::PyHash, lock::PyMutex},
17    convert::{ToPyObject, ToPyResult},
18    function::{
19        ArgBytesLike, ArgIndex, ArgIterable, Either, FuncArgs, OptionalArg, OptionalOption,
20        PyComparisonValue,
21    },
22    protocol::{
23        BufferDescriptor, BufferMethods, PyBuffer, PyIterReturn, PyMappingMethods, PyNumberMethods,
24        PySequenceMethods,
25    },
26    sliceable::{SequenceIndex, SliceableSequenceOp},
27    types::{
28        AsBuffer, AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, Hashable,
29        IterNext, Iterable, PyComparisonOp, Representable, SelfIter,
30    },
31};
32use bstr::ByteSlice;
33use core::{mem::size_of, ops::Deref};
34
35#[pyclass(module = false, name = "bytes")]
36#[derive(Clone, Debug)]
37pub struct PyBytes {
38    inner: PyBytesInner,
39}
40
41pub type PyBytesRef = PyRef<PyBytes>;
42
43impl From<Vec<u8>> for PyBytes {
44    fn from(elements: Vec<u8>) -> Self {
45        Self {
46            inner: PyBytesInner { elements },
47        }
48    }
49}
50
51impl From<PyBytesInner> for PyBytes {
52    fn from(inner: PyBytesInner) -> Self {
53        Self { inner }
54    }
55}
56
57impl ToPyObject for Vec<u8> {
58    fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
59        vm.ctx.new_bytes(self).into()
60    }
61}
62
63impl Deref for PyBytes {
64    type Target = [u8];
65
66    fn deref(&self) -> &[u8] {
67        self.as_bytes()
68    }
69}
70
71impl AsRef<[u8]> for PyBytes {
72    fn as_ref(&self) -> &[u8] {
73        self.as_bytes()
74    }
75}
76impl AsRef<[u8]> for PyBytesRef {
77    fn as_ref(&self) -> &[u8] {
78        self.as_bytes()
79    }
80}
81
82impl PyPayload for PyBytes {
83    #[inline]
84    fn class(ctx: &Context) -> &'static Py<PyType> {
85        ctx.types.bytes_type
86    }
87}
88
89pub(crate) fn init(context: &'static Context) {
90    PyBytes::extend_class(context, context.types.bytes_type);
91    PyBytesIterator::extend_class(context, context.types.bytes_iterator_type);
92}
93
94impl Constructor for PyBytes {
95    type Args = Vec<u8>;
96
97    fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
98        let options: ByteInnerNewOptions = args.bind(vm)?;
99
100        // Optimizations for exact bytes type
101        if cls.is(vm.ctx.types.bytes_type) {
102            // Return empty bytes singleton
103            if options.source.is_missing()
104                && options.encoding.is_missing()
105                && options.errors.is_missing()
106            {
107                return Ok(vm.ctx.empty_bytes.clone().into());
108            }
109
110            // Return exact bytes as-is
111            if let OptionalArg::Present(ref obj) = options.source
112                && options.encoding.is_missing()
113                && options.errors.is_missing()
114                && let Ok(b) = obj.clone().downcast_exact::<PyBytes>(vm)
115            {
116                return Ok(b.into_pyref().into());
117            }
118        }
119
120        // Handle __bytes__ method - may return PyBytes directly
121        if let OptionalArg::Present(ref obj) = options.source
122            && options.encoding.is_missing()
123            && options.errors.is_missing()
124            && let Some(bytes_method) = vm.get_method(obj.clone(), identifier!(vm, __bytes__))
125        {
126            let bytes = bytes_method?.call((), vm)?;
127            // If exact bytes type and __bytes__ returns bytes, use it directly
128            if cls.is(vm.ctx.types.bytes_type)
129                && let Ok(b) = bytes.clone().downcast::<PyBytes>()
130            {
131                return Ok(b.into());
132            }
133            // Otherwise convert to Vec<u8>
134            let inner = PyBytesInner::try_from_borrowed_object(vm, &bytes)?;
135            let payload = Self::py_new(&cls, inner.elements, vm)?;
136            return payload.into_ref_with_type(vm, cls).map(Into::into);
137        }
138
139        // Fallback to get_bytearray_inner
140        let elements = options.get_bytearray_inner(vm)?.elements;
141
142        // Return empty bytes singleton for exact bytes types
143        if elements.is_empty() && cls.is(vm.ctx.types.bytes_type) {
144            return Ok(vm.ctx.empty_bytes.clone().into());
145        }
146
147        let payload = Self::py_new(&cls, elements, vm)?;
148        payload.into_ref_with_type(vm, cls).map(Into::into)
149    }
150
151    fn py_new(_cls: &Py<PyType>, elements: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
152        Ok(Self::from(elements))
153    }
154}
155
156impl PyBytes {
157    #[deprecated(note = "use PyBytes::from(...).into_ref() instead")]
158    pub fn new_ref(data: Vec<u8>, ctx: &Context) -> PyRef<Self> {
159        Self::from(data).into_ref(ctx)
160    }
161
162    fn _getitem(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult {
163        match SequenceIndex::try_from_borrowed_object(vm, needle, "byte")? {
164            SequenceIndex::Int(i) => self
165                .getitem_by_index(vm, i)
166                .map(|x| vm.ctx.new_int(x).into()),
167            SequenceIndex::Slice(slice) => self
168                .getitem_by_slice(vm, slice)
169                .map(|x| vm.ctx.new_bytes(x).into()),
170        }
171    }
172}
173
174impl PyRef<PyBytes> {
175    fn repeat(self, count: isize, vm: &VirtualMachine) -> PyResult<Self> {
176        if count == 1 && self.class().is(vm.ctx.types.bytes_type) {
177            // Special case: when some `bytes` is multiplied by `1`,
178            // nothing really happens, we need to return an object itself
179            // with the same `id()` to be compatible with CPython.
180            // This only works for `bytes` itself, not its subclasses.
181            return Ok(self);
182        }
183        self.inner
184            .mul(count, vm)
185            .map(|x| PyBytes::from(x).into_ref(&vm.ctx))
186    }
187}
188
189#[pyclass(
190    itemsize = 1,
191    flags(BASETYPE, _MATCH_SELF),
192    with(
193        Py,
194        PyRef,
195        AsMapping,
196        AsSequence,
197        Hashable,
198        Comparable,
199        AsBuffer,
200        Iterable,
201        Constructor,
202        AsNumber,
203        Representable,
204    )
205)]
206impl PyBytes {
207    #[inline]
208    pub const fn __len__(&self) -> usize {
209        self.inner.len()
210    }
211
212    #[inline]
213    pub const fn is_empty(&self) -> bool {
214        self.inner.is_empty()
215    }
216
217    #[inline]
218    pub fn as_bytes(&self) -> &[u8] {
219        self.inner.as_bytes()
220    }
221
222    #[pymethod]
223    fn __sizeof__(&self) -> usize {
224        size_of::<Self>() + self.len() * size_of::<u8>()
225    }
226
227    #[pyslot]
228    fn slot_str(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyStrRef> {
229        let zelf = zelf.downcast_ref::<Self>().expect("expected bytes");
230        PyBytesInner::warn_on_str("str() on a bytes instance", vm)?;
231        Ok(vm.ctx.new_str(zelf.inner.repr_bytes(vm)?))
232    }
233
234    fn __add__(&self, other: ArgBytesLike) -> Vec<u8> {
235        self.inner.add(&other.borrow_buf())
236    }
237
238    fn __contains__(
239        &self,
240        needle: Either<PyBytesInner, PyIntRef>,
241        vm: &VirtualMachine,
242    ) -> PyResult<bool> {
243        self.inner.contains(needle, vm)
244    }
245
246    #[pystaticmethod]
247    fn maketrans(from: PyBytesInner, to: PyBytesInner, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
248        PyBytesInner::maketrans(from, to, vm)
249    }
250
251    fn __getitem__(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
252        self._getitem(&needle, vm)
253    }
254
255    #[pymethod]
256    fn isalnum(&self) -> bool {
257        self.inner.isalnum()
258    }
259
260    #[pymethod]
261    fn isalpha(&self) -> bool {
262        self.inner.isalpha()
263    }
264
265    #[pymethod]
266    fn isascii(&self) -> bool {
267        self.inner.isascii()
268    }
269
270    #[pymethod]
271    fn isdigit(&self) -> bool {
272        self.inner.isdigit()
273    }
274
275    #[pymethod]
276    fn islower(&self) -> bool {
277        self.inner.islower()
278    }
279
280    #[pymethod]
281    fn isspace(&self) -> bool {
282        self.inner.isspace()
283    }
284
285    #[pymethod]
286    fn isupper(&self) -> bool {
287        self.inner.isupper()
288    }
289
290    #[pymethod]
291    fn istitle(&self) -> bool {
292        self.inner.istitle()
293    }
294
295    #[pymethod]
296    fn lower(&self) -> Self {
297        self.inner.lower().into()
298    }
299
300    #[pymethod]
301    fn upper(&self) -> Self {
302        self.inner.upper().into()
303    }
304
305    #[pymethod]
306    fn capitalize(&self) -> Self {
307        self.inner.capitalize().into()
308    }
309
310    #[pymethod]
311    fn swapcase(&self) -> Self {
312        self.inner.swapcase().into()
313    }
314
315    #[pymethod]
316    pub(crate) fn hex(
317        &self,
318        sep: OptionalArg<Either<PyStrRef, PyBytesRef>>,
319        bytes_per_sep: OptionalArg<isize>,
320        vm: &VirtualMachine,
321    ) -> PyResult<String> {
322        self.inner.hex(sep, bytes_per_sep, vm)
323    }
324
325    #[pyclassmethod]
326    fn fromhex(cls: PyTypeRef, string: PyObjectRef, vm: &VirtualMachine) -> PyResult {
327        let bytes = PyBytesInner::fromhex_object(string, vm)?;
328        let bytes = vm.ctx.new_bytes(bytes).into();
329        PyType::call(&cls, vec![bytes].into(), vm)
330    }
331
332    #[pymethod]
333    fn center(&self, options: ByteInnerPaddingOptions, vm: &VirtualMachine) -> PyResult<Self> {
334        Ok(self.inner.center(options, vm)?.into())
335    }
336
337    #[pymethod]
338    fn ljust(&self, options: ByteInnerPaddingOptions, vm: &VirtualMachine) -> PyResult<Self> {
339        Ok(self.inner.ljust(options, vm)?.into())
340    }
341
342    #[pymethod]
343    fn rjust(&self, options: ByteInnerPaddingOptions, vm: &VirtualMachine) -> PyResult<Self> {
344        Ok(self.inner.rjust(options, vm)?.into())
345    }
346
347    #[pymethod]
348    fn count(&self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult<usize> {
349        self.inner.count(options, vm)
350    }
351
352    #[pymethod]
353    fn join(&self, iter: ArgIterable<PyBytesInner>, vm: &VirtualMachine) -> PyResult<Self> {
354        Ok(self.inner.join(iter, vm)?.into())
355    }
356
357    #[pymethod]
358    fn endswith(&self, options: anystr::StartsEndsWithArgs, vm: &VirtualMachine) -> PyResult<bool> {
359        let (affix, substr) =
360            match options.prepare(self.as_bytes(), self.len(), |s, r| s.get_bytes(r)) {
361                Some(x) => x,
362                None => return Ok(false),
363            };
364        substr.py_starts_ends_with(
365            &affix,
366            "endswith",
367            "bytes",
368            |s, x: PyBytesInner| s.ends_with(x.as_bytes()),
369            vm,
370        )
371    }
372
373    #[pymethod]
374    fn startswith(
375        &self,
376        options: anystr::StartsEndsWithArgs,
377        vm: &VirtualMachine,
378    ) -> PyResult<bool> {
379        let (affix, substr) =
380            match options.prepare(self.as_bytes(), self.len(), |s, r| s.get_bytes(r)) {
381                Some(x) => x,
382                None => return Ok(false),
383            };
384        substr.py_starts_ends_with(
385            &affix,
386            "startswith",
387            "bytes",
388            |s, x: PyBytesInner| s.starts_with(x.as_bytes()),
389            vm,
390        )
391    }
392
393    #[pymethod]
394    fn find(&self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult<isize> {
395        let index = self.inner.find(options, |h, n| h.find(n), vm)?;
396        Ok(index.map_or(-1, |v| v as isize))
397    }
398
399    #[pymethod]
400    fn index(&self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult<usize> {
401        let index = self.inner.find(options, |h, n| h.find(n), vm)?;
402        index.ok_or_else(|| vm.new_value_error("substring not found"))
403    }
404
405    #[pymethod]
406    fn rfind(&self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult<isize> {
407        let index = self.inner.find(options, |h, n| h.rfind(n), vm)?;
408        Ok(index.map_or(-1, |v| v as isize))
409    }
410
411    #[pymethod]
412    fn rindex(&self, options: ByteInnerFindOptions, vm: &VirtualMachine) -> PyResult<usize> {
413        let index = self.inner.find(options, |h, n| h.rfind(n), vm)?;
414        index.ok_or_else(|| vm.new_value_error("substring not found"))
415    }
416
417    #[pymethod]
418    fn translate(&self, options: ByteInnerTranslateOptions, vm: &VirtualMachine) -> PyResult<Self> {
419        Ok(self.inner.translate(options, vm)?.into())
420    }
421
422    #[pymethod]
423    fn strip(&self, chars: OptionalOption<PyBytesInner>) -> Self {
424        self.inner.strip(chars).into()
425    }
426
427    #[pymethod]
428    fn removeprefix(&self, prefix: PyBytesInner) -> Self {
429        self.inner.removeprefix(prefix).into()
430    }
431
432    #[pymethod]
433    fn removesuffix(&self, suffix: PyBytesInner) -> Self {
434        self.inner.removesuffix(suffix).into()
435    }
436
437    #[pymethod]
438    fn split(
439        &self,
440        options: ByteInnerSplitOptions,
441        vm: &VirtualMachine,
442    ) -> PyResult<Vec<PyObjectRef>> {
443        self.inner
444            .split(options, |s, vm| vm.ctx.new_bytes(s.to_vec()).into(), vm)
445    }
446
447    #[pymethod]
448    fn rsplit(
449        &self,
450        options: ByteInnerSplitOptions,
451        vm: &VirtualMachine,
452    ) -> PyResult<Vec<PyObjectRef>> {
453        self.inner
454            .rsplit(options, |s, vm| vm.ctx.new_bytes(s.to_vec()).into(), vm)
455    }
456
457    #[pymethod]
458    fn partition(&self, sep: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
459        let sub = PyBytesInner::try_from_borrowed_object(vm, &sep)?;
460        let (front, has_mid, back) = self.inner.partition(&sub, vm)?;
461        Ok(vm.new_tuple((
462            vm.ctx.new_bytes(front),
463            if has_mid {
464                sep
465            } else {
466                vm.ctx.new_bytes(Vec::new()).into()
467            },
468            vm.ctx.new_bytes(back),
469        )))
470    }
471
472    #[pymethod]
473    fn rpartition(&self, sep: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
474        let sub = PyBytesInner::try_from_borrowed_object(vm, &sep)?;
475        let (back, has_mid, front) = self.inner.rpartition(&sub, vm)?;
476        Ok(vm.new_tuple((
477            vm.ctx.new_bytes(front),
478            if has_mid {
479                sep
480            } else {
481                vm.ctx.new_bytes(Vec::new()).into()
482            },
483            vm.ctx.new_bytes(back),
484        )))
485    }
486
487    #[pymethod]
488    fn expandtabs(&self, options: anystr::ExpandTabsArgs) -> Self {
489        self.inner.expandtabs(options).into()
490    }
491
492    #[pymethod]
493    fn splitlines(&self, options: anystr::SplitLinesArgs, vm: &VirtualMachine) -> Vec<PyObjectRef> {
494        self.inner
495            .splitlines(options, |x| vm.ctx.new_bytes(x.to_vec()).into())
496    }
497
498    #[pymethod]
499    fn zfill(&self, width: isize) -> Self {
500        self.inner.zfill(width).into()
501    }
502
503    #[pymethod]
504    fn replace(
505        &self,
506        old: PyBytesInner,
507        new: PyBytesInner,
508        count: OptionalArg<isize>,
509        vm: &VirtualMachine,
510    ) -> PyResult<Self> {
511        Ok(self.inner.replace(old, new, count, vm)?.into())
512    }
513
514    #[pymethod]
515    fn title(&self) -> Self {
516        self.inner.title().into()
517    }
518
519    fn __mul__(zelf: PyRef<Self>, value: ArgIndex, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
520        zelf.repeat(value.into_int_ref().try_to_primitive(vm)?, vm)
521    }
522
523    fn __mod__(&self, values: PyObjectRef, vm: &VirtualMachine) -> PyResult<Self> {
524        let formatted = self.inner.cformat(values, vm)?;
525        Ok(formatted.into())
526    }
527
528    #[pymethod]
529    fn __getnewargs__(&self, vm: &VirtualMachine) -> PyTupleRef {
530        let param: Vec<PyObjectRef> = self.elements().map(|x| x.to_pyobject(vm)).collect();
531        PyTuple::new_ref(param, &vm.ctx)
532    }
533
534    // TODO: Uncomment when Python adds __class_getitem__ to bytes
535    // #[pyclassmethod]
536    fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
537        PyGenericAlias::from_args(cls, args, vm)
538    }
539}
540
541#[pyclass]
542impl Py<PyBytes> {
543    #[pymethod]
544    fn __reduce_ex__(
545        &self,
546        _proto: usize,
547        vm: &VirtualMachine,
548    ) -> (PyTypeRef, PyTupleRef, Option<PyDictRef>) {
549        self.__reduce__(vm)
550    }
551
552    #[pymethod]
553    fn __reduce__(&self, vm: &VirtualMachine) -> (PyTypeRef, PyTupleRef, Option<PyDictRef>) {
554        let bytes = PyBytes::from(self.to_vec()).to_pyobject(vm);
555        (
556            self.class().to_owned(),
557            PyTuple::new_ref(vec![bytes], &vm.ctx),
558            self.as_object().dict(),
559        )
560    }
561}
562
563#[pyclass]
564impl PyRef<PyBytes> {
565    #[pymethod]
566    fn __bytes__(self, vm: &VirtualMachine) -> Self {
567        if self.is(vm.ctx.types.bytes_type) {
568            self
569        } else {
570            PyBytes::from(self.inner.clone()).into_ref(&vm.ctx)
571        }
572    }
573
574    #[pymethod]
575    fn lstrip(self, chars: OptionalOption<PyBytesInner>, vm: &VirtualMachine) -> Self {
576        let stripped = self.inner.lstrip(chars);
577        if stripped == self.as_bytes() {
578            self
579        } else {
580            vm.ctx.new_bytes(stripped.to_vec())
581        }
582    }
583
584    #[pymethod]
585    fn rstrip(self, chars: OptionalOption<PyBytesInner>, vm: &VirtualMachine) -> Self {
586        let stripped = self.inner.rstrip(chars);
587        if stripped == self.as_bytes() {
588            self
589        } else {
590            vm.ctx.new_bytes(stripped.to_vec())
591        }
592    }
593
594    /// Return a string decoded from the given bytes.
595    /// Default encoding is 'utf-8'.
596    /// Default errors is 'strict', meaning that encoding errors raise a UnicodeError.
597    /// Other possible values are 'ignore', 'replace'
598    /// For a list of possible encodings,
599    /// see https://docs.python.org/3/library/codecs.html#standard-encodings
600    /// currently, only 'utf-8' and 'ascii' implemented
601    #[pymethod]
602    fn decode(self, args: DecodeArgs, vm: &VirtualMachine) -> PyResult<PyStrRef> {
603        bytes_decode(self.into(), args, vm)
604    }
605}
606
607static BUFFER_METHODS: BufferMethods = BufferMethods {
608    obj_bytes: |buffer| buffer.obj_as::<PyBytes>().as_bytes().into(),
609    obj_bytes_mut: |_| panic!(),
610    release: |_| {},
611    retain: |_| {},
612};
613
614impl AsBuffer for PyBytes {
615    fn as_buffer(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyBuffer> {
616        let buf = PyBuffer::new(
617            zelf.to_owned().into(),
618            BufferDescriptor::simple(zelf.len(), true),
619            &BUFFER_METHODS,
620        );
621        Ok(buf)
622    }
623}
624
625impl AsMapping for PyBytes {
626    fn as_mapping() -> &'static PyMappingMethods {
627        static AS_MAPPING: LazyLock<PyMappingMethods> = LazyLock::new(|| PyMappingMethods {
628            length: atomic_func!(|mapping, _vm| Ok(PyBytes::mapping_downcast(mapping).len())),
629            subscript: atomic_func!(
630                |mapping, needle, vm| PyBytes::mapping_downcast(mapping)._getitem(needle, vm)
631            ),
632            ..PyMappingMethods::NOT_IMPLEMENTED
633        });
634        &AS_MAPPING
635    }
636}
637
638impl AsSequence for PyBytes {
639    fn as_sequence() -> &'static PySequenceMethods {
640        static AS_SEQUENCE: LazyLock<PySequenceMethods> = LazyLock::new(|| PySequenceMethods {
641            length: atomic_func!(|seq, _vm| Ok(PyBytes::sequence_downcast(seq).len())),
642            concat: atomic_func!(|seq, other, vm| {
643                PyBytes::sequence_downcast(seq)
644                    .inner
645                    .concat(other, vm)
646                    .map(|x| vm.ctx.new_bytes(x).into())
647            }),
648            repeat: atomic_func!(|seq, n, vm| {
649                let zelf = seq.obj.to_owned().downcast::<PyBytes>().map_err(|_| {
650                    vm.new_type_error("bad argument type for built-in operation".to_owned())
651                })?;
652                zelf.repeat(n, vm).to_pyresult(vm)
653            }),
654            item: atomic_func!(|seq, i, vm| {
655                PyBytes::sequence_downcast(seq)
656                    .as_bytes()
657                    .getitem_by_index(vm, i)
658                    .map(|x| vm.ctx.new_bytes(vec![x]).into())
659            }),
660            contains: atomic_func!(|seq, other, vm| {
661                let other =
662                    <Either<PyBytesInner, PyIntRef>>::try_from_object(vm, other.to_owned())?;
663                PyBytes::sequence_downcast(seq).__contains__(other, vm)
664            }),
665            ..PySequenceMethods::NOT_IMPLEMENTED
666        });
667        &AS_SEQUENCE
668    }
669}
670
671impl AsNumber for PyBytes {
672    fn as_number() -> &'static PyNumberMethods {
673        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
674            remainder: Some(|a, b, vm| {
675                if let Some(a) = a.downcast_ref::<PyBytes>() {
676                    a.__mod__(b.to_owned(), vm).to_pyresult(vm)
677                } else {
678                    Ok(vm.ctx.not_implemented())
679                }
680            }),
681            ..PyNumberMethods::NOT_IMPLEMENTED
682        };
683        &AS_NUMBER
684    }
685}
686
687impl Hashable for PyBytes {
688    #[inline]
689    fn hash(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyHash> {
690        Ok(zelf.inner.hash(vm))
691    }
692}
693
694impl Comparable for PyBytes {
695    fn cmp(
696        zelf: &Py<Self>,
697        other: &PyObject,
698        op: PyComparisonOp,
699        vm: &VirtualMachine,
700    ) -> PyResult<PyComparisonValue> {
701        Ok(if let Some(res) = op.identical_optimization(zelf, other) {
702            res.into()
703        } else if other.fast_isinstance(vm.ctx.types.memoryview_type)
704            && op != PyComparisonOp::Eq
705            && op != PyComparisonOp::Ne
706        {
707            return Err(vm.new_type_error(format!(
708                "'{}' not supported between instances of '{}' and '{}'",
709                op.operator_token(),
710                zelf.class().name(),
711                other.class().name()
712            )));
713        } else {
714            zelf.inner.cmp(other, op, vm)
715        })
716    }
717}
718
719impl Iterable for PyBytes {
720    fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
721        Ok(PyBytesIterator {
722            internal: PyMutex::new(PositionIterInternal::new(zelf, 0)),
723        }
724        .into_pyobject(vm))
725    }
726}
727
728impl Representable for PyBytes {
729    #[inline]
730    fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
731        zelf.inner.repr_bytes(vm)
732    }
733}
734
735#[pyclass(module = false, name = "bytes_iterator")]
736#[derive(Debug)]
737pub struct PyBytesIterator {
738    internal: PyMutex<PositionIterInternal<PyBytesRef>>,
739}
740
741impl PyPayload for PyBytesIterator {
742    #[inline]
743    fn class(ctx: &Context) -> &'static Py<PyType> {
744        ctx.types.bytes_iterator_type
745    }
746}
747
748#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
749impl PyBytesIterator {
750    #[pymethod]
751    fn __length_hint__(&self) -> usize {
752        self.internal.lock().length_hint(|obj| obj.len())
753    }
754
755    #[pymethod]
756    fn __reduce__(&self, vm: &VirtualMachine) -> PyTupleRef {
757        let func = builtins_iter(vm);
758        self.internal.lock().reduce(
759            func,
760            |x| x.clone().into(),
761            |vm| vm.ctx.empty_tuple.clone().into(),
762            vm,
763        )
764    }
765
766    #[pymethod]
767    fn __setstate__(&self, state: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
768        self.internal
769            .lock()
770            .set_state(state, |obj, pos| pos.min(obj.len()), vm)
771    }
772}
773
774impl SelfIter for PyBytesIterator {}
775impl IterNext for PyBytesIterator {
776    fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
777        zelf.internal.lock().next(|bytes, pos| {
778            Ok(PyIterReturn::from_result(
779                bytes
780                    .as_bytes()
781                    .get(pos)
782                    .map(|&x| vm.new_pyobj(x))
783                    .ok_or(None),
784            ))
785        })
786    }
787}
788
789impl<'a> TryFromBorrowedObject<'a> for PyBytes {
790    fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<Self> {
791        PyBytesInner::try_from_borrowed_object(vm, obj).map(|x| x.into())
792    }
793}