@@ -1034,62 +1034,118 @@ def __reduce__(self):
10341034 return (type (self ), (self .data , self .selectors ))
10351035
10361036
1037+ class _tee_dataobject :
1038+ LINKCELLS = 32
1039+
1040+ @__graalpython__ .builtin_method
1041+ def __init__ (self , it , values = None , nxt = None ):
1042+ self .it = it
1043+ if values :
1044+ self .values = values
1045+ self .numread = len (values )
1046+ if self .numread == _tee_dataobject .LINKCELLS :
1047+ self .nextlink = nxt
1048+ elif self .numread > _tee_dataobject .LINKCELLS :
1049+ raise ValueError (f"_tee_dataobject should nove have more than { _tee_dataobject .LINKCELLS } links" )
1050+ elif nxt is not None :
1051+ raise ValueError ("_tee_dataobject shouldn't have a next if not full" )
1052+ else :
1053+ self .values = [None ] * _tee_dataobject .LINKCELLS
1054+ self .numread = 0
1055+ self .running = False
1056+ self .nextlink = nxt
1057+
1058+ @__graalpython__ .builtin_method
1059+ def _jumplink (self ):
1060+ if not self .nextlink :
1061+ self .nextlink = _tee_dataobject (self .it )
1062+ return self .nextlink
1063+
1064+ @__graalpython__ .builtin_method
1065+ def __getitem__ (self , i ):
1066+ if i < self .numread :
1067+ return self .values [i ]
1068+ else :
1069+ if self .running :
1070+ raise RuntimeError ("cannot re-enter the tee iterator" )
1071+ self .running = True
1072+ try :
1073+ value = next (self .it )
1074+ finally :
1075+ self .running = False
1076+ self .numread += 1
1077+ self .values [i ] = value
1078+ return value
1079+
1080+ @__graalpython__ .builtin_method
1081+ def __reduce__ (self ):
1082+ return type (self ), (self .it , self .values , self .nextlink )
1083+
1084+
10371085class _tee :
10381086 # This uses a linked list of fixed size lists where
10391087 # the last item in the fixed size list is a link to
10401088 # another fixed size list. Once all _tee instances have
10411089 # traversed given fixed size list, it'll become a garbage
10421090 # to be collected
10431091 @__graalpython__ .builtin_method
1044- def __init__ (self , it , buffer = None ):
1045- self .itemIndex = 0
1046- if buffer is None :
1047- # Support for direct creation of _tee from user code,
1048- # where the ctor takes a single iterable object
1049- self .it = iter (it )
1050- self .buffer = [None ] * 8
1092+ def __new__ (cls , iterable ):
1093+ it = iter (iterable )
1094+ if isinstance (it , _tee ):
1095+ return it .__copy__ ()
10511096 else :
1052- self .it = it
1053- self .buffer = buffer
1097+ to = object .__new__ (_tee )
1098+ to .dataobj = _tee_dataobject (it )
1099+ to .index = 0
1100+ return to
10541101
10551102 @__graalpython__ .builtin_method
10561103 def __iter__ (self ):
10571104 return self
10581105
10591106 @__graalpython__ .builtin_method
10601107 def __next__ (self ):
1061- # jump to the next buffer if necessary
1062- lastIndex = len (self .buffer ) - 1
1063- if self .itemIndex == lastIndex :
1064- if self .buffer [lastIndex ] is None :
1065- self .buffer [lastIndex ] = [None ] * 8
1066- self .buffer = self .buffer [lastIndex ]
1067- self .itemIndex = 0
1068- # take existing item from the buffer or advance the iterator
1069- if self .buffer [self .itemIndex ] is None :
1070- result = next (self .it )
1071- self .buffer [self .itemIndex ] = result
1072- else :
1073- result = self .buffer [self .itemIndex ]
1074- self .itemIndex += 1
1075- return result
1108+ if self .index >= _tee_dataobject .LINKCELLS :
1109+ self .dataobj = self .dataobj ._jumplink ()
1110+ self .index = 0
1111+ value = self .dataobj [self .index ]
1112+ self .index += 1
1113+ return value
1114+
1115+ @__graalpython__ .builtin_method
1116+ def __reduce__ (self ):
1117+ return type (self ), ((),), (self .dataobj , self .index )
1118+
1119+ @__graalpython__ .builtin_method
1120+ def __setstate__ (self , state ):
1121+ if not isinstance (state , tuple ) or len (state ) != 2 :
1122+ raise TypeError ("state is not a 2-tuple" )
1123+ if not isinstance (state [0 ], _tee_dataobject ):
1124+ raise TypeError ("state is not a _tee_dataobject" )
1125+ self .dataobj = state [0 ]
1126+ if state [1 ] < 0 or state [1 ] > _tee_dataobject .LINKCELLS :
1127+ raise ValueError ("Index out of range" )
1128+ self .index = int (state [1 ])
10761129
10771130 @__graalpython__ .builtin_method
10781131 def __copy__ (self ):
1079- return _tee (self .it , self .buffer )
1132+ to = object .__new__ (_tee )
1133+ to .dataobj = self .dataobj
1134+ to .index = self .index
1135+ return to
10801136
10811137
10821138def tee (iterable , n = 2 ):
10831139 if not isinstance (n , int ):
10841140 raise TypeError ()
10851141 if n < 0 :
10861142 raise ValueError ("n must be >=0" )
1143+ if n == 0 :
1144+ return tuple ()
10871145 # if the iterator can be copied, use that instead of _tee
10881146 # note: this works for _tee itself
10891147 it = iter (iterable )
10901148 copy = getattr (it , "__copy__" , None )
1091- if callable (copy ):
1092- return tuple ([it ] + [it .__copy__ () for i in range (1 , n )])
1093- else :
1094- queue = [None ] * 8
1095- return tuple (_tee (it , queue ) for i in range (0 , n ))
1149+ if not callable (copy ):
1150+ it = _tee (it )
1151+ return tuple ([it ] + [it .__copy__ () for i in range (1 , n )])
0 commit comments