@@ -1034,62 +1034,118 @@ def __reduce__(self):
1034
1034
return (type (self ), (self .data , self .selectors ))
1035
1035
1036
1036
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
+
1037
1085
class _tee :
1038
1086
# This uses a linked list of fixed size lists where
1039
1087
# the last item in the fixed size list is a link to
1040
1088
# another fixed size list. Once all _tee instances have
1041
1089
# traversed given fixed size list, it'll become a garbage
1042
1090
# to be collected
1043
1091
@__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__ ()
1051
1096
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
1054
1101
1055
1102
@__graalpython__ .builtin_method
1056
1103
def __iter__ (self ):
1057
1104
return self
1058
1105
1059
1106
@__graalpython__ .builtin_method
1060
1107
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 ])
1076
1129
1077
1130
@__graalpython__ .builtin_method
1078
1131
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
1080
1136
1081
1137
1082
1138
def tee (iterable , n = 2 ):
1083
1139
if not isinstance (n , int ):
1084
1140
raise TypeError ()
1085
1141
if n < 0 :
1086
1142
raise ValueError ("n must be >=0" )
1143
+ if n == 0 :
1144
+ return tuple ()
1087
1145
# if the iterator can be copied, use that instead of _tee
1088
1146
# note: this works for _tee itself
1089
1147
it = iter (iterable )
1090
1148
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