1use super::{
2 PositionIterInternal, PyGenericAlias, PyInt, PyStrRef, PyType, PyTypeRef, iter::builtins_iter,
3 locked_next,
4};
5use crate::common::lock::LazyLock;
6use crate::common::{hash, hash::PyHash, lock::PyMutex, wtf8::wtf8_concat};
7use crate::object::{Traverse, TraverseFn};
8use crate::{
9 AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
10 atomic_func,
11 class::{PyClassDef, PyClassImpl},
12 convert::{ToPyObject, TransmuteFromObject},
13 function::{FuncArgs, OptionalArg, PyArithmeticValue, PyComparisonValue, PySsize},
14 iter::PyExactSizeIterator,
15 protocol::{PyIterReturn, PyMappingMethods, PyNumberMethods, PySequenceMethods},
16 recursion::ReprGuard,
17 sequence::{OptionalRangeArgs, SequenceExt},
18 sliceable::{SequenceIndex, SliceableSequenceOp},
19 types::{
20 AsMapping, AsNumber, AsSequence, Comparable, Constructor, Hashable, IterNext, Iterable,
21 PyComparisonOp, Representable, SelfIter,
22 },
23 utils::collection_repr,
24 vm::VirtualMachine,
25};
26use alloc::fmt;
27use core::cell::{Cell, UnsafeCell};
28use core::ptr::NonNull;
29
30#[pyclass(module = false, name = "tuple", traverse = "manual")]
31pub struct PyTuple<R = PyObjectRef> {
32 elements: TupleElements<R>,
33}
34
35struct TupleElements<R>(UnsafeCell<Box<[R]>>);
39
40unsafe impl<R: Send> Send for TupleElements<R> {}
41unsafe impl<R: Sync> Sync for TupleElements<R> {}
42
43impl<R> TupleElements<R> {
44 const fn new(elements: Box<[R]>) -> Self {
45 Self(UnsafeCell::new(elements))
46 }
47
48 fn as_slice(&self) -> &[R] {
49 unsafe { &*self.0.get() }
52 }
53
54 fn get_mut(&mut self) -> &mut Box<[R]> {
55 self.0.get_mut()
56 }
57
58 unsafe fn set_initializing(&self, index: usize, value: R) {
62 unsafe { (*self.0.get())[index] = value };
63 }
64}
65
66impl<R> core::ops::Deref for TupleElements<R> {
67 type Target = [R];
68
69 fn deref(&self) -> &Self::Target {
70 self.as_slice()
71 }
72}
73
74impl<'a, R> IntoIterator for &'a TupleElements<R> {
75 type Item = &'a R;
76 type IntoIter = core::slice::Iter<'a, R>;
77
78 fn into_iter(self) -> Self::IntoIter {
79 self.iter()
80 }
81}
82
83impl<R> fmt::Debug for PyTuple<R> {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 f.write_str("tuple")
87 }
88}
89
90unsafe impl Traverse for PyTuple {
93 fn traverse(&self, traverse_fn: &mut TraverseFn<'_>) {
94 self.elements.as_slice().traverse(traverse_fn);
95 }
96
97 fn clear(&mut self, out: &mut Vec<PyObjectRef>) {
98 let elements = core::mem::take(self.elements.get_mut());
99 if out.is_empty() {
100 *out = elements.into_vec();
102 } else {
103 out.extend(elements.into_vec());
104 }
105 }
106}
107
108thread_local! {
109 static TUPLE_FREELIST: Cell<crate::object::FreeList<PyTuple>> =
115 const { Cell::new(crate::object::FreeList::new()) };
116}
117
118impl PyPayload for PyTuple {
119 const MAX_FREELIST: usize = 2000;
120 const HAS_FREELIST: bool = true;
121
122 #[inline]
123 fn class(ctx: &Context) -> &'static Py<PyType> {
124 ctx.types.tuple_type
125 }
126
127 #[inline]
128 unsafe fn freelist_push(obj: *mut PyObject) -> bool {
129 TUPLE_FREELIST
130 .try_with(|fl| {
131 let mut list = fl.take();
132 let stored = if list.len() < Self::MAX_FREELIST {
133 list.push(obj);
134 true
135 } else {
136 false
137 };
138 fl.set(list);
139 stored
140 })
141 .unwrap_or(false)
142 }
143
144 #[inline]
145 unsafe fn freelist_pop(_payload: &Self) -> Option<NonNull<PyObject>> {
146 TUPLE_FREELIST
147 .try_with(|fl| {
148 let mut list = fl.take();
149 let result = list.pop().map(|p| unsafe { NonNull::new_unchecked(p) });
150 fl.set(list);
151 result
152 })
153 .ok()
154 .flatten()
155 }
156}
157
158pub trait IntoPyTuple {
159 fn into_pytuple(self, vm: &VirtualMachine) -> PyTupleRef;
160}
161
162impl IntoPyTuple for () {
163 fn into_pytuple(self, vm: &VirtualMachine) -> PyTupleRef {
164 vm.ctx.empty_tuple.clone()
165 }
166}
167
168impl IntoPyTuple for Vec<PyObjectRef> {
169 fn into_pytuple(self, vm: &VirtualMachine) -> PyTupleRef {
170 PyTuple::new_ref(self, &vm.ctx)
171 }
172}
173
174pub trait FromPyTuple<'a>: Sized {
175 fn from_pytuple(tuple: &'a Py<PyTuple>, vm: &VirtualMachine) -> PyResult<Self>;
176}
177
178macro_rules! impl_from_into_pytuple {
179 ($($T:ident),+) => {
180 impl<$($T: ToPyObject),*> IntoPyTuple for ($($T,)*) {
181 fn into_pytuple(self, vm: &VirtualMachine) -> PyTupleRef {
182 #[allow(non_snake_case)]
183 let ($($T,)*) = self;
184 PyTuple::new_ref(vec![$($T.to_pyobject(vm)),*], &vm.ctx)
185 }
186 }
187
188 impl<'a, $($T: TryFromObject),*> FromPyTuple<'a> for ($($T,)*) {
191 fn from_pytuple(tuple: &'a Py<PyTuple>, vm: &VirtualMachine) -> PyResult<Self> {
192 #[allow(non_snake_case)]
193 let &[$(ref $T),+] = tuple.as_slice().try_into().map_err(|_| {
194 vm.new_type_error(format!("expected tuple with {} elements", impl_from_into_pytuple!(@count $($T)+)))
195 })?;
196 Ok(($($T::try_from_object(vm, $T.clone())?,)+))
197
198 }
199 }
200
201 impl<$($T: ToPyObject),*> ToPyObject for ($($T,)*) {
202 fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
203 self.into_pytuple(vm).into()
204 }
205 }
206 };
207 (@count $($T:ident)+) => {
208 0 $(+ impl_from_into_pytuple!(@discard $T))+
209 };
210 (@discard $T:ident) => {
211 1
212 };
213}
214
215impl_from_into_pytuple!(A);
216impl_from_into_pytuple!(A, B);
217impl_from_into_pytuple!(A, B, C);
218impl_from_into_pytuple!(A, B, C, D);
219impl_from_into_pytuple!(A, B, C, D, E);
220impl_from_into_pytuple!(A, B, C, D, E, F);
221impl_from_into_pytuple!(A, B, C, D, E, F, G);
222
223pub type PyTupleRef = PyRef<PyTuple>;
224
225impl Constructor for PyTuple {
226 type Args = crate::function::PositionalIterable;
227
228 fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
229 let tuple_type = vm.ctx.types.tuple_type;
230 let uses_tuple_init = {
231 let cls_init = cls.slots.init.load().map(crate::types::fn_addr);
232 let tuple_init = tuple_type.slots.init.load().map(crate::types::fn_addr);
233 cls_init == tuple_init
234 };
235 let parsed: Self::Args = if cls.is(tuple_type) || uses_tuple_init {
236 args.bind_for(vm, Self::NAME)?
237 } else {
238 match args.args.as_slice() {
239 [] => Self::Args {
240 iterable: OptionalArg::Missing,
241 },
242 [iterable] => Self::Args {
243 iterable: OptionalArg::Present(iterable.clone()),
244 },
245 slice => {
246 return Err(vm.new_arity_type_error(Self::NAME, 0..=1, slice.len()));
247 }
248 }
249 };
250 let iterable = parsed.iterable;
251
252 if cls.is(vm.ctx.types.tuple_type) {
254 if let OptionalArg::Present(ref input) = iterable
256 && let Ok(tuple) = input.clone().downcast_exact::<Self>(vm)
257 {
258 return Ok(tuple.into_pyref().into());
259 }
260
261 if iterable.is_missing() {
263 return Ok(vm.ctx.empty_tuple.clone().into());
264 }
265 }
266
267 let elements = if let OptionalArg::Present(iterable) = iterable {
268 iterable.try_to_value(vm)?
269 } else {
270 vec![]
271 };
272
273 if elements.is_empty() && cls.is(vm.ctx.types.tuple_type) {
275 return Ok(vm.ctx.empty_tuple.clone().into());
276 }
277
278 let payload = Self::from_elements(elements);
279 payload.into_ref_with_type(vm, cls).map(Into::into)
280 }
281
282 fn py_new(_cls: &Py<PyType>, args: Self::Args, vm: &VirtualMachine) -> PyResult<Self> {
283 let elements = if let OptionalArg::Present(iterable) = args.iterable {
284 iterable.try_to_value(vm)?
285 } else {
286 Vec::new()
287 };
288 Ok(Self::from_elements(elements))
289 }
290}
291
292impl PyTuple {
293 fn from_elements(elements: Vec<PyObjectRef>) -> Self {
294 Self {
295 elements: TupleElements::new(elements.into_boxed_slice()),
296 }
297 }
298}
299
300impl<R> AsRef<[R]> for PyTuple<R> {
301 fn as_ref(&self) -> &[R] {
302 &self.elements
303 }
304}
305
306impl<R> core::ops::Deref for PyTuple<R> {
307 type Target = [R];
308
309 fn deref(&self) -> &[R] {
310 &self.elements
311 }
312}
313
314impl<'a, R> core::iter::IntoIterator for &'a PyTuple<R> {
315 type Item = &'a R;
316 type IntoIter = core::slice::Iter<'a, R>;
317
318 fn into_iter(self) -> Self::IntoIter {
319 self.as_slice().iter()
320 }
321}
322
323impl<'a, R> core::iter::IntoIterator for &'a Py<PyTuple<R>> {
324 type Item = &'a R;
325 type IntoIter = core::slice::Iter<'a, R>;
326
327 fn into_iter(self) -> Self::IntoIter {
328 self.as_slice().iter()
329 }
330}
331
332impl<R> PyTuple<R> {
333 #[must_use]
334 pub fn as_slice(&self) -> &[R] {
335 &self.elements
336 }
337}
338
339impl PyTuple<PyObjectRef> {
340 pub fn new_ref(elements: Vec<PyObjectRef>, ctx: &Context) -> PyRef<Self> {
342 if elements.is_empty() {
343 ctx.empty_tuple.clone()
344 } else {
345 let elements = elements.into_boxed_slice();
346 PyRef::new_ref(
347 Self {
348 elements: TupleElements::new(elements),
349 },
350 ctx.types.tuple_type.to_owned(),
351 None,
352 )
353 }
354 }
355
356 #[must_use]
360 pub const fn new_unchecked(elements: Box<[PyObjectRef]>) -> Self {
361 Self {
362 elements: TupleElements::new(elements),
363 }
364 }
365
366 pub(crate) unsafe fn set_marshal_item(&self, index: usize, value: PyObjectRef) {
370 unsafe { self.elements.set_initializing(index, value) };
371 }
372
373 fn repeat(zelf: PyRef<Self>, value: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
374 Ok(if zelf.as_slice().is_empty() || value == 0 {
375 vm.ctx.empty_tuple.clone()
376 } else if value == 1 && zelf.class().is(vm.ctx.types.tuple_type) {
377 zelf
382 } else {
383 let v = zelf.as_slice().mul(vm, value)?;
384 let elements = v.into_boxed_slice();
385 Self {
386 elements: TupleElements::new(elements),
387 }
388 .into_ref(&vm.ctx)
389 })
390 }
391}
392
393impl<R> Py<PyTuple<R>> {
394 #[inline]
395 pub fn as_slice(&self) -> &[R] {
396 self.payload.as_slice()
397 }
398}
399
400impl Py<PyTuple> {
401 pub fn extract_tuple<'a, T: FromPyTuple<'a>>(&'a self, vm: &VirtualMachine) -> PyResult<T> {
402 T::from_pytuple(self, vm)
403 }
404}
405
406impl<T> PyTuple<PyRef<T>> {
407 pub(crate) fn new_ref_typed_with_type(
408 elements: Vec<PyRef<T>>,
409 tuple_type: PyTypeRef,
410 ) -> PyRef<Self> {
411 unsafe {
413 let elements: Vec<PyObjectRef> =
414 core::mem::transmute::<Vec<PyRef<T>>, Vec<PyObjectRef>>(elements);
415 let tuple = PyRef::new_ref(
416 PyTuple::new_unchecked(elements.into_boxed_slice()),
417 tuple_type,
418 None,
419 );
420 core::mem::transmute::<PyRef<PyTuple>, PyRef<Self>>(tuple)
421 }
422 }
423
424 pub fn new_ref_typed(elements: Vec<PyRef<T>>, ctx: &Context) -> PyRef<Self> {
425 unsafe {
427 let elements: Vec<PyObjectRef> =
428 core::mem::transmute::<Vec<PyRef<T>>, Vec<PyObjectRef>>(elements);
429 let tuple = PyTuple::<PyObjectRef>::new_ref(elements, ctx);
430 core::mem::transmute::<PyRef<PyTuple>, PyRef<Self>>(tuple)
431 }
432 }
433}
434
435impl PyTuple {
436 fn __add__(
437 zelf: PyRef<Self>,
438 other: PyObjectRef,
439 vm: &VirtualMachine,
440 ) -> PyArithmeticValue<PyRef<Self>> {
441 let added = other.downcast::<Self>().map(|other| {
442 if other.as_slice().is_empty() && zelf.class().is(vm.ctx.types.tuple_type) {
443 zelf
444 } else if zelf.as_slice().is_empty() && other.class().is(vm.ctx.types.tuple_type) {
445 other
446 } else {
447 let elements = zelf
448 .as_slice()
449 .iter()
450 .chain(other.as_slice())
451 .cloned()
452 .collect::<Box<[_]>>();
453 Self {
454 elements: TupleElements::new(elements),
455 }
456 .into_ref(&vm.ctx)
457 }
458 });
459 PyArithmeticValue::from_option(added.ok())
460 }
461
462 #[inline]
463 #[must_use]
464 pub fn __len__(&self) -> usize {
465 self.as_slice().len()
466 }
467
468 fn __mul__(zelf: PyRef<Self>, value: PySsize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
469 Self::repeat(zelf, value, vm)
470 }
471
472 fn _getitem(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult {
473 match SequenceIndex::try_from_borrowed_object(vm, needle, "tuple")? {
474 SequenceIndex::Int(i) => {
475 let index = self
476 .elements
477 .wrap_index(i)
478 .ok_or_else(|| vm.new_index_error("tuple index out of range"))?;
479 Ok(self.elements[index].clone())
480 }
481 SequenceIndex::Slice(slice) => self
482 .elements
483 .getitem_by_slice(vm, slice)
484 .map(|x| vm.ctx.new_tuple(x).into()),
485 }
486 }
487
488 fn __getitem__(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult {
489 self._getitem(needle, vm)
490 }
491
492 fn _contains(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
493 for element in &self.elements {
494 if vm.identical_or_equal(element, needle)? {
495 return Ok(true);
496 }
497 }
498 Ok(false)
499 }
500
501 fn __contains__(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
502 self._contains(needle, vm)
503 }
504}
505
506#[pyclass(
507 itemsize = core::mem::size_of::<crate::PyObjectRef>(),
508 flags(BASETYPE, SEQUENCE, _MATCH_SELF),
509 with(AsMapping, AsNumber, AsSequence, Hashable, Comparable, Iterable, Constructor, Representable)
510)]
511impl Py<PyTuple> {
512 #[pymethod]
513 fn count(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
514 let mut count: usize = 0;
515 for element in self {
516 if vm.identical_or_equal(element, &value)? {
517 count += 1;
518 }
519 }
520 Ok(count)
521 }
522
523 #[pymethod]
524 fn index(
525 &self,
526 value: PyObjectRef,
527 range: OptionalRangeArgs,
528 vm: &VirtualMachine,
529 ) -> PyResult<usize> {
530 let (start, stop) = range.saturate(self.as_slice().len(), vm)?;
531 for (index, element) in self.as_slice().iter().enumerate().take(stop).skip(start) {
532 if vm.identical_or_equal(element, &value)? {
533 return Ok(index);
534 }
535 }
536 Err(vm.new_value_error("tuple.index(x): x not in tuple"))
537 }
538
539 #[pymethod]
540 fn __getnewargs__(zelf: PyRef<PyTuple>, vm: &VirtualMachine) -> (PyTupleRef,) {
541 let tup_arg = if zelf.class().is(vm.ctx.types.tuple_type) {
545 zelf
546 } else {
547 PyTuple::new_ref(zelf.as_slice().to_vec(), &vm.ctx)
548 };
549 (tup_arg,)
550 }
551
552 #[pyclassmethod]
553 fn __class_getitem__(
554 cls: PyTypeRef,
555 object: PyObjectRef,
556 vm: &VirtualMachine,
557 ) -> PyResult<PyGenericAlias> {
558 PyGenericAlias::from_args(cls, object, vm)
559 }
560}
561
562impl AsMapping for PyTuple {
563 fn as_mapping() -> &'static PyMappingMethods {
564 static AS_MAPPING: LazyLock<PyMappingMethods> = LazyLock::new(|| PyMappingMethods {
565 length: atomic_func!(|mapping, _vm| {
566 Ok(PyTuple::mapping_downcast(mapping).as_slice().len())
567 }),
568 subscript: atomic_func!(|mapping, needle, vm| PyTuple::mapping_downcast(mapping)
569 .payload
570 ._getitem(needle, vm)),
571 ..PyMappingMethods::NOT_IMPLEMENTED
572 });
573 &AS_MAPPING
574 }
575}
576
577impl AsSequence for PyTuple {
578 fn as_sequence() -> &'static PySequenceMethods {
579 static AS_SEQUENCE: LazyLock<PySequenceMethods> = LazyLock::new(|| PySequenceMethods {
580 length: atomic_func!(|seq, _vm| Ok(PyTuple::sequence_downcast(seq).as_slice().len())),
581 concat: atomic_func!(|seq, other, vm| {
582 let zelf = PyTuple::sequence_downcast(seq);
583 match PyTuple::__add__(zelf.to_owned(), other.to_owned(), vm) {
584 PyArithmeticValue::Implemented(tuple) => Ok(tuple.into()),
585 PyArithmeticValue::NotImplemented => Err(vm.new_type_error(format!(
586 "can only concatenate tuple (not '{}') to tuple",
587 other.class().name()
588 ))),
589 }
590 }),
591 repeat: atomic_func!(|seq, n, vm| {
592 let zelf = PyTuple::sequence_downcast(seq);
593 PyTuple::repeat(zelf.to_owned(), n, vm).map(|x| x.into())
594 }),
595 item: atomic_func!(|seq, i, vm| {
596 let zelf = PyTuple::sequence_downcast(seq);
597 zelf.as_slice().getitem_by_index(vm, i)
598 }),
599 contains: atomic_func!(|seq, needle, vm| {
600 let zelf = PyTuple::sequence_downcast(seq);
601 zelf.payload._contains(needle, vm)
602 }),
603 ..PySequenceMethods::NOT_IMPLEMENTED
604 });
605 &AS_SEQUENCE
606 }
607}
608
609impl AsNumber for PyTuple {
610 fn as_number() -> &'static PyNumberMethods {
611 static AS_NUMBER: PyNumberMethods = PyNumberMethods {
612 boolean: Some(|number, _vm| {
613 let zelf = number.obj.downcast_ref::<PyTuple>().unwrap();
614 Ok(!zelf.as_slice().is_empty())
615 }),
616 ..PyNumberMethods::NOT_IMPLEMENTED
617 };
618 &AS_NUMBER
619 }
620}
621
622impl Hashable for PyTuple {
623 #[inline]
624 fn hash(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyHash> {
625 tuple_hash(zelf.as_slice(), vm)
626 }
627}
628
629impl Comparable for PyTuple {
630 fn cmp(
631 zelf: &Py<Self>,
632 other: &PyObject,
633 op: PyComparisonOp,
634 vm: &VirtualMachine,
635 ) -> PyResult<PyComparisonValue> {
636 if let Some(res) = op.identical_optimization(zelf, other) {
637 return Ok(res.into());
638 }
639 let other = class_or_notimplemented!(Self, other);
640 zelf.as_slice()
641 .iter()
642 .map(|o| &**o)
643 .richcompare(other.as_slice().iter().map(|o| &**o), op, vm)
644 .map(PyComparisonValue::Implemented)
645 }
646}
647
648impl Iterable for PyTuple {
649 fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
650 Ok(PyTupleIterator {
651 internal: PyMutex::new(PositionIterInternal::new(zelf, 0)),
652 }
653 .into_pyobject(vm))
654 }
655}
656
657impl Representable for PyTuple {
658 #[inline]
659 fn repr(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
660 let s = if zelf.as_slice().is_empty() {
661 vm.ctx.intern_str("()").to_owned()
662 } else if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
663 let s = if zelf.as_slice().len() == 1 {
664 wtf8_concat!("(", zelf.as_slice()[0].repr(vm)?.as_wtf8(), ",)")
665 } else {
666 collection_repr(
667 None,
668 "(",
669 ")",
670 "()",
671 zelf.as_slice().iter().map(|o| &**o),
672 vm,
673 )?
674 };
675 vm.ctx.new_str(s)
676 } else {
677 vm.ctx.intern_str("(...)").to_owned()
678 };
679 Ok(s)
680 }
681
682 #[cold]
683 fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
684 unreachable!("use repr instead")
685 }
686}
687
688impl PyRef<PyTuple<PyObjectRef>> {
689 pub fn try_into_typed<T: PyPayload>(
690 self,
691 vm: &VirtualMachine,
692 ) -> PyResult<PyRef<PyTuple<PyRef<T>>>> {
693 for elem in self.as_slice() {
695 <PyRef<T> as TransmuteFromObject>::check(vm, elem)?;
696 }
697 Ok(unsafe { core::mem::transmute::<Self, PyRef<PyTuple<PyRef<T>>>>(self) })
699 }
700}
701
702impl<T: PyPayload> PyRef<PyTuple<PyRef<T>>> {
703 #[must_use]
704 pub fn into_untyped(self) -> PyRef<PyTuple> {
705 unsafe { core::mem::transmute::<Self, PyRef<PyTuple>>(self) }
707 }
708}
709
710impl<T: PyPayload> Py<PyTuple<PyRef<T>>> {
711 pub fn as_untyped(&self) -> &Py<PyTuple> {
712 unsafe { core::mem::transmute::<&Self, &Py<PyTuple>>(self) }
714 }
715}
716
717impl<T: PyPayload> From<PyRef<PyTuple<PyRef<T>>>> for PyTupleRef {
718 #[inline]
719 fn from(tup: PyRef<PyTuple<PyRef<T>>>) -> Self {
720 tup.into_untyped()
721 }
722}
723
724#[pyclass(module = false, name = "tuple_iterator", traverse)]
725#[derive(Debug)]
726pub(crate) struct PyTupleIterator {
727 internal: PyMutex<PositionIterInternal<PyTupleRef>>,
728}
729
730impl PyPayload for PyTupleIterator {
731 fn class(ctx: &Context) -> &'static Py<PyType> {
732 ctx.types.tuple_iterator_type
733 }
734}
735
736#[pyclass(flags(DISALLOW_INSTANTIATION), with(IterNext, Iterable))]
737impl Py<PyTupleIterator> {
738 #[pymethod]
739 fn __length_hint__(&self) -> usize {
740 self.internal.lock().length_hint(|obj| obj.as_slice().len())
741 }
742
743 #[pymethod]
744 fn __setstate__(&self, object: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
745 self.internal
746 .lock()
747 .set_state(&object, |obj, pos| pos.min(obj.as_slice().len()), vm)
748 }
749
750 #[pymethod]
751 fn __reduce__(&self, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
752 let func = builtins_iter(vm)?;
753 Ok(self.internal.lock().reduce(
754 func,
755 |x| x.clone().into(),
756 |vm| vm.ctx.empty_tuple.clone().into(),
757 vm,
758 ))
759 }
760}
761
762impl PyTupleIterator {
763 pub(crate) fn fast_next(&self) -> Option<PyObjectRef> {
765 locked_next(&self.internal, |tuple, pos| {
766 Ok(PyIterReturn::from_result(
767 tuple.as_slice().get(pos).cloned().ok_or(None),
768 ))
769 })
770 .ok()
771 .and_then(|r| match r {
772 PyIterReturn::Return(v) => Some(v),
773 PyIterReturn::StopIteration(_) => None,
774 })
775 }
776}
777
778impl SelfIter for PyTupleIterator {}
779impl IterNext for PyTupleIterator {
780 fn next(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyIterReturn> {
781 locked_next(&zelf.internal, |tuple, pos| {
782 Ok(PyIterReturn::from_result(
783 tuple.as_slice().get(pos).cloned().ok_or(None),
784 ))
785 })
786 }
787}
788
789fn vectorcall_tuple(
790 zelf_obj: &PyObject,
791 args: Vec<PyObjectRef>,
792 nargs: usize,
793 kwnames: Option<&[PyObjectRef]>,
794 vm: &VirtualMachine,
795) -> PyResult {
796 let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
797 let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
798 (zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm)
801}
802
803pub(crate) fn init(context: &'static Context) {
804 PyTuple::extend_class(context, context.types.tuple_type);
805 PyTupleIterator::extend_class(context, context.types.tuple_iterator_type);
806 context
807 .types
808 .tuple_type
809 .slots
810 .vectorcall
811 .store(Some(vectorcall_tuple));
812}
813
814pub(super) fn tuple_hash(elements: &[PyObjectRef], vm: &VirtualMachine) -> PyResult<PyHash> {
815 hash::hash_tuple(elements.iter().map(|val| element_hash(val, vm)))
816}
817
818#[inline]
825fn element_hash(val: &PyObject, vm: &VirtualMachine) -> PyResult<PyHash> {
826 if val.class().is(vm.ctx.types.int_type)
827 && let Some(i) = val.downcast_ref::<PyInt>()
828 {
829 return Ok(hash::hash_bigint(i.as_bigint()));
830 }
831 val.hash(vm)
832}