@@ -3,6 +3,7 @@ use std::ops::Range;
3
3
use std:: string:: ToString ;
4
4
use std:: { char, ffi, fmt} ;
5
5
6
+ use crossbeam_utils:: atomic:: AtomicCell ;
6
7
use itertools:: Itertools ;
7
8
use num_traits:: ToPrimitive ;
8
9
use unic_ucd_bidi:: BidiClass ;
@@ -12,7 +13,11 @@ use unicode_casing::CharExt;
12
13
13
14
use super :: bytes:: PyBytesRef ;
14
15
use super :: dict:: PyDict ;
15
- use super :: int:: { PyInt , PyIntRef } ;
16
+ use super :: int:: { try_to_primitive, PyInt , PyIntRef } ;
17
+ use super :: iter:: {
18
+ IterStatus ,
19
+ IterStatus :: { Active , Exhausted } ,
20
+ } ;
16
21
use super :: pytype:: PyTypeRef ;
17
22
use crate :: anystr:: { self , adjust_indices, AnyStr , AnyStrContainer , AnyStrWrapper } ;
18
23
use crate :: exceptions:: IntoPyException ;
@@ -113,6 +118,7 @@ impl TryIntoRef<PyStr> for &str {
113
118
pub struct PyStrIterator {
114
119
string : PyStrRef ,
115
120
position : PyAtomic < usize > ,
121
+ status : AtomicCell < IterStatus > ,
116
122
}
117
123
118
124
impl PyValue for PyStrIterator {
@@ -122,20 +128,66 @@ impl PyValue for PyStrIterator {
122
128
}
123
129
124
130
#[ pyimpl( with( PyIter ) ) ]
125
- impl PyStrIterator { }
131
+ impl PyStrIterator {
132
+ #[ pymethod( magic) ]
133
+ fn length_hint ( & self ) -> usize {
134
+ match self . status . load ( ) {
135
+ Active => {
136
+ let pos = self . position . load ( atomic:: Ordering :: SeqCst ) ;
137
+ self . string . len ( ) . saturating_sub ( pos)
138
+ }
139
+ Exhausted => 0 ,
140
+ }
141
+ }
142
+
143
+ #[ pymethod( magic) ]
144
+ fn setstate ( & self , state : PyObjectRef , vm : & VirtualMachine ) -> PyResult < ( ) > {
145
+ // When we're exhausted, just return.
146
+ if let Exhausted = self . status . load ( ) {
147
+ return Ok ( ( ) ) ;
148
+ }
149
+ let pos = state
150
+ . payload :: < PyInt > ( )
151
+ . ok_or_else ( || vm. new_type_error ( "an integer is required." . to_owned ( ) ) ) ?;
152
+ let pos = std:: cmp:: min (
153
+ try_to_primitive ( pos. as_bigint ( ) , vm) . unwrap_or ( 0 ) ,
154
+ self . string . len ( ) ,
155
+ ) ;
156
+ self . position . store ( pos, atomic:: Ordering :: SeqCst ) ;
157
+ Ok ( ( ) )
158
+ }
159
+
160
+ #[ pymethod( magic) ]
161
+ fn reduce ( & self , vm : & VirtualMachine ) -> PyResult {
162
+ let iter = vm. get_attribute ( vm. builtins . clone ( ) , "iter" ) ?;
163
+ Ok ( vm. ctx . new_tuple ( match self . status . load ( ) {
164
+ Exhausted => vec ! [ iter, vm. ctx. new_tuple( vec![ vm. ctx. new_str( "" ) ] ) ] ,
165
+ Active => vec ! [
166
+ iter,
167
+ vm. ctx. new_tuple( vec![ self . string. clone( ) . into_object( ) ] ) ,
168
+ vm. ctx
169
+ . new_int( self . position. load( atomic:: Ordering :: Relaxed ) ) ,
170
+ ] ,
171
+ } ) )
172
+ }
173
+ }
126
174
127
175
impl PyIter for PyStrIterator {
128
176
fn next ( zelf : & PyRef < Self > , vm : & VirtualMachine ) -> PyResult {
177
+ if let Exhausted = zelf. status . load ( ) {
178
+ return Err ( vm. new_stop_iteration ( ) ) ;
179
+ }
129
180
let value = & * zelf. string . value ;
130
181
let mut start = zelf. position . load ( atomic:: Ordering :: SeqCst ) ;
131
182
loop {
132
183
if start == value. len ( ) {
184
+ zelf. status . store ( Exhausted ) ;
133
185
return Err ( vm. new_stop_iteration ( ) ) ;
134
186
}
135
- let ch = value[ start..]
136
- . chars ( )
137
- . next ( )
138
- . ok_or_else ( || vm . new_stop_iteration ( ) ) ?;
187
+ let ch = value[ start..] . chars ( ) . next ( ) . ok_or_else ( || {
188
+ zelf . status . store ( Exhausted ) ;
189
+ vm . new_stop_iteration ( )
190
+ } ) ?;
139
191
140
192
match zelf. position . compare_exchange_weak (
141
193
start,
@@ -1123,6 +1175,7 @@ impl Iterable for PyStr {
1123
1175
Ok ( PyStrIterator {
1124
1176
position : Radium :: new ( 0 ) ,
1125
1177
string : zelf,
1178
+ status : AtomicCell :: new ( Active ) ,
1126
1179
}
1127
1180
. into_object ( vm) )
1128
1181
}
0 commit comments