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 if cls.is(vm.ctx.types.bytes_type) {
102 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 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 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 cls.is(vm.ctx.types.bytes_type)
129 && let Ok(b) = bytes.clone().downcast::<PyBytes>()
130 {
131 return Ok(b.into());
132 }
133 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 let elements = options.get_bytearray_inner(vm)?.elements;
141
142 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 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 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 #[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}