@@ -6,9 +6,8 @@ use super::objsequence::get_item;
66use super :: objtype:: PyClassRef ;
77use crate :: function:: OptionalArg ;
88use crate :: pyobject:: {
9- self , BorrowValue , IdProtocol , IntoPyObject ,
10- PyArithmaticValue :: { self , * } ,
11- PyClassImpl , PyComparisonValue , PyContext , PyObjectRef , PyRef , PyResult , PyValue ,
9+ self , BorrowValue , Either , IdProtocol , IntoPyObject , PyArithmaticValue , PyClassImpl ,
10+ PyComparisonValue , PyContext , PyObjectRef , PyRef , PyResult , PyValue , TypeProtocol ,
1211} ;
1312use crate :: sequence:: { self , SimpleSeq } ;
1413use crate :: slots:: { Comparable , Hashable , PyComparisonOp } ;
@@ -21,7 +20,7 @@ use rustpython_common::hash::PyHash;
2120/// If the argument is a tuple, the return value is the same object.
2221#[ pyclass( module = false , name = "tuple" ) ]
2322pub struct PyTuple {
24- elements : Vec < PyObjectRef > ,
23+ elements : Box < [ PyObjectRef ] > ,
2524}
2625
2726impl fmt:: Debug for PyTuple {
@@ -31,12 +30,6 @@ impl fmt::Debug for PyTuple {
3130 }
3231}
3332
34- impl From < Vec < PyObjectRef > > for PyTuple {
35- fn from ( elements : Vec < PyObjectRef > ) -> Self {
36- PyTuple { elements }
37- }
38- }
39-
4033impl < ' a > BorrowValue < ' a > for PyTuple {
4134 type Borrowed = & ' a [ PyObjectRef ] ;
4235
@@ -77,25 +70,53 @@ impl PyTuple {
7770
7871pub type PyTupleRef = PyRef < PyTuple > ;
7972
73+ impl PyTupleRef {
74+ pub ( crate ) fn with_elements ( elements : Vec < PyObjectRef > , ctx : & PyContext ) -> Self {
75+ if elements. is_empty ( ) {
76+ ctx. empty_tuple . clone ( )
77+ } else {
78+ let elements = elements. into_boxed_slice ( ) ;
79+ Self :: new_ref ( PyTuple { elements } , ctx. types . tuple_type . clone ( ) , None )
80+ }
81+ }
82+ }
83+
8084pub ( crate ) fn get_value ( obj : & PyObjectRef ) -> & [ PyObjectRef ] {
8185 obj. payload :: < PyTuple > ( ) . unwrap ( ) . borrow_value ( )
8286}
8387
8488#[ pyimpl( flags( BASETYPE ) , with( Hashable , Comparable ) ) ]
8589impl PyTuple {
90+ /// Creating a new tuple with given boxed slice.
91+ /// NOTE: for usual case, you probably want to use PyTupleRef::with_elements.
92+ /// Calling this function implies trying micro optimization for non-zero-sized tuple.
93+ pub ( crate ) fn _new ( elements : Box < [ PyObjectRef ] > ) -> Self {
94+ Self { elements }
95+ }
96+
8697 #[ pymethod( name = "__add__" ) ]
87- fn add ( & self , other : PyObjectRef , vm : & VirtualMachine ) -> PyArithmaticValue < PyTuple > {
88- if let Some ( other) = other. payload_if_subclass :: < PyTuple > ( vm) {
89- let elements: Vec < _ > = self
90- . elements
91- . boxed_iter ( )
92- . chain ( other. borrow_value ( ) . boxed_iter ( ) )
93- . cloned ( )
94- . collect ( ) ;
95- Implemented ( elements. into ( ) )
96- } else {
97- NotImplemented
98- }
98+ fn add (
99+ zelf : PyRef < Self > ,
100+ other : PyObjectRef ,
101+ vm : & VirtualMachine ,
102+ ) -> PyArithmaticValue < PyRef < Self > > {
103+ let added = other. downcast :: < Self > ( ) . map ( |other| {
104+ if other. elements . is_empty ( ) && zelf. class ( ) . is ( & vm. ctx . types . tuple_type ) {
105+ zelf
106+ } else if zelf. elements . is_empty ( ) && other. class ( ) . is ( & vm. ctx . types . tuple_type ) {
107+ other
108+ } else {
109+ let elements = zelf
110+ . elements
111+ . boxed_iter ( )
112+ . chain ( other. borrow_value ( ) . boxed_iter ( ) )
113+ . cloned ( )
114+ . collect :: < Vec < _ > > ( )
115+ . into_boxed_slice ( ) ;
116+ Self { elements } . into_ref ( vm)
117+ }
118+ } ) ;
119+ PyArithmaticValue :: from_option ( added. ok ( ) )
99120 }
100121
101122 #[ pymethod( name = "__bool__" ) ]
@@ -149,16 +170,26 @@ impl PyTuple {
149170
150171 #[ pymethod( name = "__mul__" ) ]
151172 #[ pymethod( name = "__rmul__" ) ]
152- fn mul ( & self , counter : isize ) -> PyTuple {
153- let new_elements: Vec < _ > = sequence:: seq_mul ( & self . elements , counter)
154- . cloned ( )
155- . collect ( ) ;
156- new_elements. into ( )
173+ fn mul ( & self , counter : isize , vm : & VirtualMachine ) -> PyRef < Self > {
174+ if self . elements . is_empty ( ) || counter == 0 {
175+ vm. ctx . empty_tuple . clone ( )
176+ } else {
177+ let elements = sequence:: seq_mul ( & self . elements , counter)
178+ . cloned ( )
179+ . collect :: < Vec < _ > > ( )
180+ . into_boxed_slice ( ) ;
181+ Self { elements } . into_ref ( vm)
182+ }
157183 }
158184
159185 #[ pymethod( name = "__getitem__" ) ]
160186 fn getitem ( zelf : PyRef < Self > , needle : PyObjectRef , vm : & VirtualMachine ) -> PyResult {
161- get_item ( vm, zelf. as_object ( ) , & zelf. elements , needle)
187+ Ok (
188+ match get_item ( vm, zelf. as_object ( ) , & zelf. elements , needle) ? {
189+ Either :: A ( obj) => obj,
190+ Either :: B ( vec) => vm. ctx . new_tuple ( vec) ,
191+ } ,
192+ )
162193 }
163194
164195 #[ pymethod( name = "index" ) ]
@@ -186,10 +217,10 @@ impl PyTuple {
186217 cls : PyClassRef ,
187218 iterable : OptionalArg < PyObjectRef > ,
188219 vm : & VirtualMachine ,
189- ) -> PyResult < PyTupleRef > {
220+ ) -> PyResult < PyRef < Self > > {
190221 let elements = if let OptionalArg :: Present ( iterable) = iterable {
191222 let iterable = if cls. is ( & vm. ctx . types . tuple_type ) {
192- match iterable. downcast_exact :: < PyTuple > ( vm) {
223+ match iterable. downcast_exact :: < Self > ( vm) {
193224 Ok ( tuple) => return Ok ( tuple) ,
194225 Err ( iterable) => iterable,
195226 }
@@ -199,9 +230,10 @@ impl PyTuple {
199230 vm. extract_elements ( & iterable) ?
200231 } else {
201232 vec ! [ ]
202- } ;
233+ }
234+ . into_boxed_slice ( ) ;
203235
204- PyTuple :: from ( elements) . into_ref_with_type ( vm, cls)
236+ Self { elements } . into_ref_with_type ( vm, cls)
205237 }
206238}
207239
0 commit comments