55use crossbeam_utils:: atomic:: AtomicCell ;
66
77use super :: pytype:: PyTypeRef ;
8+ use super :: { int, PyInt } ;
89use crate :: slots:: PyIter ;
910use crate :: vm:: VirtualMachine ;
1011use crate :: {
@@ -24,8 +25,9 @@ pub enum IterStatus {
2425#[ pyclass( module = false , name = "iterator" ) ]
2526#[ derive( Debug ) ]
2627pub struct PySequenceIterator {
27- pub position : AtomicCell < isize > ,
28+ pub position : AtomicCell < usize > ,
2829 pub obj : PyObjectRef ,
30+ pub status : AtomicCell < IterStatus > ,
2931}
3032
3133impl PyValue for PySequenceIterator {
@@ -36,26 +38,69 @@ impl PyValue for PySequenceIterator {
3638
3739#[ pyimpl( with( PyIter ) ) ]
3840impl PySequenceIterator {
39- pub fn new_forward ( obj : PyObjectRef ) -> Self {
41+ pub fn new ( obj : PyObjectRef ) -> Self {
4042 Self {
4143 position : AtomicCell :: new ( 0 ) ,
4244 obj,
45+ status : AtomicCell :: new ( IterStatus :: Active ) ,
46+ }
47+ }
48+
49+ #[ pymethod( magic) ]
50+ fn length_hint ( & self , vm : & VirtualMachine ) -> PyObjectRef {
51+ match self . status . load ( ) {
52+ IterStatus :: Active => {
53+ let pos = self . position . load ( ) ;
54+ // return NotImplemented if no length is around.
55+ vm. obj_len ( & self . obj )
56+ . map_or ( vm. ctx . not_implemented ( ) , |len| {
57+ PyInt :: from ( len. saturating_sub ( pos) ) . into_object ( vm)
58+ } )
59+ }
60+ IterStatus :: Exhausted => PyInt :: from ( 0 ) . into_object ( vm) ,
4361 }
4462 }
4563
4664 #[ pymethod( magic) ]
47- fn length_hint ( & self , vm : & VirtualMachine ) -> PyResult < isize > {
48- let pos = self . position . load ( ) ;
49- let len = vm. obj_len ( & self . obj ) ?;
50- Ok ( len as isize - pos)
65+ fn reduce ( & self , vm : & VirtualMachine ) -> PyResult {
66+ let iter = vm. get_attribute ( vm. builtins . clone ( ) , "iter" ) ?;
67+ Ok ( match self . status . load ( ) {
68+ IterStatus :: Exhausted => vm
69+ . ctx
70+ . new_tuple ( vec ! [ iter, vm. ctx. new_tuple( vec![ vm. ctx. new_list( vec![ ] ) ] ) ] ) ,
71+ IterStatus :: Active => vm. ctx . new_tuple ( vec ! [
72+ iter,
73+ vm. ctx. new_tuple( vec![ self . obj. clone( ) ] ) ,
74+ vm. ctx. new_int( self . position. load( ) ) ,
75+ ] ) ,
76+ } )
77+ }
78+
79+ #[ pymethod( magic) ]
80+ fn setstate ( & self , state : PyObjectRef , vm : & VirtualMachine ) -> PyResult < ( ) > {
81+ // When we're exhausted, just return.
82+ if let IterStatus :: Exhausted = self . status . load ( ) {
83+ return Ok ( ( ) ) ;
84+ }
85+ if let Some ( i) = state. payload :: < PyInt > ( ) {
86+ self . position
87+ . store ( int:: try_to_primitive ( i. as_bigint ( ) , vm) . unwrap_or ( 0 ) ) ;
88+ Ok ( ( ) )
89+ } else {
90+ Err ( vm. new_type_error ( "an integer is required." . to_owned ( ) ) )
91+ }
5192 }
5293}
5394
5495impl PyIter for PySequenceIterator {
5596 fn next ( zelf : & PyRef < Self > , vm : & VirtualMachine ) -> PyResult {
97+ if let IterStatus :: Exhausted = zelf. status . load ( ) {
98+ return Err ( vm. new_stop_iteration ( ) ) ;
99+ }
56100 let pos = zelf. position . fetch_add ( 1 ) ;
57101 match zelf. obj . get_item ( pos, vm) {
58102 Err ( ref e) if e. isinstance ( & vm. ctx . exceptions . index_error ) => {
103+ zelf. status . store ( IterStatus :: Exhausted ) ;
59104 Err ( vm. new_stop_iteration ( ) )
60105 }
61106 // also catches stop_iteration => stop_iteration
0 commit comments