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